From 592434abc6ff6d7c0b0fd4b2affece399f66d7f5 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 10 Feb 2026 09:48:40 -0800 Subject: [PATCH 01/25] feat: changeset --- .changeset/fair-cats-obey.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fair-cats-obey.md diff --git a/.changeset/fair-cats-obey.md b/.changeset/fair-cats-obey.md new file mode 100644 index 000000000..b1b773634 --- /dev/null +++ b/.changeset/fair-cats-obey.md @@ -0,0 +1,6 @@ +--- +'@relayprotocol/relay-kit-hooks': patch +'@relayprotocol/relay-kit-ui': patch +--- + +Refactor deposit address transaction tracking to use requests API From d7cda02fe7972751f2322c3694810c924e5a752f Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Fri, 20 Feb 2026 14:44:40 -0500 Subject: [PATCH 02/25] wip: pandacss -> tailwind --- packages/ui/package.json | 22 +- packages/ui/panda.config.ts | 389 ----- packages/ui/postcss.config.mjs | 6 + packages/ui/scripts/scope-css.mjs | 50 + .../ui/src/components/common/AmountInput.tsx | 27 +- .../src/components/common/BalanceDisplay.tsx | 4 +- .../src/components/common/CopyToClipBoard.tsx | 29 +- .../components/common/CustomAddressModal.tsx | 74 +- .../ui/src/components/common/ErrorWell.tsx | 43 +- .../src/components/common/LoadingSpinner.tsx | 28 +- packages/ui/src/components/common/Modal.tsx | 94 +- .../components/common/MultiWalletDropdown.tsx | 89 +- .../components/common/PercentageButtons.tsx | 50 +- .../common/SlippageToleranceConfig.tsx | 178 +-- .../ui/src/components/common/StepIcon.tsx | 8 +- .../common/TokenSelector/ChainFilter.tsx | 153 +- .../common/TokenSelector/ChainFilterRow.tsx | 76 +- .../TokenSelector/ChainFilterSidebar.tsx | 144 +- .../common/TokenSelector/ChainShortcuts.tsx | 41 +- .../TokenSelector/CompactChainFilter.tsx | 101 +- .../TokenSelector/MobileChainSelector.tsx | 150 +- .../common/TokenSelector/PaymentMethod.tsx | 135 +- .../common/TokenSelector/PaymentTokenList.tsx | 104 +- .../common/TokenSelector/SuggestedTokens.tsx | 35 +- .../common/TokenSelector/TagPill.tsx | 10 +- .../common/TokenSelector/TokenList.tsx | 93 +- .../common/TokenSelector/TokenSelector.tsx | 109 +- .../triggers/PaymentMethodTrigger.tsx | 57 +- .../TokenSelector/triggers/TokenTrigger.tsx | 44 +- .../TransactionModal/DepositAddressModal.tsx | 14 +- .../TransactionModal/TransactionModal.tsx | 17 +- .../steps/ApprovalPlusSwapStep.tsx | 91 +- .../steps/DepositAddressValidatingStep.tsx | 14 +- .../TransactionModal/steps/ErrorStep.tsx | 76 +- .../steps/SwapConfirmationStep.tsx | 175 +- .../steps/SwapSuccessStep.tsx | 160 +- .../steps/TransactionsByChain.tsx | 17 +- .../steps/WaitingForDepositStep.tsx | 122 +- .../common/UnverifiedTokenModal.tsx | 62 +- .../components/primitives/AccessibleList.tsx | 42 +- .../ui/src/components/primitives/Anchor.tsx | 86 +- packages/ui/src/components/primitives/Box.tsx | 16 +- .../ui/src/components/primitives/Button.tsx | 220 +-- .../src/components/primitives/ChainIcon.tsx | 21 +- .../components/primitives/ChainTokenIcon.tsx | 49 +- .../src/components/primitives/Collapsible.tsx | 60 +- .../ui/src/components/primitives/Dialog.tsx | 161 +- .../ui/src/components/primitives/Dropdown.tsx | 65 +- .../ui/src/components/primitives/Flex.tsx | 91 +- .../ui/src/components/primitives/Input.tsx | 120 +- .../ui/src/components/primitives/Pill.tsx | 91 +- .../ui/src/components/primitives/Skeleton.tsx | 22 +- .../components/primitives/SlippageButton.tsx | 14 +- .../ui/src/components/primitives/Switch.tsx | 74 +- .../ui/src/components/primitives/Tabs.tsx | 74 +- .../ui/src/components/primitives/Text.tsx | 151 +- .../ui/src/components/primitives/Tooltip.tsx | 59 +- .../src/components/widgets/FeeBreakdown.tsx | 167 +- .../widgets/FetchingQuoteLoader.tsx | 20 +- .../OnrampWidget/modals/OnrampModal.tsx | 9 +- .../modals/steps/OnrampConfirmingStep.tsx | 36 +- .../modals/steps/OnrampMoonPayStep.tsx | 64 +- .../steps/OnrampProcessingPassthroughStep.tsx | 59 +- .../modals/steps/OnrampProcessingStepUI.tsx | 126 +- .../modals/steps/OnrampSuccessStep.tsx | 62 +- .../OnrampWidget/widget/FiatCurrencyModal.tsx | 136 +- .../widgets/OnrampWidget/widget/index.tsx | 162 +- .../components/widgets/PriceImpactTooltip.tsx | 30 +- .../ui/src/components/widgets/SwapButton.tsx | 25 +- .../widgets/SwapWidget/GasTopUpSection.tsx | 12 +- .../widgets/SwapWidget/PriceImpact.tsx | 8 +- .../components/widgets/SwapWidget/index.tsx | 204 +-- .../widgets/TokenSelectorContainer.tsx | 22 +- .../widgets/TokenWidget/AmountModeToggle.tsx | 16 +- .../TokenWidget/AmountSectionHeader.tsx | 2 +- .../widgets/TokenWidget/BuyTabContent.tsx | 68 +- .../TokenWidget/DestinationWalletSelector.tsx | 15 +- .../widgets/TokenWidget/FeeBreakdownInfo.tsx | 72 +- .../TokenWidget/FeeBreakdownTooltip.tsx | 22 +- .../widgets/TokenWidget/SectionContainer.tsx | 33 +- .../widgets/TokenWidget/SellTabContent.tsx | 75 +- .../TokenWidget/TransactionDetailsFooter.tsx | 32 +- .../widgets/TokenWidget/widget/index.tsx | 78 +- .../components/widgets/WidgetErrorWell.tsx | 143 +- packages/ui/src/styles/base.css | 852 ++++++++++ packages/ui/src/utils/cn.ts | 6 + packages/ui/tailwind.config.ts | 112 ++ pnpm-lock.yaml | 1407 ++++------------- 88 files changed, 2999 insertions(+), 5583 deletions(-) delete mode 100644 packages/ui/panda.config.ts create mode 100644 packages/ui/postcss.config.mjs create mode 100644 packages/ui/scripts/scope-css.mjs create mode 100644 packages/ui/src/styles/base.css create mode 100644 packages/ui/src/utils/cn.ts create mode 100644 packages/ui/tailwind.config.ts diff --git a/packages/ui/package.json b/packages/ui/package.json index df7e686e2..63636e52b 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -29,29 +29,28 @@ "import": "./_esm/src/index.js", "require": "./_cjs/src/index.js" }, - "./styles.css": "./dist/styles.css", - "./panda.buildinfo.json": "./dist/panda.buildinfo.json" + "./styles.css": "./dist/styles.css" }, "scripts": { "prebuild": "node scripts/version.mjs", - "build": "pnpm run prebuild && pnpm run lint && pnpm prepare && pnpm run clean && pnpm run build:cjs && pnpm run build:esm && pnpm run build:types && pnpm run build:panda", + "build": "pnpm run prebuild && pnpm run lint && pnpm run clean && pnpm run build:css && pnpm run build:cjs && pnpm run build:esm && pnpm run build:types && pnpm run copy:css", "build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./_cjs --removeComments --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./_cjs/package.json", "build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./_esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./_esm/package.json", "build:types": "tsc --project ./tsconfig.build.json --module esnext --outDir ./_types --declarationDir ./_types --emitDeclarationOnly --declaration --declarationMap", - "build:panda": "mkdir -p dist && panda cssgen --outfile dist/styles.css && cp dist/styles.css _cjs/src && cp dist/styles.css _esm/src && panda ship --outfile dist/panda.buildinfo.json", - "dev": "concurrently \"pnpm run clean\" \"pnpm run dev:esm\" \"pnpm run dev:types\"", + "build:css": "mkdir -p dist && tailwindcss -i src/styles/base.css -o dist/styles.css --config tailwind.config.ts --minify && node scripts/scope-css.mjs", + "copy:css": "cp dist/styles.css _cjs/src && cp dist/styles.css _esm/src", + "dev": "concurrently \"pnpm run clean\" \"pnpm run dev:esm\" \"pnpm run dev:types\" \"pnpm run dev:css\"", "dev:esm": "tsc --watch --project ./tsconfig.build.json --module es2020 --outDir ./_esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./_esm/package.json", "dev:types": "tsc --watch --project ./tsconfig.build.json --module esnext --outDir ./_types --declarationDir ./_types --emitDeclarationOnly --declaration --declarationMap", + "dev:css": "tailwindcss -i src/styles/base.css -o dist/styles.css --config tailwind.config.ts --watch", "clean": "rimraf ./_esm ./_cjs ./_types", "typecheck": "tsc --noEmit", - "prepare": "panda codegen", "lint": "pnpm eslint" }, "devDependencies": { - "@csstools/postcss-cascade-layers": "^4.0.6", "@eslint/js": "^9.5.0", - "@pandacss/dev": "^0.40.0", "@types/eslint__js": "^8.42.3", + "autoprefixer": "^10.4.0", "eslint": "~8.57.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-react": "^7.34.2", @@ -60,6 +59,7 @@ "node-fetch": "^3.3.2", "postcss": "^8", "rimraf": "^5.0.5", + "tailwindcss": "^3.4.0", "typescript": "5.4.5", "typescript-eslint": "^7.13.1", "@types/react": "^19.0.0", @@ -92,15 +92,15 @@ "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-visually-hidden": "^1.1.2", - "@relayprotocol/relay-design-system": "workspace:^", "@relayprotocol/relay-kit-hooks": "workspace:*", "@relayprotocol/relay-sdk": "workspace:*", "axios": "^1.7.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", "dayjs": "^1.11.11", - "framer-motion": "^11.2.10", "fuse.js": "^7.0.0", - "pandacss-preset-radix-colors": "^0.2.0", "qrcode.react": "^4.1.0", + "tailwind-merge": "^2.2.0", "usehooks-ts": "^3.1.0" }, "keywords": [ diff --git a/packages/ui/panda.config.ts b/packages/ui/panda.config.ts deleted file mode 100644 index f742faf46..000000000 --- a/packages/ui/panda.config.ts +++ /dev/null @@ -1,389 +0,0 @@ -import { defineConfig } from '@pandacss/dev' -import radixColorsPreset from 'pandacss-preset-radix-colors' -import postcss from 'postcss' -import postcssCascadeLayers from '@csstools/postcss-cascade-layers' - -export const Colors = { - // Gray - gray1: { value: '{colors.slate.1}' }, - gray2: { value: '{colors.slate.2}' }, - gray3: { value: '{colors.slate.3}' }, - gray4: { value: '{colors.slate.4}' }, - gray5: { value: '{colors.slate.5}' }, - gray6: { value: '{colors.slate.6}' }, - gray7: { value: '{colors.slate.7}' }, - gray8: { value: '{colors.slate.8}' }, - gray9: { value: '{colors.slate.9}' }, - gray10: { value: '{colors.slate.10}' }, - gray11: { value: '{colors.slate.11}' }, - gray12: { value: '{colors.slate.12}' }, - // BlackA - blackA10: { value: '{colors.black.a.10}' }, - // Blue - blue12: { value: '{colors.blue.12}' }, - // Red - red2: { value: '{colors.tomato.2}' }, - red3: { value: '{colors.tomato.3}' }, - red4: { value: '{colors.tomato.4}' }, - red5: { value: '{colors.tomato.5}' }, - red6: { value: '{colors.tomato.6}' }, - red9: { value: '{colors.tomato.9}' }, - red10: { value: '{colors.tomato.10}' }, - red11: { value: '{colors.tomato.11}' }, - red12: { value: '{colors.tomato.12}' }, - // Green - green2: { value: '{colors.grass.2}' }, - green3: { value: '{colors.grass.3}' }, - green9: { value: '{colors.grass.9}' }, - green10: { value: '{colors.grass.10}' }, - green11: { value: '{colors.grass.11}' }, - green12: { value: '{colors.grass.12}' }, - // Yellow - yellow9: { value: '{colors.yellow.9}' }, - yellow10: { value: '{colors.yellow.10}' }, - yellow11: { value: '{colors.yellow.11}' }, - yellow12: { value: '{colors.yellow.12}' }, - // Amber - amber2: { value: '{colors.amber.2}' }, - amber3: { value: '{colors.amber.3}' }, - amber4: { value: '{colors.amber.4}' }, - amber9: { value: '{colors.amber.9}' }, - amber10: { value: '{colors.amber.10}' }, - amber11: { value: '{colors.amber.11}' }, - amber12: { value: '{colors.amber.12}' } -} - -export default defineConfig({ - jsxFramework: 'react', - // Whether to use css reset - preflight: { - scope: '.relay-kit-reset' - }, - - // Where to look for your css declarations - include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}'], - - // Files to exclude - exclude: [], - - hooks: { - 'cssgen:done': ({ artifact, content }) => { - if (artifact === 'styles.css') { - return postcss([postcssCascadeLayers()]).process(content).css - } - } - }, - - presets: [ - radixColorsPreset({ - darkMode: { - condition: '.dark &' - }, - colorScales: [ - 'amber', - 'grass', - 'slate', - 'gray', - 'tomato', - 'black', - 'yellow', - 'blue' - ] - }), - // Re-add the panda preset if you want to keep - // the default keyframes, breakpoints, tokens - // and textStyles provided by PandaCSS - '@pandacss/preset-panda' - ], - - conditions: { - extend: { - dark: '.dark &, [data-theme="dark"] &', - light: '.light &', - typeNumber: '&[type=number]', - placeholder_parent: '&[placeholder]', - spinButtons: '&::-webkit-outer-spin-button, &::-webkit-inner-spin-button', - data_state_active: '&[data-state="active"]', - data_state_open: '&[data-state="open"]', - data_state_open_child: '[data-state=open] &', - data_state_closed: '&[data-state="closed"]', - data_swipe_move: '&[data-swipe="move"]', - data_swipe_cancel: '&[data-swipe="cancel"]', - data_swipe_end: '&[data-swipe="end"]', - data_state_checked: '&[data-state="checked"]' - } - }, - prefix: 'relay', - - // Useful for theme customization - theme: { - tokens: { - spacing: { - 1: { value: '4px' }, - 2: { value: '8px' }, - 3: { value: '12px' }, - 4: { value: '16px' }, - 5: { value: '32px' }, - 6: { value: '64px' }, - 'widget-card-section-gutter': { value: '6px' } - }, - fonts: { - body: { value: 'var(--font-inter), sans-serif' }, - heading: { value: 'var(--font-inter), sans-serif' } - }, - colors: Colors, - gradients: { - success: { value: 'linear-gradient(to right, #30A46C, #0ADF79)' } - } - }, - - semanticTokens: { - colors: { - primary1: { - value: { base: '{colors.violet.1}', _dark: '{colors.violetDark.1}' } - }, - primary2: { - value: { base: '{colors.violet.2}', _dark: '{colors.violetDark.2}' } - }, - primary3: { - value: { base: '{colors.violet.3}', _dark: '{colors.violetDark.3}' } - }, - primary4: { - value: { base: '{colors.violet.4}', _dark: '{colors.violetDark.4}' } - }, - primary5: { - value: { base: '{colors.violet.5}', _dark: '{colors.violetDark.5}' } - }, - primary6: { - value: { base: '{colors.violet.6}', _dark: '{colors.violetDark.6}' } - }, - primary7: { - value: { base: '{colors.violet.7}', _dark: '{colors.violetDark.7}' } - }, - primary8: { - value: { base: '{colors.violet.8}', _dark: '{colors.violetDark.8}' } - }, - primary9: { - value: { base: '{colors.violet.9}', _dark: '{colors.violetDark.9}' } - }, - primary10: { - value: { - base: '{colors.violet.10}', - _dark: '{colors.violetDark.10}' - } - }, - primary11: { - value: { - base: '{colors.violet.11}', - _dark: '{colors.violetDark.11}' - } - }, - primary12: { - value: { - base: '{colors.violet.12}', - _dark: '{colors.violetDark.12}' - } - }, - 'primary-color': { value: { base: '{colors.primary9}' } }, - 'focus-color': { value: { base: '{colors.primary7}' } }, - 'subtle-background-color': { value: { base: '{colors.gray.1}' } }, - 'subtle-border-color': { value: { base: '{colors.gray.5}' } }, - - // Text - 'text-default': { value: { base: '{colors.gray.12}' } }, - 'text-subtle': { value: { base: '{colors.gray.11}' } }, - 'text-subtle-secondary': { value: { base: '{colors.gray.11}' } }, - 'text-error': { value: { base: '{colors.tomato.12}' } }, - 'text-success': { value: { base: '{colors.grass.11}' } }, - - // Primary Button - 'primary-button-background': { value: { base: '{colors.primary9}' } }, - 'primary-button-color': { value: { base: 'white' } }, - 'primary-button-hover-background': { - value: { base: '{colors.primary10}' } - }, - 'primary-button-hover-color': { - value: { base: 'white' } - }, - - // Secondary Button - 'secondary-button-background': { value: { base: '{colors.primary3}' } }, - 'secondary-button-color': { value: { base: '{colors.gray12}' } }, - 'secondary-button-hover-background': { - value: { base: '{colors.primary4}' } - }, - 'secondary-button-hover-color': { - value: { base: '{colors.gray12}' } - }, - - // Disabled Button - 'button-disabled-background': { - value: { base: '{colors.gray.8}' } - }, - 'button-disabled-color': { - value: { base: '{colors.gray.11}' } - }, - - // Input - 'input-background': { - value: { base: '{colors.gray.3}' } - }, - 'input-color': { value: { base: '{colors.gray.12}' } }, - - // Anchor - 'anchor-color': { value: { base: '{colors.primary11}' } }, - 'anchor-hover-color': { value: { base: '{colors.primary9}' } }, - - // Dropdown - 'dropdown-background': { value: { base: '{colors.gray.3}' } }, - - // Widget - 'widget-background': { - value: { base: 'white', _dark: '{colors.gray.1}' } - }, - 'widget-card-background': { value: { base: '{colors.gray.1}' } }, - 'widget-selector-background': { value: { base: '{colors.gray.2}' } }, - 'widget-selector-hover-background': { - value: { base: '{colors.gray.3}' } - }, - 'widget-swap-currency-button-border-color': { - value: { base: '{colors.primary4}' } - }, - - // Modal - 'modal-background': { - value: { base: 'white', _dark: '{colors.gray.1}' } - }, - - // Skeleton - 'skeleton-background': { value: { base: '{colors.gray.3}' } } - }, - radii: { - 'widget-border-radius': { value: { base: '16px' } }, - 'widget-card-border-radius': { value: '12px' }, - 'modal-border-radius': { value: { base: '16px' } }, - 'input-border-radius': { value: { base: '8px' } }, - 'dropdown-border-radius': { value: { base: '8px' } }, - 'widget-swap-currency-button-border-radius': { value: { base: '8px' } } - }, - borders: { - 'widget-border': { value: { base: '0px solid white' } }, - 'modal-border': { value: { base: '0px solid white' } }, - 'dropdown-border': { value: { base: '1px solid {colors.gray5}' } }, - 'widget-swap-currency-button-border-width': { - value: { base: '2px' } - }, - 'widget-card-border': { - value: { base: '1px solid {colors.primary4}' } - } - }, - shadows: { - 'widget-box-shadow': { - value: { base: '0px 4px 30px rgba(0, 0, 0, 0.10)' } - } - } - }, - extend: { - tokens: { - colors: { - violet: { - 1: { value: '#FDFDFF' }, - 2: { value: '#F7F8FF' }, - 3: { value: '#EFF1FF' }, - 4: { value: '#E4E7FF' }, - 5: { value: '#D7DBFF' }, - 6: { value: '#C8CCFF' }, - 7: { value: '#B4B8FF' }, - 8: { value: '#989AFF' }, - 9: { value: '#4615C8' }, - 10: { value: '#3B00B4' }, - 11: { value: '#5A45DF' }, - 12: { value: '#2A226E' } - }, - violetDark: { - 1: { value: '#0E0E23' }, - 2: { value: '#141331' }, - 3: { value: '#216' }, - 4: { value: '#2F0093' }, - 5: { value: '#3800A8' }, - 6: { value: '#4016B8' }, - 7: { value: '#4C24D1' }, - 8: { value: '#5B2AF9' }, - 9: { value: '#4615C8' }, - 10: { value: '#3901AA' }, - 11: { value: '#A7AAFF' }, - 12: { value: '#DBDEFF' } - } - } - }, - breakpoints: { - sm: '600px', - md: '900px', - lg: '1200px', - xl: '1400px', - bp300: '300px', - bp400: '400px', - bp500: '500px', - bp600: '600px', - bp700: '700px', - bp800: '800px', - bp900: '900px', - bp1000: '1000px', - bp1100: '1100px', - bp1200: '1200px', - bp1300: '1300px', - bp1400: '1400px', - bp1500: '1500px' - }, - semanticTokens: { - colors: { - prose: { - body: { - value: '{colors.slate.12}' - } - } - } - } - }, - keyframes: { - pulse: { - '50%': { - opacity: '0.5' - } - }, - 'pulse-shadow': { - '0%': { boxShadow: '0 0 0 0px {colors.primary4}' }, - '100%': { boxShadow: '0 0 0 6px {colors.primary4}' } - }, - spin: { - '100%': { - transform: 'rotate(360deg)' - } - }, - collapsibleSlideDown: { - from: { height: 0 }, - to: { height: 'var(--radix-collapsible-content-height)' } - }, - collapsibleSlideUp: { - from: { height: 'var(--radix-collapsible-content-height)' }, - to: { height: 0 } - } - } - }, - layers: { - base: 'panda_base', - recipes: 'panda_recipes', - reset: 'panda_reset', - tokens: 'panda_tokens', - utilities: 'panda_utilities' - }, - - // The output directory for your css system - outdir: '../design-system', - importMap: { - css: '@relayprotocol/relay-design-system/css', - recipes: '@relayprotocol/relay-design-system/recipes', - patterns: '@relayprotocol/relay-design-system/patterns', - jsx: '@relayprotocol/relay-design-system/jsx' - } -}) diff --git a/packages/ui/postcss.config.mjs b/packages/ui/postcss.config.mjs new file mode 100644 index 000000000..2b75bd8a7 --- /dev/null +++ b/packages/ui/postcss.config.mjs @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +} diff --git a/packages/ui/scripts/scope-css.mjs b/packages/ui/scripts/scope-css.mjs new file mode 100644 index 000000000..bf921919f --- /dev/null +++ b/packages/ui/scripts/scope-css.mjs @@ -0,0 +1,50 @@ +/** + * Post-processes dist/styles.css to scope Tailwind's global + * `*, ::before, ::after` and `::backdrop` CSS variable initialization + * blocks to `.relay-kit-reset`. This prevents the component library + * from polluting the consuming app's global CSS namespace. + */ +import { readFileSync, writeFileSync } from 'fs' +import { resolve, dirname } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const cssPath = resolve(__dirname, '../dist/styles.css') + +let css = readFileSync(cssPath, 'utf-8') + +// Pattern: `*, :after, :before { --tw-*: ...; --tw-*: ...; }` +// This block only contains --tw-* CSS custom property declarations. +// Scope it to .relay-kit-reset so it doesn't apply globally. +css = css.replace( + /(\*\s*,\s*(?::after|::after)\s*,\s*(?::before|::before))\s*\{([^}]*)\}/g, + (match, selector, body) => { + // Only scope if the block contains exclusively --tw-* declarations + const declarations = body.split(';').map((d) => d.trim()).filter(Boolean) + const allTwVars = declarations.every((d) => d.startsWith('--tw-')) + if (allTwVars) { + const scoped = selector + .split(',') + .map((s) => `.relay-kit-reset ${s.trim()}`) + .join(',') + return `${scoped}{${body}}` + } + return match + } +) + +// Pattern: `::backdrop { --tw-*: ...; }` +css = css.replace( + /(? { + const declarations = body.split(';').map((d) => d.trim()).filter(Boolean) + const allTwVars = declarations.every((d) => d.startsWith('--tw-')) + if (allTwVars) { + return `.relay-kit-reset ${selector}{${body}}` + } + return match + } +) + +writeFileSync(cssPath, css) +console.log('Scoped Tailwind base variables to .relay-kit-reset') diff --git a/packages/ui/src/components/common/AmountInput.tsx b/packages/ui/src/components/common/AmountInput.tsx index 3413a0472..3be055c98 100644 --- a/packages/ui/src/components/common/AmountInput.tsx +++ b/packages/ui/src/components/common/AmountInput.tsx @@ -1,5 +1,6 @@ import type { ComponentPropsWithoutRef, FC } from 'react' import { Input } from '../primitives/index.js' +import { cn } from '../../utils/cn.js' type Props = { value: string @@ -23,24 +24,14 @@ const AmountInput: FC = ({ pattern="^[0-9]+(\.[0-9]*)?$" ellipsify size="large" - className="ph-no-capture" - css={{ - width: '100%', - background: 'none', - backgroundColor: 'transparent', - fontWeight: '600', - fontSize: 32, - px: '0 !important', - py: '1', - _focus: { - boxShadow: 'none', - outline: 'none' - }, - _placeholder: { - color: 'gray12' - }, - ...inputProps.css - }} + className={cn( + 'ph-no-capture', + 'relay-w-full relay-bg-none relay-bg-transparent relay-font-semibold relay-text-[32px]', + '!relay-px-0 relay-py-1', + 'focus:relay-shadow-none focus:relay-outline-none', + 'placeholder:relay-text-[color:var(--relay-colors-gray12)]', + inputProps.className + )} placeholder={inputProps.placeholder ?? '0'} value={prefixSymbol ? `${prefixSymbol}${value}` : value} onChange={ diff --git a/packages/ui/src/components/common/BalanceDisplay.tsx b/packages/ui/src/components/common/BalanceDisplay.tsx index 61ce2d2dc..e723abf70 100644 --- a/packages/ui/src/components/common/BalanceDisplay.tsx +++ b/packages/ui/src/components/common/BalanceDisplay.tsx @@ -35,7 +35,7 @@ export const BalanceDisplay: FC = ({ if (pending) { return ( - + {hideBalanceLabel ? 'pending' : 'Balance: pending'} @@ -44,7 +44,7 @@ export const BalanceDisplay: FC = ({ } return ( - + {isConnected ? ( <> {isLoading ? ( diff --git a/packages/ui/src/components/common/CopyToClipBoard.tsx b/packages/ui/src/components/common/CopyToClipBoard.tsx index 64010f432..5ecfd6ae2 100644 --- a/packages/ui/src/components/common/CopyToClipBoard.tsx +++ b/packages/ui/src/components/common/CopyToClipBoard.tsx @@ -2,7 +2,6 @@ import { type FC, useState } from 'react' import { Button, Text } from '../primitives/index.js' import Tooltip from '../primitives/Tooltip.js' import { useCopyToClipboard } from 'usehooks-ts' -import { AnimatePresence, motion } from 'framer-motion' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons' @@ -42,27 +41,15 @@ export const CopyToClipBoard: FC = ({ text }) => { setOpen(true) setTimeout(() => setOpen(false), 1000) }} - css={{ color: 'gray9', _hover: { color: 'gray11' } }} + className="relay-text-[color:var(--relay-colors-gray9)] hover:relay-text-[color:var(--relay-colors-gray11)]" > - - - {isCopied ? ( - - ) : ( - - )} - - + + {isCopied ? ( + + ) : ( + + )} + ) diff --git a/packages/ui/src/components/common/CustomAddressModal.tsx b/packages/ui/src/components/common/CustomAddressModal.tsx index a8d586ca8..83d92d6fd 100644 --- a/packages/ui/src/components/common/CustomAddressModal.tsx +++ b/packages/ui/src/components/common/CustomAddressModal.tsx @@ -172,39 +172,23 @@ export const CustomAddressModal: FC = ({ trigger={null} open={open} onOpenChange={onOpenChange} - css={{ - overflow: 'hidden' - }} + className="relay-overflow-hidden" > To Address - + 0 ? '42px' : '16px' }} @@ -226,23 +210,7 @@ export const CustomAddressModal: FC = ({ )} {isLoading && ( - +
+ +
)}
{isLighterError ? ( @@ -282,7 +246,7 @@ export const CustomAddressModal: FC = ({ {didResolveLighterFromEvm && resolvedLighterIndex ? ( = ({ {!connectedAddressSet && address && isConnected ? ( = ({ color="#FFA01C" width={16} height={16} - style={{ flexShrink: 0 }} + className="relay-shrink-0" /> This isn't the connected wallet address. Please ensure that the @@ -319,7 +283,7 @@ export const CustomAddressModal: FC = ({ {!multiWalletSupportEnabled && isConnected ? ( connectedAddressSet ? ( = ({ {filteredRecentCustomAddresses.length > 0 ? ( <> Recent addresses - + {filteredRecentCustomAddresses.map((address) => ( { onConfirmed(address) onOpenChange(false) @@ -386,7 +344,7 @@ export const CustomAddressModal: FC = ({ disabled={ isLoading || !isValidAddress(toChain?.vmType, address, toChain?.id) } - css={{ justifyContent: 'center' }} + className="relay-justify-center" onClick={() => { if (isValidAddress(toChain?.vmType, address, toChain?.id)) { // Save the address to custom addresses if it's not a connected wallet address diff --git a/packages/ui/src/components/common/ErrorWell.tsx b/packages/ui/src/components/common/ErrorWell.tsx index 2f19aab05..62337670d 100644 --- a/packages/ui/src/components/common/ErrorWell.tsx +++ b/packages/ui/src/components/common/ErrorWell.tsx @@ -2,6 +2,7 @@ import * as React from 'react' import { Text } from '../primitives/index.js' import type { AxiosError } from 'axios' import type { RelayChain } from '@relayprotocol/relay-sdk' +import { cn } from '../../utils/cn.js' interface Props { error?: Error | null | AxiosError @@ -45,39 +46,21 @@ const ErrorWell: React.FC = ({ error, hasTxHashes, fromChain }) => { typeof renderedErrorMessage === 'string' && renderedErrorMessage.length > 280 - // subtle scroll style - const scrollStyles = shouldScrollErrorMessage - ? { - maxHeight: 'min(36vh, 220px)', - overflowY: 'auto', - px: '1', - scrollbarWidth: 'thin' as const, - scrollbarColor: 'var(--relay-colors-gray5) transparent', - '&::-webkit-scrollbar': { - width: '6px', - background: 'transparent' - }, - '&::-webkit-scrollbar-track': { - background: 'transparent' - }, - '&::-webkit-scrollbar-thumb': { - backgroundColor: 'var(--relay-colors-gray5)', - borderRadius: '999px' - } - } - : {} - return ( {renderedErrorMessage} diff --git a/packages/ui/src/components/common/LoadingSpinner.tsx b/packages/ui/src/components/common/LoadingSpinner.tsx index 77af3f4a8..2839e1168 100644 --- a/packages/ui/src/components/common/LoadingSpinner.tsx +++ b/packages/ui/src/components/common/LoadingSpinner.tsx @@ -1,9 +1,5 @@ import { useContext, type FC, type SVGProps } from 'react' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' +import { cn } from '../../utils/cn.js' import { ProviderOptionsContext } from '../../providers/RelayKitProvider.js' const SpinnerSVG: FC> = (props) => { @@ -21,22 +17,15 @@ const SpinnerSVG: FC> = (props) => { ) } -const LoadingSpinnerCss = cva({ - base: { - animation: 'spin 1s linear infinite', - fill: 'primary-color' - } -}) - -export const LoadingSpinner: FC<{ css?: Styles }> = ({ css }) => { +export const LoadingSpinner: FC<{ className?: string }> = ({ className }) => { const providerOptionsContext = useContext(ProviderOptionsContext) if (providerOptionsContext.loader) { return ( -
+
{providerOptionsContext.loader({ - width: (css as any)?.width ?? 20, - height: (css as any)?.height ?? 20, - fill: (css as any)?.fill + width: 20, + height: 20, + fill: undefined })}
) @@ -44,7 +33,10 @@ export const LoadingSpinner: FC<{ css?: Styles }> = ({ css }) => { return ( ) } diff --git a/packages/ui/src/components/common/Modal.tsx b/packages/ui/src/components/common/Modal.tsx index 0421c0839..a95f5d978 100644 --- a/packages/ui/src/components/common/Modal.tsx +++ b/packages/ui/src/components/common/Modal.tsx @@ -1,4 +1,4 @@ -import type { ComponentPropsWithoutRef, FC, ReactNode } from 'react' +import type { ComponentPropsWithoutRef, CSSProperties, FC, ReactNode } from 'react' import { AnimatedContent, Overlay } from '../primitives/Dialog.js' import { Root as DialogRoot, @@ -9,12 +9,11 @@ import { import { Button } from '../primitives/index.js' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faXmark } from '@fortawesome/free-solid-svg-icons/faXmark' -import type { SystemStyleObject } from '@relayprotocol/relay-design-system/types' -import { AnimatePresence } from 'framer-motion' type ModalProps = { trigger?: ReactNode - css?: SystemStyleObject + className?: string + contentStyle?: CSSProperties overlayZIndex?: number showCloseButton?: boolean disableAnimation?: boolean @@ -33,7 +32,8 @@ export const Modal: FC< > > = ({ trigger, - css, + className, + contentStyle, overlayZIndex = 10000000, showCloseButton = true, disableAnimation = false, @@ -43,61 +43,43 @@ export const Modal: FC< return ( {trigger} - - {props.open ? ( - - + + - - {showCloseButton ? ( - + - - ) : null} - {children} - - - - ) : null} - + + + + ) : null} + {children} + + + + ) : null} ) } diff --git a/packages/ui/src/components/common/MultiWalletDropdown.tsx b/packages/ui/src/components/common/MultiWalletDropdown.tsx index 26b8cd987..57c5e03b0 100644 --- a/packages/ui/src/components/common/MultiWalletDropdown.tsx +++ b/packages/ui/src/components/common/MultiWalletDropdown.tsx @@ -15,6 +15,7 @@ import { isWalletVmTypeCompatible } from '../../utils/address.js' import { ProviderOptionsContext } from '../../providers/RelayKitProvider.js' +import { cn } from '../../utils/cn.js' type MultiWalletDropdownProps = { context: 'origin' | 'destination' @@ -119,6 +120,9 @@ export const MultiWalletDropdown: FC = ({ const shouldShowEns = isEnsCapableVmType && Boolean(displayName) + const dropdownItemClassName = + 'relay-rounded-lg relay-gap-2 relay-cursor-pointer relay-p-2 relay-transition-[backdrop-filter] relay-duration-[250ms] relay-ease-linear hover:relay-brightness-[0.98] relay-shrink-0 relay-content-center relay-w-full' + return ( = ({ }} size="none" corners="pill" - css={{ - gap: '2', - px: '2 !important', - py: '1', - cursor: 'pointer', - display: 'flex', - alignContent: 'center' - }} + className="relay-gap-2 !relay-px-2 relay-py-1 relay-cursor-pointer relay-flex relay-content-center" data-testid={testId} > - + {isSupportedSelectedWallet && selectedWallet?.walletLogoUrl ? ( ) : selectedWalletAddress && !selectedWallet ? ( - + ) : null} {isSupportedSelectedWallet && selectedWalletAddress && @@ -198,13 +187,12 @@ export const MultiWalletDropdown: FC = ({ {showDropdown && ( @@ -215,10 +203,10 @@ export const MultiWalletDropdown: FC = ({ sideOffset: 12, alignOffset: -12, align: 'end', - css: { maxWidth: 248, p: 0 } + className: 'relay-max-w-[248px] relay-p-0' }} > - + {filteredWallets.map((wallet, idx) => { return ( = ({ setOpen(false) onSelect(wallet) }} - css={{ - ...DropdownItemBaseStyle, - '--borderColor': 'colors.gray.6', - border: '1px solid var(--borderColor)' - }} + className={cn( + dropdownItemClassName, + 'relay-border relay-border-solid relay-border-[var(--relay-colors-gray-6)]' + )} > {wallet.walletLogoUrl ? ( ) : null} @@ -254,9 +241,7 @@ export const MultiWalletDropdown: FC = ({ { onAnalyticEvent?.(EventNames.WALLET_SELECTOR_SELECT, { context: 'link_option' @@ -270,9 +255,7 @@ export const MultiWalletDropdown: FC = ({ {context === 'destination' && !disablePasteWalletAddressOption ? ( { onAnalyticEvent?.(EventNames.WALLET_SELECTOR_SELECT, { context: 'custom_option' @@ -288,18 +271,4 @@ export const MultiWalletDropdown: FC = ({ ) } -const DropdownItemBaseStyle = { - borderRadius: 8, - gap: '2', - cursor: 'pointer', - p: '2', - transition: 'backdrop-filter 250ms linear', - _hover: { - backdropFilter: 'brightness(98%)' - }, - flexShrink: 0, - alignContent: 'center', - width: '100%' -} - export type { MultiWalletDropdownProps } diff --git a/packages/ui/src/components/common/PercentageButtons.tsx b/packages/ui/src/components/common/PercentageButtons.tsx index 6fd50b895..ea8761561 100644 --- a/packages/ui/src/components/common/PercentageButtons.tsx +++ b/packages/ui/src/components/common/PercentageButtons.tsx @@ -6,6 +6,7 @@ import { MAX_INPUT_BUFFER_BPS, MIN_INPUT_BUFFER_UNITS } from '../../constants/maxAmountBuffer.js' +import { cn } from '../../utils/cn.js' type PercentageButtonsProps = { balance: bigint @@ -21,7 +22,7 @@ type PercentageButtonsProps = { isFromNative?: boolean variant?: 'desktop' | 'mobile' percentages?: number[] - buttonStyles?: Record + buttonClassName?: string } export const PercentageButtons: FC = ({ @@ -33,7 +34,7 @@ export const PercentageButtons: FC = ({ isFromNative, variant = 'desktop', percentages = [20, 50], - buttonStyles: customButtonStyles + buttonClassName: customButtonClassName }) => { const getExecutionBuffer = (amount: bigint) => { if (amount <= 0n) return 0n @@ -47,24 +48,21 @@ export const PercentageButtons: FC = ({ const isMobile = variant === 'mobile' - const defaultButtonStyles = { - fontSize: isMobile ? 14 : 12, - fontWeight: '500', - px: '1', - py: isMobile ? '6px' : '1', - minHeight: isMobile ? 'auto' : '23px', - lineHeight: '100%', - backgroundColor: 'widget-selector-background', - border: 'none', - borderRadius: isMobile ? '6px' : '12px', - flex: isMobile ? '1' : 'none', - justifyContent: 'center', - _hover: { - backgroundColor: 'widget-selector-hover-background' - } - } + const defaultButtonClassName = cn( + isMobile ? 'relay-text-[14px]' : 'relay-text-[12px]', + 'relay-font-medium relay-px-1', + isMobile ? 'relay-py-[6px]' : 'relay-py-1', + isMobile ? '' : 'relay-min-h-[23px]', + 'relay-leading-none', + 'relay-bg-[var(--relay-colors-widget-selector-background)]', + 'relay-border-none', + isMobile ? 'relay-rounded-[6px]' : 'relay-rounded-[12px]', + isMobile ? 'relay-flex-1' : '', + 'relay-justify-center', + 'hover:relay-bg-[var(--relay-colors-widget-selector-hover-background)]' + ) - const buttonStyles = customButtonStyles || defaultButtonStyles + const buttonClassName = customButtonClassName || defaultButtonClassName const handleMaxClick = async () => { if (!balance || !fromChain) return @@ -113,17 +111,17 @@ export const PercentageButtons: FC = ({ return ( {percentages.map((percent) => ( - + = ({ } }} placeholder="2" - containerCss={{ - width: '100%' - }} - css={{ - height: '36px', - pr: '28px !important', - border: 'none', - textAlign: 'right', - width: '100%', - smDown: { - backgroundColor: 'transparent', - '--borderColor': 'colors.gray.5', - border: '1px solid var(--borderColor)' - }, - color: slippageRatingColor - }} + containerClassName="relay-w-full" + className={cn( + 'relay-h-9 !relay-pr-7 relay-border-none relay-text-right relay-w-full', + 'max-[520px]:relay-bg-transparent max-[520px]:relay-border max-[520px]:relay-border-solid max-[520px]:relay-border-[var(--relay-colors-gray-5)]' + )} + style={{ color: tokenToColor(slippageRatingColor) }} /> - % + % {slippageRating === 'very-high' ? ( - + Very high slippage ) : null} {slippageRating === 'high' ? ( - + High slippage ) : null} @@ -392,15 +327,7 @@ export const SlippageToleranceConfig: FC = ({ aria-label="Slippage Tolerance Configuration" color="ghost" size="none" - css={{ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - gap: '4px', - borderRadius: '8px', - padding: '4px 6px', - backgroundColor: 'gray3' - }} + className="relay-flex relay-items-center relay-justify-center relay-gap-1 relay-rounded-lg relay-px-[6px] relay-py-1 relay-bg-[var(--relay-colors-gray3)]" > Slippage @@ -414,22 +341,11 @@ export const SlippageToleranceConfig: FC = ({ aria-label="Slippage Tolerance Configuration" color="ghost" size="none" - css={{ - alignItems: 'center', - justifyContent: 'center', - gap: '1', - bg: 'subtle-background-color', - color: slippageRatingColor ?? 'gray9', - p: '2', - borderRadius: 12, - border: '1px solid', - borderColor: 'gray5', - height: '36px', - px: '10px' - }} + className="relay-items-center relay-justify-center relay-gap-1 relay-bg-[var(--relay-colors-subtle-background-color)] relay-p-2 relay-rounded-[12px] relay-border relay-border-solid relay-border-[var(--relay-colors-gray5)] relay-h-9 relay-px-[10px]" + style={{ color: tokenToColor(slippageRatingColor) ?? 'var(--relay-colors-gray9)' }} > {open === false && displayValue && ( - + {displayValue}% )} @@ -463,13 +379,9 @@ export const SlippageToleranceConfig: FC = ({ } }} trigger={triggerButton} - css={{ - width: '100%', - minHeight: '262px', - maxHeight: '90vh' - }} + className="relay-w-full relay-min-h-[262px] relay-max-h-[90vh]" > - + Max Slippage @@ -493,7 +405,7 @@ export const SlippageToleranceConfig: FC = ({ contentProps={{ align: 'end', sideOffset: 5, - css: { maxWidth: 188, mx: 0 }, + className: 'relay-max-w-[188px] relay-mx-0', avoidCollisions: false, onCloseAutoFocus: (e) => { e.preventDefault() @@ -502,22 +414,22 @@ export const SlippageToleranceConfig: FC = ({ > - + Max Slippage If the price exceeds the maximum slippage percentage, the transaction will revert. } > - + diff --git a/packages/ui/src/components/common/StepIcon.tsx b/packages/ui/src/components/common/StepIcon.tsx index 1fddd3593..01b2ee6f9 100644 --- a/packages/ui/src/components/common/StepIcon.tsx +++ b/packages/ui/src/components/common/StepIcon.tsx @@ -39,13 +39,7 @@ export const StepIcon: FC = ({ stepId, chainId }) => { return ( {getIconForStep()} diff --git a/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx b/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx index 90d674e80..cb51af085 100644 --- a/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx +++ b/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx @@ -27,6 +27,7 @@ import { import Tooltip from '../../../components/primitives/Tooltip.js' import { EventNames } from '../../../constants/events.js' import { ChainSearchInput } from './ChainFilterRow.js' +import { cn } from '../../../utils/cn.js' export type ChainFilterValue = | RelayChain @@ -90,26 +91,15 @@ const ChainFilter: FC = ({ aria-label={`Chain filter`} color="ghost" size="none" - css={{ - gap: '2', - height: 40, - width: '100%', - px: '4 !important', - cursor: 'pointer', - display: 'flex', - alignContent: 'center', - lineHeight: '20px', - backgroundColor: 'dropdown-background', - borderRadius: 'dropdown-border-radius' - }} + className="relay-gap-2 relay-h-[40px] relay-w-full !relay-px-4 relay-cursor-pointer relay-flex relay-content-center relay-leading-[20px] relay-bg-[var(--relay-colors-dropdown-background)] relay-rounded-dropdown" > - + {value.id ? ( ) : ( @@ -120,12 +110,10 @@ const ChainFilter: FC = ({ @@ -134,19 +122,14 @@ const ChainFilter: FC = ({ contentProps={{ align: 'start', avoidCollisions: false, - css: { - p: 0, - width: 'var(--radix-dropdown-menu-trigger-width)', - minWidth: 'var(--radix-dropdown-menu-trigger-width)', - mx: '0' - }, + className: 'relay-p-0 relay-mx-0', style: { width: 'var(--radix-popper-anchor-width)', minWidth: 'var(--radix-popper-anchor-width)' } }} > - + = ({ /> {filteredChains ? ( filteredChains.length > 0 ? ( @@ -173,15 +152,7 @@ const ChainFilter: FC = ({ onSelect(chain) setChainSearchInput('') }} - css={{ - padding: '8px', - borderRadius: 4, - cursor: 'pointer', - backgroundColor: 'modal-background', - _hover: { - backgroundColor: 'gray3' - } - }} + className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" > = ({ ) }) ) : ( - + No results. ) @@ -206,15 +177,7 @@ const ChainFilter: FC = ({ onSelect(allChainsOption) setChainSearchInput('') }} - css={{ - padding: '8px', - borderRadius: 4, - cursor: 'pointer', - backgroundColor: 'modal-background', - _hover: { - backgroundColor: 'gray3' - } - }} + className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" > = ({ {starredChains.length > 0 && ( <> - - + + @@ -237,7 +200,7 @@ const ChainFilter: FC = ({ Long-press to star a chain } > - + = ({ onSelect(chain) setChainSearchInput('') }} - css={{ - padding: '8px', - borderRadius: 4, - cursor: 'pointer', - backgroundColor: 'modal-background', - _hover: { - backgroundColor: 'gray3' - } - }} + className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" > = ({ )} - + Chains A-Z {alphabeticalChains.map((chain) => { @@ -292,15 +247,7 @@ const ChainFilter: FC = ({ onSelect(chain) setChainSearchInput('') }} - css={{ - padding: '8px', - borderRadius: 4, - cursor: 'pointer', - backgroundColor: 'modal-background', - _hover: { - backgroundColor: 'gray3' - } - }} + className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" > = ({ return ( {chain.name} @@ -439,7 +380,7 @@ const ChainFilterRow: FC = ({ } return ( -
+
{ @@ -455,25 +396,14 @@ const ChainFilterRow: FC = ({ onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} onTouchMove={handleTouchMove} - css={{ - gap: '2', - cursor: 'pointer', - flexShrink: 0, - alignContent: 'center', - width: '100%', - position: 'relative', - userSelect: 'none' - }} - style={{ - WebkitUserSelect: 'none' - }} + className="relay-gap-2 relay-cursor-pointer relay-shrink-0 relay-content-center relay-w-full relay-relative relay-select-none" > {('displayName' in chain && chain.displayName) || chain.name} {showStar && isStarred && ( - + )} @@ -483,14 +413,7 @@ const ChainFilterRow: FC = ({ {dropdownOpen && (
{ e.stopPropagation() handleToggleStar() @@ -499,28 +422,18 @@ const ChainFilterRow: FC = ({ onTouchStart={(e) => e.stopPropagation()} > - + {isStarred ? 'Unstar chain' : 'Star chain'} diff --git a/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx b/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx index 517921306..0198f411a 100644 --- a/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx +++ b/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx @@ -18,6 +18,7 @@ import { toggleStarredChain } from '../../../utils/localStorage.js' import { EventNames } from '../../../constants/events.js' +import { cn } from '../../../utils/cn.js' export type ChainFilterRowProps = { chain: ChainFilterValue @@ -89,13 +90,7 @@ export const ChainFilterRow: FC = ({ return ( {chain.name} @@ -104,32 +99,21 @@ export const ChainFilterRow: FC = ({ } return ( -
+
{ e.preventDefault() setDropdownOpen(true) }} - css={{ - gap: '2', - cursor: 'pointer', - flexShrink: 0, - alignContent: 'center', - width: '100%', - position: 'relative', - userSelect: 'none' - }} - style={{ - WebkitUserSelect: 'none' - }} + className="relay-gap-2 relay-cursor-pointer relay-shrink-0 relay-content-center relay-w-full relay-relative relay-select-none" > {('displayName' in chain && chain.displayName) || chain.name} {showStar && isStarred && ( - + )} @@ -140,14 +124,7 @@ export const ChainFilterRow: FC = ({ {dropdownOpen && (
{ e.stopPropagation() handleToggleStar() @@ -156,27 +133,18 @@ export const ChainFilterRow: FC = ({ onTouchStart={(e) => e.stopPropagation()} > - + {isStarred ? 'Unstar chain' : 'Star chain'} @@ -201,24 +169,12 @@ export const ChainSearchInput = forwardRef< ref={ref} placeholder={placeholder} icon={ - + } - containerCss={{ - width: '100%', - height: 40, - mb: '2' - }} - css={{ - width: '100%', - _placeholder_parent: { - textOverflow: 'ellipsis' - }, - '--borderColor': 'colors.subtle-border-color', - border: '1px solid var(--borderColor)', - backgroundColor: 'modal-background' - }} + containerClassName="relay-w-full relay-h-[40px] relay-mb-2" + className="relay-w-full relay-border relay-border-solid relay-border-[var(--relay-colors-subtle-border-color)] relay-bg-[var(--relay-colors-modal-background)] [&::placeholder]:relay-text-ellipsis" value={value} onChange={(e) => onChange((e.target as HTMLInputElement).value)} onKeyDown={onKeyDown} diff --git a/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx b/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx index ca253764d..0fb31b982 100644 --- a/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx +++ b/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx @@ -30,6 +30,7 @@ import { } from '../../../utils/localStorage.js' import Tooltip from '../../../components/primitives/Tooltip.js' import { ChainSearchInput } from './ChainFilterRow.js' +import { cn } from '../../../utils/cn.js' type ChainFilterSidebarProps = { options: (RelayChain | { id: undefined; name: string })[] @@ -111,14 +112,7 @@ export const ChainFilterSidebar: FC = ({ return ( { @@ -150,12 +144,7 @@ export const ChainFilterSidebar: FC = ({ } } }} - css={{ - display: 'flex', - flexDirection: 'column', - width: '100%', - height: '100%' - }} + className="relay-flex relay-flex-col relay-w-full relay-h-full" > = ({ {filteredChains ? ( // Show search results without sections @@ -226,27 +211,12 @@ export const ChainFilterSidebar: FC = ({ } }} ref={isSameChainSelected ? activeChainRef : null} - css={{ - p: '2', - display: 'flex', - alignItems: 'center', - gap: '2', - position: 'relative', - ...(isSameChainSelected && { - backgroundColor: 'gray6' - }), - transition: 'backdrop-filter 250ms linear', - _hover: { - backgroundColor: - isSameChainSelected - ? 'gray6' - : 'gray/10' - }, - '--focusColor': 'colors.focus-color', - _focus: { - boxShadow: 'inset 0 0 0 2px var(--focusColor)' - } - }} + className={cn( + 'relay-p-2 relay-flex relay-items-center relay-gap-2 relay-relative relay-transition-[backdrop-filter] relay-duration-[250ms] relay-ease-linear focus:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', + isSameChainSelected + ? 'relay-bg-[var(--relay-colors-gray6)] hover:relay-bg-[var(--relay-colors-gray6)]' + : 'hover:relay-bg-[rgba(var(--relay-colors-gray-rgb,0,0,0),0.1)]' + )} > = ({ {starredChains.length > 0 && ( <> - - + + @@ -277,7 +247,7 @@ export const ChainFilterSidebar: FC = ({ Right-click to star a chain } > - + = ({ )} - + Chains A-Z {alphabeticalChains.map((chain) => { @@ -433,24 +403,12 @@ const ChainFilterRow: FC = ({ size="none" onClick={onClick} ref={isActive ? activeChainRef : null} - css={{ - p: '2', - display: 'flex', - alignItems: 'center', - gap: '2', - position: 'relative', - ...(isActive && { - backgroundColor: 'gray6' - }), - transition: 'backdrop-filter 250ms linear', - _hover: { - backgroundColor: isActive ? 'gray6' : 'gray/10' - }, - '--focusColor': 'colors.focus-color', - _focus: { - boxShadow: 'inset 0 0 0 2px var(--focusColor)' - } - }} + className={cn( + 'relay-p-2 relay-flex relay-items-center relay-gap-2 relay-relative relay-transition-[backdrop-filter] relay-duration-[250ms] relay-ease-linear focus:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', + isActive + ? 'relay-bg-[var(--relay-colors-gray6)] hover:relay-bg-[var(--relay-colors-gray6)]' + : 'hover:relay-bg-[rgba(var(--relay-colors-gray-rgb,0,0,0),0.1)]' + )} > @@ -462,7 +420,7 @@ const ChainFilterRow: FC = ({ } return ( - + @@ -141,7 +126,7 @@ export const MobileChainSelector: FC = ({ + = ({ /> } - containerCss={{ - flex: 1, - height: 40 - }} - css={{ - width: '100%', - _placeholder_parent: { - textOverflow: 'ellipsis' - } - }} + containerClassName="relay-flex-1 relay-h-[40px]" + className="relay-w-full [&::placeholder]:relay-text-ellipsis" value={chainSearchInput} onChange={(e) => setChainSearchInput((e.target as HTMLInputElement).value) @@ -169,16 +146,7 @@ export const MobileChainSelector: FC = ({ color="ghost" size="none" onClick={onClose} - css={{ - p: '2', - borderRadius: '8px', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - minWidth: '40px', - height: '40px', - color: 'gray9' - }} + className="relay-p-2 relay-rounded-[8px] relay-flex relay-items-center relay-justify-center relay-min-w-[40px] relay-h-[40px] relay-text-[color:var(--relay-colors-gray9)]" > @@ -214,14 +182,7 @@ export const MobileChainSelector: FC = ({ } } }} - css={{ - display: 'flex', - flexDirection: 'column', - width: '100%', - flex: 1, - overflowY: 'auto', - scrollbarColor: 'var(--relay-colors-gray5) transparent' - }} + className="relay-flex relay-flex-col relay-w-full relay-flex-1 relay-overflow-y-auto [scrollbar-color:var(--relay-colors-gray5)_transparent]" > {filteredChains ? ( // Show search results without sections @@ -253,19 +214,7 @@ export const MobileChainSelector: FC = ({ diff --git a/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx b/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx index 60aa15a82..78d461d3e 100644 --- a/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx +++ b/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx @@ -167,11 +167,7 @@ const InnerDepositAddressModal: FC = ({ trigger={null} open={open} onOpenChange={onOpenChange} - css={{ - overflow: 'hidden', - p: '4', - maxWidth: '412px !important' - }} + className="relay-overflow-hidden relay-p-4 !relay-max-w-[412px]" showCloseButton={true} onPointerDownOutside={(e) => { const dynamicModalElements = Array.from( @@ -188,13 +184,9 @@ const InnerDepositAddressModal: FC = ({ > - + {isWaitingForDeposit ? 'Manual Transfer' : 'Trade Details'} diff --git a/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx b/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx index b57f6f312..b8945bda2 100644 --- a/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx +++ b/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx @@ -235,14 +235,7 @@ const InnerTransactionModal: FC = ({ trigger={null} open={open} onOpenChange={onOpenChange} - css={{ - overflow: 'hidden', - p: '4', - maxWidth: '412px !important', - '@media(max-width: 520px)': { - maxWidth: 'unset !important' - } - }} + className="relay-overflow-hidden relay-p-4 !relay-max-w-[412px] max-[520px]:!relay-max-w-[unset]" showCloseButton={true} onPointerDownOutside={(e) => { const dynamicModalElements = Array.from( @@ -259,13 +252,9 @@ const InnerTransactionModal: FC = ({ > - + Transaction Details {progressStep === TransactionProgressStep.Confirmation || diff --git a/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx index d99b9deaf..b79a634b3 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx @@ -18,6 +18,7 @@ import { faRepeat } from '@fortawesome/free-solid-svg-icons' import { truncateAddress } from '../../../../utils/truncate.js' import { getTxBlockExplorerUrl } from '../../../../utils/getTxBlockExplorerUrl.js' import useRelayClient from '../../../../hooks/useRelayClient.js' +import { cn } from '../../../../utils/cn.js' type ApprovalPlusSwapStepProps = { fromToken?: Token @@ -45,28 +46,22 @@ export const ApprovalPlusSwapStep: FC = ({ align="center" justify="between" direction="column" - css={{ flexShrink: 0, bp500: { flexDirection: 'row' } }} + className="relay-shrink-0 bp500:relay-flex-row" > {fromAmountFormatted} {fromToken?.symbol} @@ -78,34 +73,24 @@ export const ApprovalPlusSwapStep: FC = ({ {toAmountFormatted} {toToken?.symbol} @@ -118,14 +103,7 @@ export const ApprovalPlusSwapStep: FC = ({ {steps?.map((step, index) => { const isCurrentStep = @@ -153,41 +131,36 @@ export const ApprovalPlusSwapStep: FC = ({ - + {step.id === 'approve' ? ( ) : ( )} - + {stepTitle} {isApproveStep && !hasTxHash && ( @@ -206,7 +179,7 @@ export const ApprovalPlusSwapStep: FC = ({ key={txHash} href={txUrl} target="_blank" - css={{ fontSize: 12 }} + className="relay-text-[12px]" > View Tx: {truncateAddress(txHash, '...', 6, 4)} @@ -218,12 +191,12 @@ export const ApprovalPlusSwapStep: FC = ({ {isCurrentStep && hasTxHash ? ( ) : step?.items?.every( (item) => item.status === 'complete' ) ? ( - + ) : null} @@ -231,14 +204,8 @@ export const ApprovalPlusSwapStep: FC = ({ {index !== (steps?.length || 0) - 1 && ( - - + + )} diff --git a/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx index c0bae385b..3960a54ab 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx @@ -23,15 +23,15 @@ export const DepositAddressValidatingStep: FC< <> - + Funds received. Your transaction is now in progress. Feel free to leave at any time, you can track your progress within the{' '} diff --git a/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx index 40935c659..310824d7f 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx @@ -1,6 +1,5 @@ import { type FC } from 'react' import { Box, Button, Flex, Text } from '../../../primitives/index.js' -import { motion } from 'framer-motion' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons/faCircleExclamation' import { faArrowRight } from '@fortawesome/free-solid-svg-icons/faArrowRight' @@ -109,47 +108,39 @@ export const ErrorStep: FC = ({ direction="column" align="center" justify="between" - css={{ width: '100%' }} + className="relay-w-full" > - +
{isRefund ? ( - - + + ) : null} {!isRefund && isSolverStatusTimeout ? ( - + ) : null} {!isRefund && !isSolverStatusTimeout ? ( - - + + ) : null} - +
{isRefund ? ( transaction?.data?.failReason === 'UNKNOWN' || !transaction?.data?.failReason ? ( - + It looks like an unknown issue occurred during the transaction. {fromToken && fromAmountFormatted && fromChain ? ` We've refunded ${fromAmountFormatted} ${fromToken.symbol} on ${fromChain.displayName}.` @@ -157,11 +148,11 @@ export const ErrorStep: FC = ({ {/* Transaction Pills */} - + {fromToken && fromChain ? ( = ({ tokenSymbol={fromToken.symbol} size="base" /> - + {fromAmountFormatted} {fromToken.symbol} @@ -177,16 +168,14 @@ export const ErrorStep: FC = ({ ? )} - - + + {toToken && toChain ? ( = ({ tokenSymbol={toToken.symbol} size="base" /> - + {toAmountFormatted} {toToken.symbol} @@ -204,7 +193,7 @@ export const ErrorStep: FC = ({ ) : ( - + {refundDetails ? ` We've refunded ${refundDetails.amountFormatted} ${refundDetails.currency?.symbol} on ${refundChain?.displayName}.` @@ -222,15 +211,7 @@ export const ErrorStep: FC = ({ <> = ({ /> - + @@ -266,10 +244,7 @@ export const ErrorStep: FC = ({ onClick={() => { onOpenChange(false) }} - css={{ - justifyContent: 'center', - width: '100%' - }} + className="relay-justify-center relay-w-full" > Done @@ -281,10 +256,7 @@ export const ErrorStep: FC = ({ onClick={() => { onOpenChange(false) }} - css={{ - justifyContent: 'center', - width: '100%' - }} + className="relay-justify-center relay-w-full" > Done diff --git a/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx index c9b4a5eee..363957431 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx @@ -11,6 +11,7 @@ import { formatBN } from '../../../../utils/numbers.js' import { getTxBlockExplorerUrl } from '../../../../utils/getTxBlockExplorerUrl.js' import useRelayClient from '../../../../hooks/useRelayClient.js' import { StepIcon } from '../../StepIcon.js' +import { cn } from '../../../../utils/cn.js' type SwapConfirmationStepProps = { fromToken?: Token @@ -80,68 +81,48 @@ export const SwapConfirmationStep: FC = ({ align="center" justify="between" direction="column" - css={{ flexShrink: 0, bp500: { flexDirection: 'row' } }} + className="relay-shrink-0 bp500:relay-flex-row" > - + {fromChain?.displayName} - + {fromAmountFormatted} {fromToken?.symbol} - + {toChain?.displayName} - + {toAmountFormatted} {toToken?.symbol} @@ -151,14 +132,7 @@ export const SwapConfirmationStep: FC = ({ Additional Gas @@ -170,28 +144,15 @@ export const SwapConfirmationStep: FC = ({ ) : null} {formattedSteps.map((step, index) => ( {index !== formattedSteps.length - 1 && ( - - + + )} @@ -231,37 +192,30 @@ export const StepRow: FC = ({ const relayClient = useRelayClient() const chains = relayClient?.chains return ( - - + + *': { - color: isCompleted ? 'green11' : isActive ? 'primary11' : 'gray9' - } + : 'none' }} > {isCompleted ? ( @@ -270,13 +224,13 @@ export const StepRow: FC = ({ )} - + {action} {subText && ( - + {(() => { // Handle "Success: txhash" case with split colors and link if (subText.startsWith('Success:')) { @@ -289,8 +243,11 @@ export const StepRow: FC = ({ : undefined return ( - - + + {successText}: {hashPart && @@ -299,20 +256,20 @@ export const StepRow: FC = ({ href={txUrl} target="_blank" rel="noopener noreferrer" - style={{ - color: 'var(--colors-primary11)', - textDecoration: 'none' - }} + className="relay-text-[color:var(--colors-primary11)] relay-no-underline" > {hashPart} ) : ( - + {hashPart} ))} @@ -335,8 +292,11 @@ export const StepRow: FC = ({ : undefined return ( - - + + {labelText}: {hashPart && @@ -345,20 +305,20 @@ export const StepRow: FC = ({ href={txUrl} target="_blank" rel="noopener noreferrer" - style={{ - color: 'var(--colors-primary11)', - textDecoration: 'none' - }} + className="relay-text-[color:var(--colors-primary11)] relay-no-underline" > {hashPart} ) : ( - + {hashPart} ))} @@ -369,23 +329,22 @@ export const StepRow: FC = ({ return ( {subText} ) })()} {showSubTextSpinner && ( - + )} )} diff --git a/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx index 5949df40a..5aaaa4bcc 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx @@ -9,7 +9,6 @@ import { Skeleton, Anchor } from '../../../primitives/index.js' -import { motion } from 'framer-motion' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { type TxHashes } from '../TransactionModalRenderer.js' import { type Token } from '../../../../types/index.js' @@ -168,28 +167,14 @@ export const SwapSuccessStep: FC = ({ return isDelayedTx ? ( <> - +
= ({ /> - - + + - +
- + {isBitcoinOrigin || isBitcoinDestination ? `Bitcoin confirmation takes ${estimatedMinutes} minutes. Track progress on the transaction page.` : `Processing bridge, this will take ~${estimatedMinutes} ${estimatedMinutes === 1 ? 'min' : 'mins'}.`} - + {fromChain ? ( - + = ({ size="sm" chainRadius={2.5} /> - + {_fromAmountFormatted} {_fromToken?.symbol} ) : ( ? )} - - + + {toChain ? ( - + = ({ size="sm" chainRadius={2.5} /> - + {_toAmountFormatted} {_toToken?.symbol} @@ -260,13 +248,8 @@ export const SwapSuccessStep: FC = ({ )} You can close this modal while it finalizes on the blockchain. The transaction will continue in the background. @@ -287,7 +270,7 @@ export const SwapSuccessStep: FC = ({ View Refund Tx: {truncatedHash} @@ -298,7 +281,7 @@ export const SwapSuccessStep: FC = ({ Fetching refund transaction... @@ -317,7 +300,7 @@ export const SwapSuccessStep: FC = ({ View Tx: {truncatedHash} @@ -331,14 +314,7 @@ export const SwapSuccessStep: FC = ({ {!delayedTxUrl ? ( = ({ ) : null} - + {delayedTxUrl ? ( - + @@ -382,48 +352,25 @@ export const SwapSuccessStep: FC = ({ ) : ( <> - +
- +
{fillTime && fillTime !== '-' ? ( <> @@ -436,22 +383,15 @@ export const SwapSuccessStep: FC = ({ {_fromToken ? ( - + Sent - + = ({ /> {isLoadingTransaction ? ( ) : ( @@ -481,7 +421,7 @@ export const SwapSuccessStep: FC = ({ {truncateAddress(txHash, '...', 6, 4)} @@ -494,12 +434,12 @@ export const SwapSuccessStep: FC = ({ )} {_toToken ? ( - + Received - + = ({ /> {isLoadingTransaction ? ( ) : ( @@ -528,7 +468,7 @@ export const SwapSuccessStep: FC = ({ {truncateAddress(txHash, '...', 6, 4)} @@ -538,7 +478,7 @@ export const SwapSuccessStep: FC = ({ {/* Additional Gas - positioned below transaction hash with 8px spacing */} {formattedGasTopUpAmount && gasTopUpAmountCurrency ? ( - + = ({ - + {requestId ? (
{ e.stopPropagation() @@ -574,10 +514,7 @@ export const SwapSuccessStep: FC = ({ @@ -588,10 +525,7 @@ export const SwapSuccessStep: FC = ({ onClick={() => { onOpenChange(false) }} - css={{ - justifyContent: 'center', - width: '100%' - }} + className="relay-justify-center relay-w-full" > Done diff --git a/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx b/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx index 750e34627..1a04be64f 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx @@ -100,26 +100,21 @@ export const TransactionsByChain: FC = ({ // Return single container with all transactions return ( - + {transactions.map((transaction, idx) => ( - - + + {transaction.label} {transaction.isRefund ? ( {' '} Refunded @@ -181,7 +176,7 @@ export const TransactionsByChain: FC = ({ transaction.txHashes.length === 0 && isSolverStatusTimeout ? ( - + ) : null} diff --git a/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx index 2e95437ee..df44315f8 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx @@ -56,19 +56,11 @@ export const WaitingForDepositStep: FC = ({ <> Transfer funds manually from your {fromChain?.displayName} wallet to - Relay’s deposit address to complete the bridge. + Relay's deposit address to complete the bridge. = ({ Learn More - - + + Network {fromChain?.displayName} - + Amount to transfer {fromAmountFormatted ? ( <> {' '} - + {fromAmountFormatted} = ({ src={fromToken?.logoURI} width={20} height={20} - style={{ - borderRadius: 9999 - }} + className="relay-rounded-full" /> {fromToken?.symbol} ) : ( - + )}{' '} @@ -131,85 +113,55 @@ export const WaitingForDepositStep: FC = ({ {isFetchingQuote ? ( - + ) : ( <> - + {truncateAddress(depositAddress, '...', 28, 4)} {qrcodeUrl ? ( - - + + - - +
- {fromToken?.name} @@ -219,20 +171,16 @@ export const WaitingForDepositStep: FC = ({ height={105} level={'H'} imageSettings={{ - src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAhCAYAAAC803lsAAAMP2lDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EESkBpITQQu8INkISIJQQA0HFjiwquBZULGBDV0UUrIDYETuLYu8LKgrKuliwK29SQNd95Xvn++be//5z5j9nzp1bBgC1kxyRKBtVByBHmC+OCfKjj09KppN6AAJQQAE6wJLDzRMxo6LCALSh89/t3U3oDe2avVTrn/3/1TR4/DwuAEgUxKm8PG4OxAcBwKu4InE+AEQpbzYtXyTFsAEtMUwQ4kVSnC7HVVKcKsd7ZT5xMSyIWwFQUuFwxOkAqF6BPL2Amw41VPshdhTyBEIA1OgQe+fk5PIgToHYGvqIIJbqM1J/0En/m2bqsCaHkz6M5XORmZK/IE+UzZnxf5bjf1tOtmQohiVsKhni4BjpnGHdbmflhkqxCsR9wtSISIg1If4g4Mn8IUYpGZLgeLk/asDNY8GawbsMUEcexz8UYgOIA4XZEWEKPjVNEMiGGK4QdLognx0HsS7Ei/h5AbEKn83i3BhFLLQhTcxiKvjzHLEsrjTWQ0lWPFOh/zqDz1boY6qFGXGJEFMgNi8QJERArAqxQ15WbKjCZ1xhBitiyEcsiZHmbw5xDF8Y5CfXxwrSxIExCv/SnLyh+WKbMwTsCAXen58RFyyvD9bK5cjyh3PBrvCFzPghHX7e+LChufD4/gHyuWM9fGF8rELngyjfL0Y+FqeIsqMU/rgpPztIyptC7JxXEKsYiyfkwwUp18fTRPlRcfI88cJMTkiUPB98OQgDLOAP6EACWyrIBZlA0N7X2Aev5D2BgAPEIB3wgb2CGRqRKOsRwmMsKAR/QsQHecPj/GS9fFAA+a/DrPxoD9JkvQWyEVngKcQ5IBRkw2uJbJRwOFoCeAIZwT+ic2DjwnyzYZP2/3t+iP3OMCETpmAkQxHpakOexACiPzGYGEi0wfVxb9wTD4NHX9iccAbuPjSP7/6Ep4QOwiPCDUIn4c4UQZH4pyzDQSfUD1TUIvXHWuCWUNMF98O9oDpUxnVwfWCPO8M4TNwHRnaBLEuRt7Qq9J+0/zaDH+6Gwo/sSEbJI8i+ZOufR6raqroMq0hr/WN95LmmDtebNdzzc3zWD9XnwXPoz57YIuwAdg47hV3AjmKNgI6dwJqwNuyYFA+vriey1TUULUaWTxbUEfwj3tCdlVYyz7HWsdfxi7wvnz9d+o4GrFzRDLEgPSOfzoRfBD6dLeQ6jKI7OTo5AyD9vshfX2+iZd8NRKftO7fgDwC8TgwODh75zoWcAGCfG3z8D3/nrBnw06EMwPnDXIm4QM7h0gMBviXU4JOmB4yAGbCG83ECrsAT+IIAEAIiQRxIApNh9hlwnYvBNDALzAcloAwsB6vBerAJbAU7wR6wHzSCo+AUOAsugSvgBrgHV083eAH6wTvwGUEQEkJFaIgeYoxYIHaIE8JAvJEAJAyJQZKQFCQdESISZBayAClDypH1yBakBtmHHEZOIReQDuQO0oX0Iq+RTyiGqqBaqCFqiY5GGSgTDUXj0EloOjoVLUSL0aXoWrQa3Y02oKfQS+gNtBN9gQ5gAFPGdDATzB5jYCwsEkvG0jAxNgcrxSqwaqwOa4b3+RrWifVhH3EiTsPpuD1cwcF4PM7Fp+Jz8CX4enwn3oC34tfwLrwf/0agEgwIdgQPApswnpBOmEYoIVQQthMOEc7AZ6mb8I5IJOoQrYhu8FlMImYSZxKXEDcQ64kniR3Ex8QBEomkR7IjeZEiSRxSPqmEtI60m3SCdJXUTfqgpKxkrOSkFKiUrCRUKlKqUNqldFzpqtIzpc9kdbIF2YMcSeaRZ5CXkbeRm8mXyd3kzxQNihXFixJHyaTMp6yl1FHOUO5T3igrK5squytHKwuU5ymvVd6rfF65S/mjiqaKrQpLZaKKRGWpyg6Vkyp3VN5QqVRLqi81mZpPXUqtoZ6mPqR+UKWpOqiyVXmqc1UrVRtUr6q+VCOrWagx1SarFapVqB1Qu6zWp05Wt1RnqXPU56hXqh9Wv6U+oEHTGKMRqZGjsURjl8YFjR5NkqalZoAmT7NYc6vmac3HNIxmRmPRuLQFtG20M7RuLaKWlRZbK1OrTGuPVrtWv7amtrN2gvZ07UrtY9qdOpiOpQ5bJ1tnmc5+nZs6n0YYjmCO4I9YPKJuxNUR73VH6vrq8nVLdet1b+h+0qPrBehl6a3Qa9R7oI/r2+pH60/T36h/Rr9vpNZIz5HckaUj94+8a4Aa2BrEGMw02GrQZjBgaGQYZCgyXGd42rDPSMfI1yjTaJXRcaNeY5qxt7HAeJXxCePndG06k55NX0tvpfebGJgEm0hMtpi0m3w2tTKNNy0yrTd9YEYxY5ilma0yazHrNzc2DzefZV5rfteCbMGwyLBYY3HO4r2llWWi5ULLRsseK10rtlWhVa3VfWuqtY/1VOtq6+s2RBuGTZbNBpsrtqiti22GbaXtZTvUztVOYLfBrmMUYZT7KOGo6lG37FXsmfYF9rX2XQ46DmEORQ6NDi9Hm49OHr1i9LnR3xxdHLMdtzneG6M5JmRM0ZjmMa+dbJ24TpVO18dSxwaOnTu2aewrZztnvvNG59suNJdwl4UuLS5fXd1cxa51rr1u5m4pblVutxhajCjGEsZ5d4K7n/tc96PuHz1cPfI99nv85WnvmeW5y7NnnNU4/rht4x57mXpxvLZ4dXrTvVO8N3t3+pj4cHyqfR75mvnyfLf7PmPaMDOZu5kv/Rz9xH6H/N6zPFizWSf9Mf8g/1L/9gDNgPiA9QEPA00D0wNrA/uDXIJmBp0MJgSHBq8IvsU2ZHPZNez+ELeQ2SGtoSqhsaHrQx+F2YaJw5rD0fCQ8JXh9yMsIoQRjZEgkh25MvJBlFXU1Kgj0cToqOjK6KcxY2JmxZyLpcVOid0V+y7OL25Z3L1463hJfEuCWsLEhJqE94n+ieWJneNHj589/lKSfpIgqSmZlJyQvD15YELAhNUTuie6TCyZeHOS1aTpky5M1p+cPfnYFLUpnCkHUggpiSm7Ur5wIjnVnIFUdmpVaj+XxV3DfcHz5a3i9fK9+OX8Z2leaeVpPele6SvTezN8Mioy+gQswXrBq8zgzE2Z77Mis3ZkDWYnZtfnKOWk5BwWagqzhK25RrnTcztEdqISUedUj6mrp/aLQ8Xb85C8SXlN+VrwR75NYi35RdJV4F1QWfBhWsK0A9M1pgunt82wnbF4xrPCwMLfZuIzuTNbZpnMmj+razZz9pY5yJzUOS1zzeYWz+2eFzRv53zK/Kz5vxc5FpUXvV2QuKC52LB4XvHjX4J+qS1RLRGX3FrouXDTInyRYFH74rGL1y3+VsorvVjmWFZR9mUJd8nFX8f8uvbXwaVpS9uXuS7buJy4XLj85gqfFTvLNcoLyx+vDF/ZsIq+qnTV29VTVl+ocK7YtIayRrKmc23Y2qZ15uuWr/uyPmP9jUq/yvoqg6rFVe838DZc3ei7sW6T4aayTZ82Czbf3hK0paHasrpiK3Frwdan2xK2nfuN8VvNdv3tZdu/7hDu6NwZs7O1xq2mZpfBrmW1aK2ktnf3xN1X9vjvaaqzr9tSr1Nfthfslex9vi9l3839oftbDjAO1B20OFh1iHaotAFpmNHQ35jR2NmU1NRxOORwS7Nn86EjDkd2HDU5WnlM+9iy45TjxccHTxSeGDgpOtl3Kv3U45YpLfdOjz99vTW6tf1M6JnzZwPPnj7HPHfivNf5oxc8Lhy+yLjYeMn1UkObS9uh311+P9Tu2t5w2e1y0xX3K80d4zqOX/W5euqa/7Wz19nXL92IuNFxM/7m7VsTb3Xe5t3uuZN959Xdgruf7827T7hf+kD9QcVDg4fVf9j8Ud/p2nmsy7+r7VHso3uPuY9fPMl78qW7+Cn1acUz42c1PU49R3sDe688n/C8+4Xoxee+kj81/qx6af3y4F++f7X1j+/vfiV+Nfh6yRu9NzveOr9tGYgaePgu593n96Uf9D7s/Mj4eO5T4qdnn6d9IX1Z+9Xma/O30G/3B3MGB0UcMUf2K4DBhqalAfB6BwDUJABocH9GmSDf/8kMke9ZZQj8JyzfI8rMFYA6+P8e3Qf/bm4BsHcb3H5BfbWJAERRAYhzB+jYscNtaK8m21dKjQj3AZujv6bmpIJ/Y/I95w95/3wGUlVn8PP5XwFtfFZdDkCYAAAAlmVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAhKACAAQAAAABAAAAIqADAAQAAAABAAAAIQAAAABBU0NJSQAAAFNjcmVlbnNob3Qpdkf5AAAACXBIWXMAABYlAAAWJQFJUiTwAAAC12lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MTk0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjE4NjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjE0NDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+MTQ0PC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4Kmj5GzQAAACtJREFUWAnt0DEBAAAAwqD1T20ND4hAYcCAAQMGDBgwYMCAAQMGDBgwcDEwEakAAc3C1KAAAAAASUVORK5CYII=', + src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAhCAYAAAC803lsAAAMP2lDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EESkBpITQQu8INkISIJQQA0HFjiwquBZULGBDV0UUrIDYETuLYu8LKgrKuliwK29SQNd95Xvn++be//5z5j9nzp1bBgC1kxyRKBtVByBHmC+OCfKjj09KppN6AAJQQAE6wJLDzRMxo6LCALSh89/t3U3oDe2avVTrn/3/1TR4/DwuAEgUxKm8PG4OxAcBwKu4InE+AEQpbzYtXyTFsAEtMUwQ4kVSnC7HVVKcKsd7ZT5xMSyIWwFQUuFwxOkAqF6BPL2Amw41VPshdhTyBEIA1OgQe+fk5PIgToHYGvqIIJbqM1J/0En/m2bqsCaHkz6M5XORmZK/IE+UzZnxf5bjf1tOtmQohiVsKhni4BjpnGHdbmflhkqxCsR9wtSISIg1If4g4Mn8IUYpGZLgeLk/asDNY8GawbsMUEcexz8UYgOIA4XZEWEKPjVNEMiGGK4QdLognx0HsS7Ei/h5AbEKn83i3BhFLLQhTcxiKvjzHLEsrjTWQ0lWPFOh/zqDz1boY6qFGXGJEFMgNi8QJERArAqxQ15WbKjCZ1xhBitiyEcsiZHmbw5xDF8Y5CfXxwrSxIExCv/SnLyh+WKbMwTsCAXen58RFyyvD9bK5cjyh3PBrvCFzPghHX7e+LChufD4/gHyuWM9fGF8rELngyjfL0Y+FqeIsqMU/rgpPztIyptC7JxXEKsYiyfkwwUp18fTRPlRcfI88cJMTkiUPB98OQgDLOAP6EACWyrIBZlA0N7X2Aev5D2BgAPEIB3wgb2CGRqRKOsRwmMsKAR/QsQHecPj/GS9fFAA+a/DrPxoD9JkvQWyEVngKcQ5IBRkw2uJbJRwOFoCeAIZwT+ic2DjwnyzYZP2/3t+iP3OMCETpmAkQxHpakOexACiPzGYGEi0wfVxb9wTD4NHX9iccAbuPjSP7/6Ep4QOwiPCDUIn4c4UQZH4pyzDQSfUD1TUIvXHWuCWUNMF98O9oDpUxnVwfWCPO8M4TNwHRnaBLEuRt7Qq9J+0/zaDH+6Gwo/sSEbJI8i+ZOufR6raqroMq0hr/WN95LmmDtebNdzzc3zWD9XnwXPoz57YIuwAdg47hV3AjmKNgI6dwJqwNuyYFA+vriey1TUULUaWTxbUEfwj3tCdlVYyz7HWsdfxi7wvnz9d+o4GrFzRDLEgPSOfzoRfBD6dLeQ6jKI7OTo5AyD9vshfX2+iZd8NRKftO7fgDwC8TgwODh75zoWcAGCfG3z8B3/nrBnw06EMwPnDXIm4QM7h0gMBviXU4JOmB4yAGbCG83ECrsAT+IIAEAIiQRxIApNh9hlwnYvBNDALzAcloAwsB6vBerAJbAU7wR6wHzSCo+AUOAsugSvgBrgHV083eAH6wTvwGUEQEkJFaIgeYoxYIHaIE8JAvJEAJAyJQZKQFCQdESISZBayAClDypH1yBakBtmHHEZOIReQDuQO0oX0Iq+RTyiGqqBaqCFqiY5GGSgTDUXj0EloOjoVLUSL0aXoWrQa3Y02oKfQS+gNtBN9gQ5gAFPGdDATzB5jYCwsEkvG0jAxNgcrxSqwaqwOa4b3+RrWifVhH3EiTsPpuD1cwcF4PM7Fp+Jz8CX4enwn3oC34tfwLrwf/0agEgwIdgQPApswnpBOmEYoIVQQthMOEc7AZ6mb8I5IJOoQrYhu8FlMImYSZxKXEDcQ64kniR3Ex8QBEomkR7IjeZEiSRxSPqmEtI60m3SCdJXUTfqgpKxkrOSkFKiUrCRUKlKqUNqldFzpqtIzpc9kdbIF2YMcSeaRZ5CXkbeRm8mXyd3kzxQNihXFixJHyaTMp6yl1FHOUO5T3igrK5squytHKwuU5ymvVd6rfF65S/mjiqaKrQpLZaKKRGWpyg6Vkyp3VN5QqVRLqi81mZpPXUqtoZ6mPqR+UKWpOqiyVXmqc1UrVRtUr6q+VCOrWagx1SarFapVqB1Qu6zWp05Wt1RnqXPU56hXqh9Wv6U+oEHTGKMRqZGjsURjl8YFjR5NkqalZoAmT7NYc6vmac3HNIxmRmPRuLQFtG20M7RuLaKWlRZbK1OrTGuPVrtWv7amtrN2gvZ07UrtY9qdOpiOpQ5bJ1tnmc5+nZs6n0YYjmCO4I9YPKJuxNUR77VH6vrq8nVLdet1b+h+0qPrBehl6a3Qa9R7oI/r2+pH60/T36h/Rr9vpNZIz5HckaUj94+8a4Aa2BrEGMw02GrQZjBgaGQYZCgyXGd42rDPSMfI1yjTaJXRcaNeY5qxt7HAeJXxCePndG06k55NX0tvpfebGJgEm0hMtpi0m3w2tTKNNy0yrTd9YEYxY5ilma0yazHrNzc2DzefZV5rfteCbMGwyLBYY3HO4r2llWWi5ULLRsseK10rtlWhVa3VfWuqtY/1VOtq6+s2RBuGTZbNBpsrtqiti22GbaXtZTvUztVOYLfBrmMUYZT7KOGo6lG37FXsmfYF9rX2XQ46DmEORQ6NDi9Hm49OHr1i9LnR3xxdHLMdtzneG6M5JmRM0ZjmMa+dbJ24TpVO18dSxwaOnTu2aewrZztnvvNG59suNJdwl4UuLS5fXd1cxa51rr1u5m4pblVutxhajCjGEsZ5d4K7n/tc96PuHz1cPfI99nv85WnvmeW5y7NnnNU4/rht4x57mXpxvLZ4dXrTvVO8N3t3+pj4cHyqfR75mvnyfLf7PmPaMDOZu5kv/Rz9xH6H/N6zPFizWSf9Mf8g/1L/9gDNgPiA9QEPA00D0wNrA/uDXIJmBp0MJgSHBq8IvsU2ZHPZNez+ELeQ2SGtoSqhsaHrQx+F2YaJw5rD0fCQ8JXh9yMsIoQRjZEgkh25MvJBlFXU1Kgj0cToqOjK6KcxY2JmxZyLpcVOid0V+y7OL25Z3L1463hJfEuCWsLEhJqE94n+ieWJneNHj589/lKSfpIgqSmZlJyQvD15YELAhNUTuie6TCyZeHOS1aTpky5M1p+cPfnYFLUpnCkHUggpiSm7Ur5wIjnVnIFUdmpVaj+XxV3DfcHz5a3i9fK9+OX8Z2leaeVpPele6SvTezN8Mioy+gQswXrBq8zgzE2Z77Mis3ZkDWYnZtfnKOWk5BwWagqzhK25RrnTcztEdqISUedUj6mrp/aLQ8Xb85C8SXlN+VrwR75NYi35RdJV4F1QWfBhWsK0A9M1pgunt82wnbF4xrPCwMLfZuIzuTNbZpnMmj+razZz9pY5yJzUOS1zzeYWz+2eFzRv53zK/Kz5vxc5FpUXvV2QuKC52LB4XvHjX4J+qS1RLRGX3FrouXDTInyRYFH74rGL1y3+VsorvVjmWFZR9mUJd8nFX8f8uvbXwaVpS9uXuS7buJy4XLj85gqfFTvLNcoLyx+vDF/ZsIq+qnTV29VTVl+ocK7YtIayRrKmc23Y2qZ15uuWr/uyPmP9jUq/yvoqg6rFVe838DZc3ei7sW6T4aayTZ82Czbf3hK0paHasrpiK3Frwdan2xK2nfuN8VvNdv3tZdu/7hDu6NwZs7O1xq2mZpfBrmW1aK2ktnf3xN1X9vjvaaqzr9tSr1Nfthfslex9vi9l3839oftbDjAO1B20OFh1iHaotAFpmNHQ35jR2NmU1NRxOORwS7Nn86EjDkd2HDU5WnlM+9iy45TjxccHTxSeGDgpOtl3Kv3U45YpLfdOjz99vTW6tf1M6JnzZwPPnj7HPHfivNf5oxc8Lhy+yLjYeMn1UkObS9uh311+P9Tu2t5w2e1y0xX3K80d4zqOX/W5euqa/7Wz19nXL92IuNFxM/7m7VsTb3Xe5t3uuZN959Xdgruf7827T7hf+kD9QcVDg4fVf9j8Ud/p2nmsy7+r7VHso3uPuY9fPMl78qW7+Cn1acUz42c1PU49R3sDe688n/C8+4Xoxee+kj81/qx6af3y4F++f7X1j+/vfiV+Nfh6yRu9NzveOr9tGYgaePgu593n96Uf9D7s/Mj4eO5T4qdnn6d9IX1Z+9Xma/O30G/3B3MGB0UcMUf2K4DBhqalAfB6BwDUJABocH9GmSDf/8kMke9ZZQj8JyzfI8rMFYA6+P8e3Qf/bm4BsHcb3H5BfbWJAERRAYhzB+jYscNtaK8m21dKjQj3AZujv6bmpIJ/Y/I95w95/3wGUlVn8PP5XwFtfFZdDkCYAAAAlmVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAhKACAAQAAAABAAAAIqADAAQAAAABAAAAIQAAAABBU0NJSQAAAFNjcmVlbnNob3Qpdkf5AAAACXBIWXMAABYlAAAWJQFJUiTwAAAC12lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MTk0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjE4NjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjE0NDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+MTQ0PC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4Kmj5GzQAAACtJREFUWAnt0DEBAAAAwqD1T20ND4hAYcCAAQMGDBgwYMCAAQMGDBgwcDEwEakAAc3C1KAAAAAASUVORK5CYII=', height: 33, width: 33, excavate: true }} - style={{ - margin: '0 auto', - width: 105, - height: 105 - }} + className="relay-mx-auto relay-w-[105px] relay-h-[105px]" /> - - +
+
@@ -243,14 +191,10 @@ export const WaitingForDepositStep: FC = ({ diff --git a/packages/ui/src/components/common/UnverifiedTokenModal.tsx b/packages/ui/src/components/common/UnverifiedTokenModal.tsx index be6af4ecc..24aebc32d 100644 --- a/packages/ui/src/components/common/UnverifiedTokenModal.tsx +++ b/packages/ui/src/components/common/UnverifiedTokenModal.tsx @@ -15,6 +15,7 @@ import { setRelayUiKitData } from '../../utils/localStorage.js' import { EventNames } from '../../constants/events.js' +import { cn } from '../../utils/cn.js' type UnverifiedTokenModalProps = { open: boolean @@ -51,68 +52,48 @@ export const UnverifiedTokenModal: FC = ({ onCloseButtonClicked={() => { onDecline?.(data?.token, data?.context) }} - css={{ - overflow: 'hidden', - zIndex: 10000001 - }} + className="relay-overflow-hidden relay-z-[10000001]" overlayZIndex={10000001} > Unverified Token - + {isValidTokenLogo ? ( {data?.token?.name} ) : null} - + - - This token isn’t traded on leading U.S. centralized exchanges or + + This token isn't traded on leading U.S. centralized exchanges or frequently swapped on major DEXes. Always conduct your own research before trading. {data?.token?.address} @@ -122,26 +103,21 @@ export const UnverifiedTokenModal: FC = ({ - + - + @@ -168,7 +144,7 @@ export const UnverifiedTokenModal: FC = ({ onAcceptToken(data?.token, data?.context) }} color="warning" - css={{ flex: 1, justifyContent: 'center', px: '16px' }} + className="relay-flex-1 relay-justify-center relay-px-4" > I Understand diff --git a/packages/ui/src/components/primitives/AccessibleList.tsx b/packages/ui/src/components/primitives/AccessibleList.tsx index 5a753bce6..90886f854 100644 --- a/packages/ui/src/components/primitives/AccessibleList.tsx +++ b/packages/ui/src/components/primitives/AccessibleList.tsx @@ -1,49 +1,24 @@ import React, { forwardRef, type FC } from 'react' import * as ToggleGroup from '@radix-ui/react-toggle-group' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' - -const AccessibleListContainerCss = cva({ - base: { - display: 'flex', - flexDirection: 'column' - } -}) - -const AccessibleListItemCss = cva({ - base: { - display: 'flex', - alignItems: 'center', - position: 'relative', - userSelect: 'none', - cursor: 'pointer', - '&[data-state=on]': {} - } -}) +import { cn } from '../../utils/cn.js' type AccessibleListProps = { children: React.ReactNode onSelect: (value: string) => void - css?: Styles + className?: string } export const AccessibleList: FC = ({ children, onSelect, - css + className }) => { return ( {children} @@ -53,18 +28,21 @@ export const AccessibleList: FC = ({ type AccessibleListItemProps = { children: React.ReactNode value: string - css?: Styles + className?: string asChild?: boolean } export const AccessibleListItem = forwardRef< HTMLButtonElement, AccessibleListItemProps ->(({ children, value, css, asChild, ...props }, forwardedRef) => { +>(({ children, value, className, asChild, ...props }, forwardedRef) => { return ( ['0'] +type AnchorVariantProps = VariantProps const Anchor: FC< - AnchorHTMLAttributes & AnchorCssProps & { css?: Styles } -> = ({ css, weight, color, ...props }) => { + AnchorHTMLAttributes & AnchorVariantProps & { className?: string } +> = ({ className, weight, color, ...props }) => { return ( + className={cn(anchorVariants({ weight, color }), className)} + /> ) } export const AnchorButton: FC< - ButtonHTMLAttributes & AnchorCssProps & { css?: Styles } -> = ({ css, weight, color, children, ...props }) => { + ButtonHTMLAttributes & AnchorVariantProps & { className?: string } +> = ({ className, weight, color, children, ...props }) => { return ( diff --git a/packages/ui/src/components/primitives/Box.tsx b/packages/ui/src/components/primitives/Box.tsx index b231b6003..9fb17d1fe 100644 --- a/packages/ui/src/components/primitives/Box.tsx +++ b/packages/ui/src/components/primitives/Box.tsx @@ -1,19 +1,11 @@ -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' import type { FC, PropsWithChildren } from 'react' - -const BoxCss = cva({}) - -type BoxCssProps = Parameters['0'] +import { cn } from '../../utils/cn.js' const Box: FC< - { css?: Styles; id?: string } & BoxCssProps & PropsWithChildren -> = ({ css, children, id, ...props }) => { + { className?: string; id?: string } & PropsWithChildren +> = ({ className, children, id }) => { return ( -
+
{children}
) diff --git a/packages/ui/src/components/primitives/Button.tsx b/packages/ui/src/components/primitives/Button.tsx index eda70643d..cfe1761f3 100644 --- a/packages/ui/src/components/primitives/Button.tsx +++ b/packages/ui/src/components/primitives/Button.tsx @@ -1,163 +1,99 @@ -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' +import { cva, type VariantProps } from 'class-variance-authority' import { forwardRef, type ButtonHTMLAttributes } from 'react' +import { cn } from '../../utils/cn.js' -const ButtonCss = cva({ - base: { - cursor: 'pointer', - outline: 'none', - fontFamily: 'body', - fontWeight: 700, - fontSize: 16, - transition: 'background-color 250ms linear', - gap: '2', - display: 'inline-flex', - alignItems: 'center', - lineHeight: '20px', - '--focusColor': 'colors.focus-color', - _focusVisible: { - boxShadow: '0 0 0 2px var(--focusColor)' - }, - _disabled: { - cursor: 'not-allowed', - backgroundColor: 'button-disabled-background', - color: 'button-disabled-color', - _hover: { - backgroundColor: 'button-disabled-background', - color: 'button-disabled-color' - } - } - }, - variants: { - color: { - primary: { - backgroundColor: 'primary-button-background', - color: 'primary-button-color', - '&:hover': { - backgroundColor: 'primary-button-hover-background', - color: 'primary-button-hover-color' - } - }, - secondary: { - backgroundColor: 'primary4', - color: 'primary12', - '&:hover': { - backgroundColor: 'secondary-button-hover-background', - color: 'secondary-button-hover-color' - } - }, - ghost: { - color: 'text-default', - backgroundColor: 'transparent' - }, - white: { - '--borderColor': 'colors.subtle-border-color', - backgroundColor: 'widget-background', - transition: 'filter 250ms linear', - '&:hover': { - filter: 'brightness(97%)' - }, - border: '1px solid var(--borderColor)' - }, - error: { - backgroundColor: 'red9', - color: 'white', - '&:hover': { - backgroundColor: 'red10' - } - }, - warning: { - backgroundColor: 'amber3', - color: 'amber11', - '&:hover': { - backgroundColor: 'amber4', - color: 'amber11' - } - }, - grey: { - backgroundColor: 'gray3', - color: 'gray11', - '&:hover': { - backgroundColor: 'gray4' - } - } - }, - corners: { - square: { - borderRadius: 0 +const buttonVariants = cva( + [ + 'relay-cursor-pointer relay-outline-none relay-font-body relay-font-bold relay-text-[16px]', + 'relay-transition-[background-color] relay-duration-[250ms] relay-ease-linear', + 'relay-gap-2 relay-inline-flex relay-items-center relay-leading-[20px]', + 'focus-visible:relay-shadow-[0_0_0_2px_var(--relay-colors-focus-color)]', + 'disabled:relay-cursor-not-allowed disabled:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:relay-text-[color:var(--relay-colors-button-disabled-color)]', + 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]' + ].join(' '), + { + variants: { + color: { + primary: [ + 'relay-bg-[var(--relay-colors-primary-button-background)]', + 'relay-text-[color:var(--relay-colors-primary-button-color)]', + 'hover:relay-bg-[var(--relay-colors-primary-button-hover-background)]', + 'hover:relay-text-[color:var(--relay-colors-primary-button-hover-color)]' + ].join(' '), + secondary: [ + 'relay-bg-[var(--relay-colors-primary4)]', + 'relay-text-[color:var(--relay-colors-primary12)]', + 'hover:relay-bg-[var(--relay-colors-secondary-button-hover-background)]', + 'hover:relay-text-[color:var(--relay-colors-secondary-button-hover-color)]' + ].join(' '), + ghost: 'relay-text-[color:var(--relay-colors-text-default)] relay-bg-transparent', + white: [ + 'relay-bg-[var(--relay-colors-widget-background)]', + 'relay-transition-[filter] relay-duration-[250ms] relay-ease-linear', + 'hover:relay-brightness-[0.97]', + 'relay-border relay-border-solid relay-border-[var(--relay-colors-subtle-border-color)]' + ].join(' '), + error: [ + 'relay-bg-[var(--relay-colors-red9)] relay-text-white', + 'hover:relay-bg-[var(--relay-colors-red10)]' + ].join(' '), + warning: [ + 'relay-bg-[var(--relay-colors-amber3)]', + 'relay-text-[color:var(--relay-colors-amber11)]', + 'hover:relay-bg-[var(--relay-colors-amber4)]', + 'hover:relay-text-[color:var(--relay-colors-amber11)]' + ].join(' '), + grey: [ + 'relay-bg-[var(--relay-colors-gray3)]', + 'relay-text-[color:var(--relay-colors-gray11)]', + 'hover:relay-bg-[var(--relay-colors-gray4)]' + ].join(' ') }, - rounded: { - borderRadius: 12 + corners: { + square: 'relay-rounded-none', + rounded: 'relay-rounded-[12px]', + pill: 'relay-rounded-full', + circle: 'relay-rounded-full relay-items-center relay-justify-center' }, - pill: { - borderRadius: 99999 + size: { + none: '', + xs: 'relay-p-3 relay-leading-[16px] relay-min-h-[40px]', + small: 'relay-px-3 relay-py-2 relay-leading-[12px] relay-min-h-[40px]', + medium: 'relay-px-5 relay-py-3 relay-min-h-[44px]', + large: 'relay-px-5 relay-py-4 relay-min-h-[52px]' }, - circle: { - borderRadius: '99999px', - alignItems: 'center', - justifyContent: 'center' + cta: { + true: [ + '[&:not(:disabled)]:relay-font-heading', + '[&:not(:disabled)]:relay-font-bold', + '[&:not(:disabled)]:relay-uppercase', + '[&:not(:disabled)]:relay-italic' + ].join(' ') } }, - size: { - none: {}, - xs: { - p: '3', - lineHeight: '16px', - minHeight: 40 - }, - small: { - px: '3', - py: '2', - lineHeight: '12px', - minHeight: 40 - }, - medium: { - px: '5', - py: '3', - minHeight: 44 - }, - large: { - px: '5', - py: '4', - minHeight: 52 - } - }, - cta: { - true: { - '&:not(:disabled)': { - fontFamily: 'heading', - fontWeight: 700, - textTransform: 'uppercase', - fontStyle: 'var(--relay-fonts-button-cta-font-style, italic)' - } - } + defaultVariants: { + color: 'primary', + corners: 'rounded', + size: 'medium' } - }, - defaultVariants: { - color: 'primary', - corners: 'rounded', - size: 'medium' } -}) +) -type ButtonCssProps = Parameters['0'] +export type ButtonVariantProps = VariantProps const Button = forwardRef< HTMLButtonElement, - Omit, 'ref'> & - ButtonCssProps & { css?: Styles } ->(({ css, children, ...props }, forwardedRef) => { + Omit, 'ref' | 'color'> & + ButtonVariantProps & { className?: string } +>(({ className, children, ...props }, forwardedRef) => { const { color, size, corners, cta, ...buttonProps } = { ...props } return (
) : null } diff --git a/packages/ui/src/components/primitives/ChainTokenIcon.tsx b/packages/ui/src/components/primitives/ChainTokenIcon.tsx index 71cf551b6..0ce153e18 100644 --- a/packages/ui/src/components/primitives/ChainTokenIcon.tsx +++ b/packages/ui/src/components/primitives/ChainTokenIcon.tsx @@ -1,9 +1,7 @@ import { type FC } from 'react' -import Flex from './Flex.js' import ChainIcon from './ChainIcon.js' -import Box from './Box.js' import Text from './Text.js' -import type { Styles } from '@relayprotocol/relay-design-system/css' +import { cn } from '../../utils/cn.js' type Size = 'sm' | 'base' | 'md' | 'lg' @@ -11,7 +9,7 @@ type ChainTokenProps = { chainId?: number tokenlogoURI?: string tokenSymbol?: string - css?: Styles + className?: string size?: Size chainRadius?: number chainIconSize?: number @@ -40,7 +38,7 @@ export const ChainTokenIcon: FC = ({ chainId, tokenlogoURI, tokenSymbol, - css = {}, + className, size = 'md', chainRadius = 4, chainIconSize @@ -50,15 +48,15 @@ export const ChainTokenIcon: FC = ({ const chainSize = chainIconSize ?? dimensions.chain return chainId ? ( - {isValidTokenLogo ? ( @@ -73,36 +71,23 @@ export const ChainTokenIcon: FC = ({ }} /> ) : tokenSymbol ? ( - {tokenSymbol?.charAt(0).toUpperCase()} - +
) : null} - +
) : null } diff --git a/packages/ui/src/components/primitives/Collapsible.tsx b/packages/ui/src/components/primitives/Collapsible.tsx index a7d5f78f4..4ca9250b5 100644 --- a/packages/ui/src/components/primitives/Collapsible.tsx +++ b/packages/ui/src/components/primitives/Collapsible.tsx @@ -1,54 +1,27 @@ import * as Collapsible from '@radix-ui/react-collapsible' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' import { forwardRef, type ComponentPropsWithoutRef, type ElementRef, type PropsWithChildren } from 'react' - -const CollapsibleContentCss = cva({ - base: { - overflow: 'hidden', - _data_state_open: { - animation: `collapsibleSlideDown 300ms cubic-bezier(0.87, 0, 0.13, 1)` - }, - _data_state_closed: { - animation: `collapsibleSlideUp 300ms cubic-bezier(0.87, 0, 0.13, 1)` - } - } -}) - -const CollapsibleRootCss = cva({ - base: { - width: '100%' - } -}) - -const CollapsibleTriggerCss = cva({ - base: { - width: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - cursor: 'pointer' - } -}) +import { cn } from '../../utils/cn.js' const CollapsibleContent = forwardRef< ElementRef, ComponentPropsWithoutRef & - PropsWithChildren & { css?: Styles } ->(({ children, css, ...props }, forwardedRef) => { + PropsWithChildren & { className?: string } +>(({ children, className, ...props }, forwardedRef) => { return ( {children} @@ -58,13 +31,13 @@ const CollapsibleContent = forwardRef< const CollapsibleRoot = forwardRef< ElementRef, ComponentPropsWithoutRef & - PropsWithChildren & { css?: Styles } ->(({ children, css, ...props }, forwardedRef) => { + PropsWithChildren & { className?: string } +>(({ children, className, ...props }, forwardedRef) => { return ( {children} @@ -74,13 +47,16 @@ const CollapsibleRoot = forwardRef< const CollapsibleTrigger = forwardRef< ElementRef, ComponentPropsWithoutRef & - PropsWithChildren & { css?: Styles } ->(({ children, css, ...props }, forwardedRef) => { + PropsWithChildren & { className?: string } +>(({ children, className, ...props }, forwardedRef) => { return ( {children} diff --git a/packages/ui/src/components/primitives/Dialog.tsx b/packages/ui/src/components/primitives/Dialog.tsx index bfeb28685..2d24ae376 100644 --- a/packages/ui/src/components/primitives/Dialog.tsx +++ b/packages/ui/src/components/primitives/Dialog.tsx @@ -1,5 +1,4 @@ import * as DialogPrimitive from '@radix-ui/react-dialog' -import { motion, AnimatePresence } from 'framer-motion' import type { ComponentPropsWithoutRef, ElementRef, @@ -9,63 +8,35 @@ import type { } from 'react' import { forwardRef, useState } from 'react' import { useMediaQuery } from 'usehooks-ts' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' +import { cn } from '../../utils/cn.js' import { VisuallyHidden } from '@radix-ui/react-visually-hidden' -const OverlayStyle = cva({ - base: { - position: 'fixed', - inset: 0 - } -}) - const Overlay = forwardRef< ElementRef, ComponentPropsWithoutRef & - PropsWithChildren & { css?: Styles } ->(({ children, css, ...props }, forwardedRef) => { + PropsWithChildren & { className?: string } +>(({ children, className, ...props }, forwardedRef) => { return ( {children} ) }) -const ContentCss = cva({ - base: { - backgroundColor: 'modal-background', - borderRadius: 'modal-border-radius', - border: 'modal-border', - position: 'fixed', - left: '50%', - top: '100%', - minWidth: '90vw', - maxWidth: '100vw', - sm: { - minWidth: '400px', - maxWidth: '532px' - }, - maxHeight: '85vh', - overflowY: 'auto', - _focus: { outline: 'none' }, - '@media(max-width: 520px)': { - borderBottomRightRadius: 0, - borderBottomLeftRadius: 0, - width: '100%' - } - } -}) +const contentBase = [ + 'relay-bg-[var(--relay-colors-modal-background)]', + 'relay-rounded-modal', + 'relay-border-modal', + 'relay-fixed relay-left-1/2', + 'relay-min-w-[90vw] relay-max-w-[100vw]', + 'sm:relay-min-w-[400px] sm:relay-max-w-[532px]', + 'relay-max-h-[85vh] relay-overflow-y-auto', + 'focus:relay-outline-none' +].join(' ') const Content = forwardRef< ElementRef, @@ -76,7 +47,7 @@ const Content = forwardRef< {children} @@ -86,71 +57,39 @@ const Content = forwardRef< const AnimatedContent = forwardRef< ElementRef, ComponentPropsWithoutRef & - PropsWithChildren & { css?: Styles; disableAnimation?: boolean } ->(({ children, css, disableAnimation = false, ...props }, forwardedRef) => { + PropsWithChildren & { className?: string; disableAnimation?: boolean } +>(({ children, className, disableAnimation = false, ...props }, forwardedRef) => { const isMobile = useMediaQuery('(max-width: 520px)') - const isMobileSlideUpEnabled = isMobile && !disableAnimation + const isMobileSlideUp = isMobile && !disableAnimation + + const mobileClasses = [ + 'relay-bottom-0 relay-top-auto relay-left-0', + 'relay-translate-x-0', + 'relay-animate-dialog-slide-up', + 'data-[state=closed]:relay-animate-dialog-slide-down', + 'max-[520px]:relay-rounded-b-none max-[520px]:relay-w-full' + ].join(' ') + + const desktopClasses = [ + 'relay-top-1/2 -relay-translate-x-1/2 -relay-translate-y-1/2', + 'relay-animate-dialog-fade-in', + 'data-[state=closed]:relay-animate-dialog-fade-out' + ].join(' ') - const animation = isMobileSlideUpEnabled - ? { - initial: { - opacity: 0, - bottom: '-100%', - top: 'auto', - left: 0 - }, - animate: { - opacity: 1, - bottom: 0, - top: 'auto', - left: 0 - }, - exit: { - opacity: 0, - bottom: '-100%', - top: 'auto', - left: 0 - } - } - : { - initial: { - opacity: 0, - top: '50%', - transform: 'translateX(-50%) translateY(-50%)' - }, - animate: { - opacity: 1, - top: '50%', - transform: 'translateX(-50%) translateY(-50%)' - }, - exit: { - opacity: 0, - top: '50%', - transform: 'translateX(-50%) translateY(-50%)' - } - } return ( - - - Title - - {children} - + + Title + + {children} ) }) @@ -173,15 +112,13 @@ const Dialog = forwardRef< {trigger} - - {open ? ( - - - {children} - - - ) : null} - + {open ? ( + + + {children} + + + ) : null} ) }) diff --git a/packages/ui/src/components/primitives/Dropdown.tsx b/packages/ui/src/components/primitives/Dropdown.tsx index 4fbc5e8c4..4ae1085dd 100644 --- a/packages/ui/src/components/primitives/Dropdown.tsx +++ b/packages/ui/src/components/primitives/Dropdown.tsx @@ -6,72 +6,51 @@ import { useState } from 'react' import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' - -const DropdownMenuContentCss = cva({ - base: { - mx: '4', - p: '3', - borderRadius: 8, - zIndex: 10000002, - background: 'modal-background', - boxShadow: '0px 0px 50px 0px #0000001F', - border: 'dropdown-border' - } -}) +import { cn } from '../../utils/cn.js' const DropdownMenuContent = forwardRef< ElementRef, ComponentPropsWithoutRef & { - css?: Styles + className?: string } ->(({ children, css, ...props }, forwardedRef) => { +>(({ children, className, ...props }, forwardedRef) => { return ( {children} ) }) -const DropdownMenuItemCss = cva({ - base: { - display: 'flex', - alignItems: 'center', - fontSize: 16, - color: 'text-default', - background: 'modal-background', - p: '3', - outline: 'none', - cursor: 'pointer', - transition: 'background-color 150ms linear', - _hover: { - backgroundColor: 'gray/10' - }, - '&:focus': { - backgroundColor: 'gray/10' - } - } -}) - const DropdownMenuItem = forwardRef< ElementRef, ComponentPropsWithoutRef & { - css?: Styles + className?: string } ->(({ children, css, ...props }, forwardedRef) => { +>(({ children, className, ...props }, forwardedRef) => { return ( {children} diff --git a/packages/ui/src/components/primitives/Flex.tsx b/packages/ui/src/components/primitives/Flex.tsx index 9392d790e..1a08b52a7 100644 --- a/packages/ui/src/components/primitives/Flex.tsx +++ b/packages/ui/src/components/primitives/Flex.tsx @@ -1,90 +1,50 @@ -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' +import { cva, type VariantProps } from 'class-variance-authority' import type { FC, PropsWithChildren, CSSProperties, HTMLAttributes } from 'react' +import { cn } from '../../utils/cn.js' -export const FlexCss = cva({ - base: { - display: 'flex' - }, +export const flexVariants = cva('relay-flex', { variants: { align: { - start: { - alignItems: 'flex-start' - }, - center: { - alignItems: 'center' - }, - end: { - alignItems: 'flex-end' - }, - stretch: { - alignItems: 'stretch' - }, - baseline: { - alignItems: 'baseline' - }, - normal: { - alignItems: 'normal' - } + start: 'relay-items-start', + center: 'relay-items-center', + end: 'relay-items-end', + stretch: 'relay-items-stretch', + baseline: 'relay-items-baseline', + normal: 'relay-items-[normal]' }, justify: { - start: { - justifyContent: 'flex-start' - }, - center: { - justifyContent: 'center' - }, - end: { - justifyContent: 'flex-end' - }, - between: { - justifyContent: 'space-between' - } + start: 'relay-justify-start', + center: 'relay-justify-center', + end: 'relay-justify-end', + between: 'relay-justify-between' }, direction: { - row: { - flexDirection: 'row' - }, - column: { - flexDirection: 'column' - }, - rowReverse: { - flexDirection: 'row-reverse' - }, - columnReverse: { - flexDirection: 'column-reverse' - } + row: 'relay-flex-row', + column: 'relay-flex-col', + rowReverse: 'relay-flex-row-reverse', + columnReverse: 'relay-flex-col-reverse' }, wrap: { - noWrap: { - flexWrap: 'nowrap' - }, - wrap: { - flexWrap: 'wrap' - }, - wrapReverse: { - flexWrap: 'wrap-reverse' - } + noWrap: 'relay-flex-nowrap', + wrap: 'relay-flex-wrap', + wrapReverse: 'relay-flex-wrap-reverse' } } }) -type FlexCssProps = Parameters['0'] +export type FlexVariantProps = VariantProps const Flex: FC< - { css?: Styles; style?: CSSProperties; id?: string } & FlexCssProps & + { className?: string; style?: CSSProperties; id?: string } & FlexVariantProps & PropsWithChildren & Omit, 'className'> > = ({ - css, + className, style, children, id, @@ -96,10 +56,7 @@ const Flex: FC< }) => { return (
['0'] -> +type InputVariants = VariantProps const Input = forwardRef< HTMLInputElement, @@ -60,20 +33,20 @@ const Input = forwardRef< PropsWithChildren & { icon?: ReactNode iconPosition?: 'left' | 'right' - iconCss?: Styles - containerCss?: Styles + iconClassName?: string + containerClassName?: string inputStyle?: React.CSSProperties - } & { css?: Styles } & StyledInputCssVariants + } & { className?: string } & InputVariants >( ( { children, icon, iconPosition, - iconCss, - containerCss, + iconClassName, + containerClassName, inputStyle, - css, + className, ...props }, ref @@ -81,27 +54,21 @@ const Input = forwardRef< const { size, ellipsify, style, ...inputProps } = props return ( - +
{icon && ( - - +
{icon} - - +
+
)} -
+
) } ) diff --git a/packages/ui/src/components/primitives/Pill.tsx b/packages/ui/src/components/primitives/Pill.tsx index a82e86682..5e2502d7b 100644 --- a/packages/ui/src/components/primitives/Pill.tsx +++ b/packages/ui/src/components/primitives/Pill.tsx @@ -1,81 +1,44 @@ -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' +import { cva, type VariantProps } from 'class-variance-authority' import type { FC, HTMLAttributes } from 'react' -import type { FlexCss } from './Flex.js' +import { cn } from '../../utils/cn.js' -export const PillStyle = cva({ - base: { - display: 'flex', - background: 'subtle-background-color', - px: '3', - py: '1', - gap: '1' - }, - variants: { - color: { - red: { - background: 'red3', - color: 'red11' +export const pillVariants = cva( + 'relay-flex relay-bg-[var(--relay-colors-subtle-background-color)] relay-px-3 relay-py-1 relay-gap-1', + { + variants: { + color: { + red: 'relay-bg-[var(--relay-colors-red3)] relay-text-[color:var(--relay-colors-red11)]', + gray: 'relay-bg-[var(--relay-colors-gray2)] relay-text-[color:var(--relay-colors-gray8)]', + green: 'relay-bg-[var(--relay-colors-green3)] relay-text-[color:var(--relay-colors-green12)]', + amber: 'relay-bg-[var(--relay-colors-amber2)] relay-text-[color:var(--relay-colors-amber9)]', + transparent: 'relay-bg-transparent relay-text-[color:var(--relay-colors-gray12)]', + primary: 'relay-bg-[var(--relay-colors-primary3)] relay-text-[color:var(--relay-colors-primary12)]' }, - gray: { - background: 'gray2', - color: 'gray8' + radius: { + pill: 'relay-rounded-[25px]', + rounded: 'relay-rounded-[12px]', + squared: 'relay-rounded-[8px]' }, - green: { - background: 'green3', - color: 'green12' - }, - amber: { - background: 'amber2', - color: 'amber9' - }, - transparent: { - background: 'none', - color: 'gray12' - }, - primary: { - background: 'primary3', - color: 'primary12' - } - }, - radius: { - pill: { - borderRadius: 25 - }, - rounded: { - borderRadius: 12 - }, - squared: { - borderRadius: 8 + bordered: { + true: 'relay-border relay-border-solid relay-border-[var(--relay-colors-gray-6)]' } }, - bordered: { - true: { - '--borderColor': 'colors.gray.6', - border: '1px solid var(--borderColor)' - } + defaultVariants: { + radius: 'pill' } - }, - defaultVariants: { - radius: 'pill' } -}) +) -type PillProps = Parameters['0'] +type PillVariantProps = VariantProps export const Pill: FC< - HTMLAttributes & { - css?: Parameters<(typeof FlexCss)['raw']>[0] & Styles - } & PillProps -> = ({ css, ...props }) => { + HTMLAttributes & { className?: string } & PillVariantProps +> = ({ className, color, radius, bordered, ...props }) => { return (
+ className={cn(pillVariants({ color, radius, bordered }), className)} + /> ) } diff --git a/packages/ui/src/components/primitives/Skeleton.tsx b/packages/ui/src/components/primitives/Skeleton.tsx index da99a2bdc..7c24d924e 100644 --- a/packages/ui/src/components/primitives/Skeleton.tsx +++ b/packages/ui/src/components/primitives/Skeleton.tsx @@ -1,23 +1,17 @@ import type { FC } from 'react' -import Flex from './Flex.js' -import { type Styles } from '@relayprotocol/relay-design-system/css' +import { cn } from '../../utils/cn.js' type SkeletonProps = { - css?: Styles + className?: string } -const Skeleton: FC = ({ css }) => { +const Skeleton: FC = ({ className }) => { return ( - ) } diff --git a/packages/ui/src/components/primitives/SlippageButton.tsx b/packages/ui/src/components/primitives/SlippageButton.tsx index 5eb38618c..8e8ef73a8 100644 --- a/packages/ui/src/components/primitives/SlippageButton.tsx +++ b/packages/ui/src/components/primitives/SlippageButton.tsx @@ -31,19 +31,7 @@ export const SlippageButton: FC = ({ aria-label="Slippage Settings" size="none" color="ghost" - css={{ - display: 'flex', - borderRadius: '8px', - alignItems: 'center', - gap: '4px', - justifyContent: 'center', - p: '1', - _hover: { - backgroundColor: 'gray2' - }, - backgroundColor: 'gray3', - padding: '4px 6px' - }} + className="relay-flex relay-rounded-[8px] relay-items-center relay-gap-[4px] relay-justify-center relay-p-1 hover:relay-bg-[var(--relay-colors-gray2)] relay-bg-[var(--relay-colors-gray3)] relay-px-[6px] relay-py-[4px]" onClick={() => { onOpenSlippageConfig?.() }} diff --git a/packages/ui/src/components/primitives/Switch.tsx b/packages/ui/src/components/primitives/Switch.tsx index eb936f3ca..9a044fc64 100644 --- a/packages/ui/src/components/primitives/Switch.tsx +++ b/packages/ui/src/components/primitives/Switch.tsx @@ -1,79 +1,51 @@ import * as Switch from '@radix-ui/react-switch' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' import { type ElementRef, forwardRef, type ComponentPropsWithoutRef } from 'react' - -const StyledSwitchCss = cva({ - base: { - cursor: 'pointer', - width: '38px', - height: '20px', - backgroundColor: 'gray7', - borderRadius: '9999px', - position: 'relative', - WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)', - display: 'flex', - alignItems: 'center', - padding: '0 10px', - transition: 'background-color 250ms', - "&[data-state='checked']": { - backgroundColor: 'primary-button-background' - } - } -}) +import { cn } from '../../utils/cn.js' export const StyledSwitch = forwardRef< ElementRef, - ComponentPropsWithoutRef & { css?: Styles } ->(({ children, css, ...props }, forwardedRef) => { + ComponentPropsWithoutRef & { className?: string } +>(({ children, className, ...props }, forwardedRef) => { return ( {children} ) }) -const StyledThumbCss = cva({ - base: { - display: 'block', - width: '17.5px', - height: '17.5px', - backgroundColor: 'gray1', - borderRadius: '9999px', - zIndex: 1, - '--borderColor': 'colors.gray.8', - border: '1px solid var(--borderColor)', - transition: 'transform 100ms', - transform: 'translateX(0px)', - willChange: 'transform', - position: 'absolute', - left: '2px', - "&[data-state='checked']": { - transform: 'translateX(17px)' - } - } -}) - export const StyledThumb = forwardRef< ElementRef, - ComponentPropsWithoutRef & { css?: Styles } ->(({ children, css, ...props }, forwardedRef) => { + ComponentPropsWithoutRef & { className?: string } +>(({ children, className, ...props }, forwardedRef) => { return ( ) }) diff --git a/packages/ui/src/components/primitives/Tabs.tsx b/packages/ui/src/components/primitives/Tabs.tsx index 5f2147e3d..4bf74a004 100644 --- a/packages/ui/src/components/primitives/Tabs.tsx +++ b/packages/ui/src/components/primitives/Tabs.tsx @@ -1,58 +1,26 @@ import * as TabsPrimitive from '@radix-ui/react-tabs' -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from 'react' - -const TabsListCss = cva({ - base: { - display: 'flex', - alignItems: 'center', - borderRadius: 8, - p: '1', - backgroundColor: 'gray2', - border: 'none' - } -}) - -const TabsTriggerCss = cva({ - base: { - width: '100%', - fontWeight: '500', - fontSize: '14px', - cursor: 'pointer', - py: '2px', - color: 'gray12', - borderRadius: 8, - backgroundColor: 'transparent', - borderWidth: '1px', - borderStyle: 'solid', - borderColor: 'transparent', - '&[data-state="active"]': { - backgroundColor: 'subtle-background-color', - '--borderColor': 'colors.gray.5', - borderColor: 'var(--borderColor)' - } - } -}) +import { cn } from '../../utils/cn.js' const TabsList = forwardRef< ElementRef, ComponentPropsWithoutRef & { - css?: Styles + className?: string } ->(({ children, css, ...props }, forwardedRef) => { +>(({ children, className, ...props }, forwardedRef) => { return ( {children} @@ -62,14 +30,22 @@ const TabsList = forwardRef< const TabsTrigger = forwardRef< ElementRef, ComponentPropsWithoutRef & { - css?: Styles + className?: string } ->(({ children, css, ...props }, forwardedRef) => { +>(({ children, className, ...props }, forwardedRef) => { return ( {children} @@ -79,14 +55,14 @@ const TabsTrigger = forwardRef< const TabsContent = forwardRef< ElementRef, ComponentPropsWithoutRef & { - css?: Styles + className?: string } ->(({ children, css, ...props }, forwardedRef) => { +>(({ children, className, ...props }, forwardedRef) => { return ( {children} @@ -96,14 +72,14 @@ const TabsContent = forwardRef< const TabsRoot = forwardRef< ElementRef, ComponentPropsWithoutRef & { - css?: Styles + className?: string } ->(({ children, css, ...props }, forwardedRef) => { +>(({ children, className, ...props }, forwardedRef) => { return ( {children} diff --git a/packages/ui/src/components/primitives/Text.tsx b/packages/ui/src/components/primitives/Text.tsx index f11998c25..1c546f285 100644 --- a/packages/ui/src/components/primitives/Text.tsx +++ b/packages/ui/src/components/primitives/Text.tsx @@ -1,122 +1,57 @@ -import { - cva, - css as designCss, - type Styles -} from '@relayprotocol/relay-design-system/css' +import { cva, type VariantProps } from 'class-variance-authority' import type { FC, PropsWithChildren } from 'react' +import { cn } from '../../utils/cn.js' -const TextCss = cva({ - base: { - color: 'text-default', - fontFamily: 'body' - }, - variants: { - style: { - h1: { - fontWeight: 800, - fontSize: '64px', - fontFamily: 'heading' - }, - h2: { - fontWeight: 700, - fontSize: '48px' - }, - h3: { - fontWeight: 700, - fontSize: '32px' - }, - h4: { - fontWeight: 700, - fontSize: '24px' - }, - h5: { - fontWeight: 700, - fontSize: '20px' - }, - h6: { - fontWeight: 700, - fontSize: '16px' - }, - subtitle1: { - fontWeight: 500, - fontSize: '16px' - }, - subtitle2: { - fontWeight: 500, - fontSize: '14px' - }, - subtitle3: { - fontWeight: 500, - fontSize: '12px' - }, - body1: { - fontWeight: 400, - fontSize: '16px' - }, - body2: { - fontWeight: 400, - fontSize: '14px' - }, - body3: { - fontWeight: 400, - fontSize: '12px' - }, - tiny: { - fontWeight: 500, - fontSize: 10, - color: 'gray11' - } - }, - color: { - subtle: { - color: 'text-subtle' - }, - subtleSecondary: { - color: 'text-subtle-secondary' - }, - error: { - color: 'text-error' - }, - red: { - color: 'red11' - }, - blue: { - color: 'blue12' - }, - success: { - color: 'text-success' - }, - warning: { - color: 'amber12' - }, - warningSecondary: { - color: 'amber11' - } - }, - italic: { - true: { - fontStyle: 'italic' - } - }, - ellipsify: { - true: { - textOverflow: 'ellipsis', - overflow: 'hidden', - whiteSpace: 'nowrap' +const textVariants = cva( + 'relay-text-[color:var(--relay-colors-text-default)] relay-font-body', + { + variants: { + style: { + h1: 'relay-font-[800] relay-text-[64px] relay-font-heading', + h2: 'relay-font-bold relay-text-[48px]', + h3: 'relay-font-bold relay-text-[32px]', + h4: 'relay-font-bold relay-text-[24px]', + h5: 'relay-font-bold relay-text-[20px]', + h6: 'relay-font-bold relay-text-[16px]', + subtitle1: 'relay-font-medium relay-text-[16px]', + subtitle2: 'relay-font-medium relay-text-[14px]', + subtitle3: 'relay-font-medium relay-text-[12px]', + body1: 'relay-font-normal relay-text-[16px]', + body2: 'relay-font-normal relay-text-[14px]', + body3: 'relay-font-normal relay-text-[12px]', + tiny: + 'relay-font-medium relay-text-[10px] relay-text-[color:var(--relay-colors-gray11)]' + }, + color: { + subtle: 'relay-text-[color:var(--relay-colors-text-subtle)]', + subtleSecondary: + 'relay-text-[color:var(--relay-colors-text-subtle-secondary)]', + error: 'relay-text-[color:var(--relay-colors-text-error)]', + red: 'relay-text-[color:var(--relay-colors-red11)]', + blue: 'relay-text-[color:var(--relay-colors-blue12)]', + success: 'relay-text-[color:var(--relay-colors-text-success)]', + warning: 'relay-text-[color:var(--relay-colors-amber12)]', + warningSecondary: 'relay-text-[color:var(--relay-colors-amber11)]' + }, + italic: { + true: 'relay-italic' + }, + ellipsify: { + true: 'relay-text-ellipsis relay-overflow-hidden relay-whitespace-nowrap' } } } -}) +) -type TextCssProps = Parameters['0'] +export type TextVariantProps = VariantProps -const Text: FC<{ css?: Styles } & TextCssProps & PropsWithChildren> = ({ - css, +const Text: FC<{ className?: string } & TextVariantProps & PropsWithChildren> = ({ + className, children, ...props }) => { return ( -
+
{children}
) diff --git a/packages/ui/src/components/primitives/Tooltip.tsx b/packages/ui/src/components/primitives/Tooltip.tsx index c0499314e..1b0ddc603 100644 --- a/packages/ui/src/components/primitives/Tooltip.tsx +++ b/packages/ui/src/components/primitives/Tooltip.tsx @@ -1,20 +1,7 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip' import * as Popover from '@radix-ui/react-popover' import { useMediaQuery } from 'usehooks-ts' -import Box from './Box.js' -import { cva } from '@relayprotocol/relay-design-system/css' - -const TooltipArrowStyle = cva({ - base: { - fill: 'modal-background' - } -}) - -const PopoverArrow = cva({ - base: { - fill: 'modal-background' - } -}) +import { cn } from '../../utils/cn.js' const Tooltip = ({ children, @@ -42,24 +29,14 @@ const Tooltip = ({ style={{ zIndex: 10000003, outline: 'none', maxWidth: '100vw' }} {...props} > - - +
- +
{content} - - +
+
) @@ -82,24 +59,14 @@ const Tooltip = ({ style={{ zIndex: 10000003 }} {...props} > -
- +
- +
{content} - - +
+
diff --git a/packages/ui/src/components/widgets/FeeBreakdown.tsx b/packages/ui/src/components/widgets/FeeBreakdown.tsx index 78a95b8f0..7177b4349 100644 --- a/packages/ui/src/components/widgets/FeeBreakdown.tsx +++ b/packages/ui/src/components/widgets/FeeBreakdown.tsx @@ -17,6 +17,7 @@ import { PriceImpactTooltip } from './PriceImpactTooltip.js' import { getSlippageRating, ratingToColor } from '../../utils/slippage.js' import Tooltip from '../primitives/Tooltip.js' import React from 'react' +import { cn } from '../../utils/cn.js' type Props = Pick< ChildrenProps, @@ -102,12 +103,12 @@ const FeeBreakdown: FC = ({ value: ( @@ -118,11 +119,11 @@ const FeeBreakdown: FC = ({ { title: 'Network cost', value: ( - + {originGasFeeFormatted} @@ -137,12 +138,16 @@ const FeeBreakdown: FC = ({ > {
- + {feeBreakdown?.totalFees?.priceImpactPercentage} @@ -150,10 +155,7 @@ const FeeBreakdown: FC = ({ icon={faInfoCircle} width={14} height={14} - style={{ - display: 'inline-block', - marginLeft: 4 - }} + className="relay-inline-block relay-ml-[4px]" />
@@ -168,25 +170,11 @@ const FeeBreakdown: FC = ({ return ( - +
+ +
) } else { @@ -196,39 +184,27 @@ const FeeBreakdown: FC = ({ return ( - + Max Slippage - + {isAutoSlippage ? (
- + {breakdown.map((item) => { return ( @@ -363,12 +324,12 @@ const FeeBreakdown: FC = ({ {item.title} @@ -407,7 +368,7 @@ const ConversionRate: FC = ({ const shouldShowTooltip = rateText.length > 22 const content = ( -
+
{rateText} diff --git a/packages/ui/src/components/widgets/FetchingQuoteLoader.tsx b/packages/ui/src/components/widgets/FetchingQuoteLoader.tsx index 88a2c9b4f..122e97ac6 100644 --- a/packages/ui/src/components/widgets/FetchingQuoteLoader.tsx +++ b/packages/ui/src/components/widgets/FetchingQuoteLoader.tsx @@ -1,14 +1,14 @@ import { type FC } from 'react' import { Flex, Text } from '../primitives/index.js' import { LoadingSpinner } from '../common/LoadingSpinner.js' -import type { Styles } from '@relayprotocol/relay-design-system/css' +import { cn } from '../../utils/cn.js' type Props = { isLoading: boolean - containerCss?: Styles + containerClassName?: string } -const FetchingQuoteLoader: FC = ({ isLoading, containerCss }) => { +const FetchingQuoteLoader: FC = ({ isLoading, containerClassName }) => { if (!isLoading) { return null } @@ -16,16 +16,12 @@ const FetchingQuoteLoader: FC = ({ isLoading, containerCss }) => { return ( - + Fetching the best price ) diff --git a/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx b/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx index b0d8af898..e9164a84a 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx @@ -425,14 +425,7 @@ export const OnrampModal: FC = ({ } onOpenChange(open) }} - css={{ - overflow: 'hidden', - p: '4', - maxWidth: '412px !important', - '@media(max-width: 520px)': { - maxWidth: 'unset !important' - } - }} + className="relay-overflow-hidden relay-p-4 !relay-max-w-[412px] max-[520px]:!relay-max-w-[unset]" > {step === OnrampStep.Confirming ? ( = ({ return ( Buy {toToken?.symbol} ({toChain?.displayName}) - + = ({ your card - - + = ({ Learn more @@ -113,7 +95,7 @@ export const OnrampConfirmingStep: FC = ({
Purchase {fromToken?.symbol} ({fromChain?.displayName}) via your card for Relay to convert to {toToken?.symbol} ( diff --git a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingPassthroughStep.tsx b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingPassthroughStep.tsx index f83341ec4..2e0651183 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingPassthroughStep.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingPassthroughStep.tsx @@ -1,7 +1,6 @@ import type { FC } from 'react' import { Anchor, - Box, ChainTokenIcon, Flex, Text @@ -25,33 +24,20 @@ export const OnrampProcessingPassthroughStep: FC< return ( - + Processing Transaction @@ -66,38 +52,21 @@ export const OnrampProcessingPassthroughStep: FC< direction="column" justify="center" align="center" - css={{ - py: '4', - px: '3', - borderRadius: 'widget-card-border-radius', - '--borderColor': 'colors.subtle-border-color', - border: '1px solid var(--borderColor)' - }} + className="relay-py-4 relay-px-3 relay-rounded-widget-card relay-border relay-border-solid relay-border-[var(--relay-colors-subtle-border-color)]" > - - +
+ - - +
+ Finalizing your purchase through MoonPay, it may take a few minutes to process. @@ -105,14 +74,14 @@ export const OnrampProcessingPassthroughStep: FC< Track MoonPay transaction{' '} - + ) : null}
- + Feel free to leave at any time, MoonPay will email you with updates.
diff --git a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingStepUI.tsx b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingStepUI.tsx index ff10aed0d..2ae71f067 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingStepUI.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampProcessingStepUI.tsx @@ -69,41 +69,29 @@ export const OnrampProcessingStepUI: FC = ({ return ( - + Processing Transaction - - - + +
+ +
+ = ({ Track MoonPay transaction{' '} ) : null} {processingStep === OnrampProcessingStep.Relaying ? ( - - + + ) : ( )}
@@ -144,17 +132,7 @@ export const OnrampProcessingStepUI: FC = ({ delayedMoonpayTx ? ( Looks like its taking longer than expected. Please go to MoonPay @@ -163,12 +141,7 @@ export const OnrampProcessingStepUI: FC = ({ @@ -184,7 +157,7 @@ export const OnrampProcessingStepUI: FC = ({ It might take a few minutes for the MoonPay transaction to @@ -194,34 +167,25 @@ export const OnrampProcessingStepUI: FC = ({ ) ) : null} - - - +
+ +
+ = ({ View Tx: {truncateAddress(fillTxHash)} @@ -246,7 +210,7 @@ export const OnrampProcessingStepUI: FC = ({ {processingStep === OnrampProcessingStep.Relaying ? ( ) : null}
@@ -257,7 +221,7 @@ export const OnrampProcessingStepUI: FC = ({ transaction page @@ -271,7 +235,7 @@ export const OnrampProcessingStepUI: FC = ({ Learn more diff --git a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx index 3e3aee160..b9959d636 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx @@ -1,5 +1,4 @@ import type { FC } from 'react' -import { motion } from 'framer-motion' import { Anchor, Box, @@ -39,72 +38,53 @@ export const OnrampSuccessStep: FC = ({ return ( - + Transaction Details - +
- - + + - +
- + Successfully purchased {isLoadingTransaction ? ( - + ) : ( {toAmountFormatted} {toToken.symbol} )} - + View MoonPay transaction{' '} - + {fillTxUrl ? ( @@ -113,11 +93,11 @@ export const OnrampSuccessStep: FC = ({ ) : null} - + {fillTxHash ? ( { e.stopPropagation() @@ -126,10 +106,7 @@ export const OnrampSuccessStep: FC = ({ @@ -140,10 +117,7 @@ export const OnrampSuccessStep: FC = ({ onClick={() => { onOpenChange(false) }} - css={{ - justifyContent: 'center', - width: '100%' - }} + className="relay-justify-center relay-w-full" > Done diff --git a/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx b/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx index 74a23d9ff..6eee61137 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx @@ -18,6 +18,7 @@ import useMoonPayCurrencies, { type MoonPayFiatCurrency } from '../../../../hooks/useMoonPayCurrencies.js' import moonpayFiatCurrencies from '../../../../constants/moonPayFiatCurrencies.js' +import { cn } from '../../../../utils/cn.js' type Props = { moonPayApiKey: string @@ -88,33 +89,17 @@ const FiatCurrencyModal: FC = ({ @@ -128,23 +113,11 @@ const FiatCurrencyModal: FC = ({ } setOpen(open) }} - css={{ - overflow: 'hidden', - p: '4', - maxWidth: '100vw', - maxHeight: '450px !important', - height: 450, - sm: { - maxWidth: '400px !important' - } - }} + className="relay-overflow-hidden relay-p-4 relay-max-w-[100vw] !relay-max-h-[450px] relay-h-[450px] sm:!relay-max-w-[400px]" > Select a currency @@ -160,30 +133,16 @@ const FiatCurrencyModal: FC = ({ setOpen(false) } }} - css={{ - display: 'flex', - flexDirection: 'column', - width: '100%', - gap: '1', - height: 'calc(100% - 24px)', - overflowY: 'auto', - scrollPaddingTop: '40px' - }} + className="relay-flex relay-flex-col relay-w-full relay-gap-1 relay-overflow-y-auto relay-h-[calc(100%-24px)] relay-scroll-pt-[40px]" > + = ({ /> } - containerCss={{ - width: '100%', - height: 40, - scrollSnapAlign: 'start' - }} - css={{ - width: '100%', - _placeholder_parent: { - textOverflow: 'ellipsis' - } - }} + containerClassName="relay-w-full" + style={{ scrollSnapAlign: 'start' }} + className="relay-w-full relay-h-[40px]" value={currencySearchInput} onChange={(e) => { setCurrencySearchInput((e.target as HTMLInputElement).value) @@ -220,55 +171,30 @@ const FiatCurrencyModal: FC = ({ } diff --git a/packages/ui/src/components/common/PercentageButtons.tsx b/packages/ui/src/components/common/PercentageButtons.tsx index ea8761561..b31529f18 100644 --- a/packages/ui/src/components/common/PercentageButtons.tsx +++ b/packages/ui/src/components/common/PercentageButtons.tsx @@ -49,10 +49,10 @@ export const PercentageButtons: FC = ({ const isMobile = variant === 'mobile' const defaultButtonClassName = cn( - isMobile ? 'relay-text-[14px]' : 'relay-text-[12px]', - 'relay-font-medium relay-px-1', - isMobile ? 'relay-py-[6px]' : 'relay-py-1', - isMobile ? '' : 'relay-min-h-[23px]', + 'relay-font-medium', + isMobile ? 'relay-px-[4px] relay-py-[6px]' : 'relay-p-[4px]', + isMobile ? 'relay-h-[26px]' : 'relay-h-[23px]', + 'relay-min-h-0', 'relay-leading-none', 'relay-bg-[var(--relay-colors-widget-selector-background)]', 'relay-border-none', @@ -62,6 +62,8 @@ export const PercentageButtons: FC = ({ 'hover:relay-bg-[var(--relay-colors-widget-selector-hover-background)]' ) + const buttonFontSize = isMobile ? '14px' : '12px' + const buttonClassName = customButtonClassName || defaultButtonClassName const handleMaxClick = async () => { @@ -123,6 +125,8 @@ export const PercentageButtons: FC = ({ aria-label={`${percent}%`} className={buttonClassName} color="white" + size="none" + style={{ fontSize: buttonFontSize }} disabled={!balance || balance === 0n} onClick={() => { if (balance && balance > 0n) { @@ -139,6 +143,8 @@ export const PercentageButtons: FC = ({ aria-label="MAX" className={buttonClassName} color="white" + size="none" + style={{ fontSize: buttonFontSize }} disabled={!balance || balance === 0n} onMouseEnter={handleMaxMouseEnter} onClick={handleMaxClick} diff --git a/packages/ui/src/components/common/SlippageToleranceConfig.tsx b/packages/ui/src/components/common/SlippageToleranceConfig.tsx index 71c199c40..5c730c1c1 100644 --- a/packages/ui/src/components/common/SlippageToleranceConfig.tsx +++ b/packages/ui/src/components/common/SlippageToleranceConfig.tsx @@ -123,25 +123,25 @@ const SlippageTabs: FC = ({ className="relay-hidden max-[520px]:relay-flex max-[520px]:relay-w-full max-[520px]:relay-gap-2 max-[520px]:relay-mb-2" > diff --git a/packages/ui/src/components/primitives/Button.tsx b/packages/ui/src/components/primitives/Button.tsx index cfe1761f3..44be38141 100644 --- a/packages/ui/src/components/primitives/Button.tsx +++ b/packages/ui/src/components/primitives/Button.tsx @@ -5,11 +5,13 @@ import { cn } from '../../utils/cn.js' const buttonVariants = cva( [ 'relay-cursor-pointer relay-outline-none relay-font-body relay-font-bold relay-text-[16px]', - 'relay-transition-[background-color] relay-duration-[250ms] relay-ease-linear', + 'relay-transition-all relay-duration-200 relay-ease-out relay-select-none', 'relay-gap-2 relay-inline-flex relay-items-center relay-leading-[20px]', - 'focus-visible:relay-shadow-[0_0_0_2px_var(--relay-colors-focus-color)]', + 'focus-visible:relay-ring-2 focus-visible:relay-ring-[var(--relay-colors-focus-color)] focus-visible:relay-ring-offset-2', + 'active:relay-scale-[0.98]', 'disabled:relay-cursor-not-allowed disabled:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:relay-text-[color:var(--relay-colors-button-disabled-color)]', - 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]' + 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]', + 'disabled:active:relay-scale-100' ].join(' '), { variants: { diff --git a/packages/ui/src/components/primitives/Dialog.tsx b/packages/ui/src/components/primitives/Dialog.tsx index 2d24ae376..26c81a9e2 100644 --- a/packages/ui/src/components/primitives/Dialog.tsx +++ b/packages/ui/src/components/primitives/Dialog.tsx @@ -20,7 +20,12 @@ const Overlay = forwardRef< {children} @@ -31,9 +36,8 @@ const contentBase = [ 'relay-bg-[var(--relay-colors-modal-background)]', 'relay-rounded-modal', 'relay-border-modal', - 'relay-fixed relay-left-1/2', - 'relay-min-w-[90vw] relay-max-w-[100vw]', - 'sm:relay-min-w-[400px] sm:relay-max-w-[532px]', + 'relay-shadow-xl', + 'relay-fixed', 'relay-max-h-[85vh] relay-overflow-y-auto', 'focus:relay-outline-none' ].join(' ') @@ -61,19 +65,26 @@ const AnimatedContent = forwardRef< >(({ children, className, disableAnimation = false, ...props }, forwardedRef) => { const isMobile = useMediaQuery('(max-width: 520px)') const isMobileSlideUp = isMobile && !disableAnimation + const isMobileFullScreen = isMobile && disableAnimation - const mobileClasses = [ - 'relay-bottom-0 relay-top-auto relay-left-0', - 'relay-translate-x-0', + const mobileSlideUpClasses = [ + 'relay-bottom-0 relay-top-auto relay-left-0 relay-w-full', 'relay-animate-dialog-slide-up', 'data-[state=closed]:relay-animate-dialog-slide-down', - 'max-[520px]:relay-rounded-b-none max-[520px]:relay-w-full' + 'max-[520px]:relay-rounded-b-none' + ].join(' ') + + const mobileFullScreenClasses = [ + 'relay-top-0 relay-left-0 relay-w-full relay-h-full', + 'relay-max-h-full relay-rounded-none' ].join(' ') const desktopClasses = [ - 'relay-top-1/2 -relay-translate-x-1/2 -relay-translate-y-1/2', - 'relay-animate-dialog-fade-in', - 'data-[state=closed]:relay-animate-dialog-fade-out' + 'relay-left-1/2 relay-top-1/2', + 'relay-min-w-[90vw] relay-max-w-[100vw]', + 'sm:relay-min-w-[400px] sm:relay-max-w-[532px]', + 'relay-animate-scale-in', + 'data-[state=closed]:relay-animate-scale-out' ].join(' ') return ( @@ -81,7 +92,11 @@ const AnimatedContent = forwardRef< ref={forwardedRef} className={cn( contentBase, - isMobileSlideUp ? mobileClasses : desktopClasses, + isMobileFullScreen + ? mobileFullScreenClasses + : isMobileSlideUp + ? mobileSlideUpClasses + : desktopClasses, className )} {...props} diff --git a/packages/ui/src/components/primitives/Dropdown.tsx b/packages/ui/src/components/primitives/Dropdown.tsx index 4ae1085dd..0cd5e7a16 100644 --- a/packages/ui/src/components/primitives/Dropdown.tsx +++ b/packages/ui/src/components/primitives/Dropdown.tsx @@ -19,10 +19,11 @@ const DropdownMenuContent = forwardRef< {...props} ref={forwardedRef} className={cn( - 'relay-mx-4 relay-p-3 relay-rounded-[8px] relay-z-[10000002]', + 'relay-mx-4 relay-p-0 relay-rounded-[8px] relay-z-[10000002]', 'relay-bg-[var(--relay-colors-modal-background)]', - 'relay-shadow-[0px_0px_50px_0px_#0000001F]', + 'relay-shadow-lg', 'relay-border-dropdown', + 'relay-animate-content-fade-in', className )} > @@ -45,7 +46,7 @@ const DropdownMenuItem = forwardRef< 'relay-flex relay-items-center relay-text-[16px]', 'relay-text-[color:var(--relay-colors-text-default)]', 'relay-bg-[var(--relay-colors-modal-background)]', - 'relay-p-3 relay-outline-none relay-cursor-pointer', + 'relay-p-2 relay-outline-none relay-cursor-pointer', 'relay-transition-[background-color] relay-duration-150 relay-ease-linear', 'hover:relay-bg-[var(--relay-colors-gray-10)]/10', 'focus:relay-bg-[var(--relay-colors-gray-10)]/10', diff --git a/packages/ui/src/components/primitives/Input.tsx b/packages/ui/src/components/primitives/Input.tsx index b4f13868c..a456a8c9c 100644 --- a/packages/ui/src/components/primitives/Input.tsx +++ b/packages/ui/src/components/primitives/Input.tsx @@ -5,10 +5,11 @@ import { cn } from '../../utils/cn.js' const inputVariants = cva( [ - 'relay-px-4 relay-py-3 relay-rounded-input relay-font-body relay-text-[16px]', + 'relay-w-full relay-px-4 relay-py-3 relay-rounded-input relay-font-body relay-text-[16px]', 'relay-text-[color:var(--relay-colors-input-color)]', 'relay-bg-[var(--relay-colors-input-background)]', - 'placeholder:relay-text-[color:var(--relay-colors-gray10)]', + 'placeholder:relay-text-[color:var(--relay-colors-gray9)]', + 'relay-transition-shadow relay-duration-200', 'focus:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)] focus:relay-outline-none', 'disabled:relay-cursor-not-allowed', '[&::-webkit-outer-spin-button]:relay-appearance-none [&::-webkit-inner-spin-button]:relay-appearance-none' @@ -59,7 +60,7 @@ const Input = forwardRef<
{content} @@ -57,11 +57,12 @@ const Tooltip = ({ side="bottom" align="center" style={{ zIndex: 10000003 }} + className="relay-animate-content-fade-in" {...props} > -
+
{content} diff --git a/packages/ui/src/components/widgets/FeeBreakdown.tsx b/packages/ui/src/components/widgets/FeeBreakdown.tsx index 7177b4349..ea161ab45 100644 --- a/packages/ui/src/components/widgets/FeeBreakdown.tsx +++ b/packages/ui/src/components/widgets/FeeBreakdown.tsx @@ -172,8 +172,8 @@ const FeeBreakdown: FC = ({ id={'fee-breakdown-section'} className="relay-rounded-[var(--relay-radii-widget-card-border-radius)] relay-bg-[var(--relay-colors-widget-background)] relay-border-widget-card relay-overflow-hidden relay-mb-[var(--relay-spacing-widget-card-section-gutter)]" > -
- +
+
) @@ -202,7 +202,8 @@ const FeeBreakdown: FC = ({ {isAutoSlippage ? ( + )} +
+ ) +} + +const SectionTitle: FC<{ children: React.ReactNode }> = ({ children }) => ( +
+ {children} +
+) + +const SubLabel: FC<{ children: React.ReactNode }> = ({ children }) => ( +
+ {children} +
+) + +export const CustomizeSidebar: FC = ({ + singleChainMode, + setSingleChainMode, + supportedWalletVMs, + setSupportedWalletVMs +}) => { + const [open, setOpen] = useState(false) + const { resolvedTheme } = useTheme() + const isDark = resolvedTheme === 'dark' + const defaults = isDark ? DARK_DEFAULTS : LIGHT_DEFAULTS + const { + themeOverrides, + updateThemeValue, + setThemeOverrides, + relayApi, + setRelayApi, + websocketsEnabled, + setWebsocketsEnabled + } = useCustomize() + + const getNestedValue = (path: string): string => { + const keys = path.split('.') + let obj: any = themeOverrides + for (const key of keys) { + if (!obj || typeof obj !== 'object') return '' + obj = obj[key] + } + return typeof obj === 'string' ? obj : '' + } + + // Auto-derive hover state from a base color + const deriveHover = useCallback( + (baseHex: string) => { + return isDark ? lighten(baseHex, 8) : darken(baseHex, 8) + }, + [isDark] + ) + + // When setting a base color, also auto-set its hover variant + const setWithHover = useCallback( + (basePath: string, hoverPath: string, value: string) => { + updateThemeValue(basePath, value) + updateThemeValue(hoverPath, deriveHover(value)) + }, + [updateThemeValue, deriveHover] + ) + + const clearWithHover = useCallback( + (basePath: string, hoverPath: string) => { + updateThemeValue(basePath, undefined) + updateThemeValue(hoverPath, undefined) + }, + [updateThemeValue] + ) + + const sidebarBg = isDark ? '#111' : '#fff' + const sidebarBorder = isDark + ? 'rgba(255,255,255,0.1)' + : 'rgba(0,0,0,0.08)' + const sidebarText = isDark ? '#e5e5e5' : '#1a1a1a' + + return ( + <> + {/* Toggle button */} + + + {/* Sidebar panel */} +
+ {/* Header */} +
+ Customize + +
+ + {/* Theme Colors */} +
+ Colors + + updateThemeValue('primaryColor', v)} + onClear={() => updateThemeValue('primaryColor', undefined)} + /> + + updateThemeValue('focusColor', v)} + onClear={() => updateThemeValue('focusColor', undefined)} + /> + + Text + + updateThemeValue('text.default', v)} + onClear={() => updateThemeValue('text.default', undefined)} + /> + + updateThemeValue('text.subtle', v)} + onClear={() => updateThemeValue('text.subtle', undefined)} + /> + + { + updateThemeValue('anchor.color', v) + updateThemeValue('anchor.hover.color', deriveHover(v)) + }} + onClear={() => { + updateThemeValue('anchor.color', undefined) + updateThemeValue('anchor.hover.color', undefined) + }} + /> +
+ + {/* Buttons */} +
+ Buttons + + Primary (hover auto-derived) + + + setWithHover( + 'buttons.primary.background', + 'buttons.primary.hover.background', + v + ) + } + onClear={() => + clearWithHover( + 'buttons.primary.background', + 'buttons.primary.hover.background' + ) + } + /> + + { + updateThemeValue('buttons.primary.color', v) + updateThemeValue('buttons.primary.hover.color', v) + }} + onClear={() => { + updateThemeValue('buttons.primary.color', undefined) + updateThemeValue('buttons.primary.hover.color', undefined) + }} + /> + + Secondary (hover auto-derived) + + + setWithHover( + 'buttons.secondary.background', + 'buttons.secondary.hover.background', + v + ) + } + onClear={() => + clearWithHover( + 'buttons.secondary.background', + 'buttons.secondary.hover.background' + ) + } + /> + + { + updateThemeValue('buttons.secondary.color', v) + updateThemeValue('buttons.secondary.hover.color', v) + }} + onClear={() => { + updateThemeValue('buttons.secondary.color', undefined) + updateThemeValue('buttons.secondary.hover.color', undefined) + }} + /> + + {/* CTA italic toggle */} +
+ + updateThemeValue( + 'buttons.cta.fontStyle', + e.target.checked ? 'italic' : 'normal' + ) + } + /> + +
+
+ + {/* Surfaces */} +
+ Surfaces + + updateThemeValue('widget.background', v)} + onClear={() => updateThemeValue('widget.background', undefined)} + /> + + updateThemeValue('widget.card.background', v)} + onClear={() => + updateThemeValue('widget.card.background', undefined) + } + /> + + + setWithHover( + 'widget.selector.background', + 'widget.selector.hover.background', + v + ) + } + onClear={() => + clearWithHover( + 'widget.selector.background', + 'widget.selector.hover.background' + ) + } + /> + + updateThemeValue('input.background', v)} + onClear={() => updateThemeValue('input.background', undefined)} + /> + + updateThemeValue('modal.background', v)} + onClear={() => updateThemeValue('modal.background', undefined)} + /> + + updateThemeValue('subtleBackgroundColor', v)} + onClear={() => + updateThemeValue('subtleBackgroundColor', undefined) + } + /> + + updateThemeValue('subtleBorderColor', v)} + onClear={() => updateThemeValue('subtleBorderColor', undefined)} + /> + + {/* Border radius */} +
+ Border Radius + { + const v = parseInt(getNestedValue('widget.card.borderRadius')) + return isNaN(v) ? 12 : v + })()} + onChange={(e) => { + const val = `${e.target.value}px` + updateThemeValue('widget.borderRadius', val) + updateThemeValue('widget.card.borderRadius', val) + updateThemeValue('modal.borderRadius', val) + updateThemeValue('input.borderRadius', val) + updateThemeValue('dropdown.borderRadius', val) + updateThemeValue('widget.swap.currency.button.borderRadius', val) + updateThemeValue('buttons.borderRadius', val) + }} + style={{ width: 80 }} + /> + + {(() => { + const v = parseInt(getNestedValue('widget.card.borderRadius')) + return isNaN(v) ? 12 : v + })()}px + +
+ + {/* Reset all button */} + +
+ + {/* Widget Config */} +
+ Config + +
+ setSingleChainMode(e.target.checked)} + /> + +
+ +
+ Wallet VMs +
+
+ {WALLET_VM_TYPES.map((vm) => ( +
+ { + if (e.target.checked) { + setSupportedWalletVMs((prev) => [...prev, vm]) + } else { + setSupportedWalletVMs((prev) => + prev.filter((item) => item !== vm) + ) + } + }} + /> + +
+ ))} +
+
+ + {/* Global / Provider Settings */} +
+ Global + +
+ API + +
+ +
+ setWebsocketsEnabled(e.target.checked)} + /> + +
+
+
+ + ) +} diff --git a/demo/components/providers/RelayKitProviderWrapper.tsx b/demo/components/providers/RelayKitProviderWrapper.tsx index d3a7f71b5..e2b58e159 100644 --- a/demo/components/providers/RelayKitProviderWrapper.tsx +++ b/demo/components/providers/RelayKitProviderWrapper.tsx @@ -6,7 +6,8 @@ import { import { RelayKitProvider } from '@relayprotocol/relay-kit-ui' import { useTheme } from 'next-themes' import { useRouter } from 'next/router' -import { FC, ReactNode, useEffect, useState } from 'react' +import { FC, ReactNode, useMemo } from 'react' +import { useCustomize } from 'context/customizeContext' const DEFAULT_APP_FEES = [ { @@ -15,6 +16,11 @@ const DEFAULT_APP_FEES = [ } ] +const BASE_THEME = { + font: 'var(--font-inter), -apple-system, Helvetica, sans-serif', + fontHeading: 'var(--font-inter), -apple-system, Helvetica, sans-serif' +} + export const RelayKitProviderWrapper: FC<{ relayApi?: string dynamicChains: RelayChain[] @@ -22,16 +28,16 @@ export const RelayKitProviderWrapper: FC<{ }> = ({ relayApi, dynamicChains, children }) => { const { theme } = useTheme() const router = useRouter() - const [websocketsEnabled, setWebsocketsEnabled] = useState(false) + const { themeOverrides, websocketsEnabled } = useCustomize() const appFeesEnabled = router.query.appFees === 'true' - // Handle websocket configuration from query params - useEffect(() => { - const websocketParam = router.query.websockets as string - if (websocketParam !== undefined) { - setWebsocketsEnabled(websocketParam === 'true') - } - }, [router.query.websockets]) + const mergedTheme = useMemo( + () => ({ + ...BASE_THEME, + ...themeOverrides + }), + [themeOverrides] + ) return ( {children} diff --git a/demo/context/customizeContext.tsx b/demo/context/customizeContext.tsx new file mode 100644 index 000000000..e31a34d68 --- /dev/null +++ b/demo/context/customizeContext.tsx @@ -0,0 +1,91 @@ +import { + createContext, + FC, + ReactNode, + useContext, + useState, + useCallback, + useMemo +} from 'react' +import type { RelayKitTheme } from '@relayprotocol/relay-kit-ui' +import { MAINNET_RELAY_API, TESTNET_RELAY_API } from '@relayprotocol/relay-sdk' + +type CustomizeState = { + themeOverrides: Partial + setThemeOverrides: (overrides: Partial) => void + updateThemeValue: (path: string, value: string | undefined) => void + relayApi: string + setRelayApi: (api: string) => void + websocketsEnabled: boolean + setWebsocketsEnabled: (enabled: boolean) => void +} + +const CustomizeContext = createContext({ + themeOverrides: {}, + setThemeOverrides: () => {}, + updateThemeValue: () => {}, + relayApi: MAINNET_RELAY_API, + setRelayApi: () => {}, + websocketsEnabled: false, + setWebsocketsEnabled: () => {} +}) + +export const useCustomize = () => useContext(CustomizeContext) + +export const CustomizeProvider: FC<{ children: ReactNode }> = ({ + children +}) => { + const [themeOverrides, setThemeOverrides] = useState< + Partial + >({ + buttons: { + cta: { + fontStyle: 'italic' + } + } + }) + const [relayApi, setRelayApi] = useState(MAINNET_RELAY_API) + const [websocketsEnabled, setWebsocketsEnabled] = useState(false) + + const updateThemeValue = useCallback( + (path: string, value: string | undefined) => { + setThemeOverrides((prev) => { + const next = { ...prev } + const keys = path.split('.') + let obj: any = next + for (let i = 0; i < keys.length - 1; i++) { + if (!obj[keys[i]]) obj[keys[i]] = {} + else obj[keys[i]] = { ...obj[keys[i]] } + obj = obj[keys[i]] + } + const lastKey = keys[keys.length - 1] + if (value === undefined || value === '') { + delete obj[lastKey] + } else { + obj[lastKey] = value + } + return next + }) + }, + [] + ) + + const value = useMemo( + () => ({ + themeOverrides, + setThemeOverrides, + updateThemeValue, + relayApi, + setRelayApi, + websocketsEnabled, + setWebsocketsEnabled + }), + [themeOverrides, updateThemeValue, relayApi, websocketsEnabled] + ) + + return ( + + {children} + + ) +} diff --git a/demo/pages/_app.tsx b/demo/pages/_app.tsx index e9a6206cd..de4be4fb3 100644 --- a/demo/pages/_app.tsx +++ b/demo/pages/_app.tsx @@ -3,7 +3,7 @@ import '../fonts.css' import '../global.css' import type { AppProps, AppContext } from 'next/app' -import React, { ReactNode, FC, useState, useEffect } from 'react' +import React, { ReactNode, FC, useEffect } from 'react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { createConfig, http, WagmiProvider } from 'wagmi' import { Chain, mainnet, optimism, base, zora } from 'wagmi/chains' @@ -33,6 +33,7 @@ import { EclipseWalletConnectors } from '@dynamic-labs/eclipse' import { TronWalletConnectors } from '@dynamic-labs/tron' import { AbstractEvmWalletConnectors } from '@dynamic-labs-connectors/abstract-global-wallet-evm' import { MoonPayProvider } from 'context/MoonpayProvider' +import { CustomizeProvider, useCustomize } from 'context/customizeContext' import { queryRelayChains } from '@relayprotocol/relay-kit-hooks' import { RelayKitProviderWrapper } from 'components/providers/RelayKitProviderWrapper' import { Barlow, Chivo, Inter } from 'next/font/google' @@ -73,14 +74,14 @@ const queryClient = new QueryClient() const AppWrapper: FC = ({ children, dynamicChains }) => { const { walletFilter, setWalletFilter } = useWalletFilter() + const { relayApi, setRelayApi } = useCustomize() const router = useRouter() - const [relayApi, setRelayApi] = useState(MAINNET_RELAY_API) useEffect(() => { const isTestnet = router.query.api === 'testnets' const newApi = isTestnet ? TESTNET_RELAY_API : MAINNET_RELAY_API if (relayApi !== newApi) { - setRelayApi(isTestnet ? TESTNET_RELAY_API : MAINNET_RELAY_API) + setRelayApi(newApi) } }, [router.query.api]) @@ -130,7 +131,7 @@ const AppWrapper: FC = ({ children, dynamicChains }) => { }, []) return ( -
+
- - - - - + + + + + + + ) } diff --git a/demo/pages/ui/swap.tsx b/demo/pages/ui/swap.tsx index 64d5ad934..28be9ae45 100644 --- a/demo/pages/ui/swap.tsx +++ b/demo/pages/ui/swap.tsx @@ -35,6 +35,7 @@ import { adaptSuiWallet } from '@relayprotocol/relay-sui-wallet-adapter' import { adaptTronWallet } from '@relayprotocol/relay-tron-wallet-adapter' import Head from 'next/head' import { isTronWallet, TronWallet } from '@dynamic-labs/tron' +import { CustomizeSidebar } from 'components/CustomizeSidebar' const WALLET_VM_TYPES = ['evm', 'bvm', 'svm', 'suivm', 'tvm', 'hypevm'] as const @@ -200,10 +201,7 @@ const SwapWidgetPage: NextPage = () => { return ( @@ -228,7 +226,7 @@ const SwapWidgetPage: NextPage = () => { flexDirection: 'column', gap: 8, width: '100%', - maxWidth: 420 + maxWidth: 400 }} >
{ />
-
-
- Config -
- -
- setSingleChainMode(e.target.checked)} - /> - -
- -
- {WALLET_VM_TYPES.map((vm) => ( -
- { - if (e.target.checked) { - setSupportedWalletVMs((prev) => [...prev, vm]) - } else { - setSupportedWalletVMs((prev) => - prev.filter((item) => item !== vm) - ) - } - }} - /> - -
- ))} -
-
+ ) } diff --git a/packages/ui/src/components/common/MultiWalletDropdown.tsx b/packages/ui/src/components/common/MultiWalletDropdown.tsx index 65a8f331a..e2b275b07 100644 --- a/packages/ui/src/components/common/MultiWalletDropdown.tsx +++ b/packages/ui/src/components/common/MultiWalletDropdown.tsx @@ -121,7 +121,7 @@ export const MultiWalletDropdown: FC = ({ const shouldShowEns = isEnsCapableVmType && Boolean(displayName) const dropdownItemClassName = - 'relay-rounded-lg relay-gap-2 relay-cursor-pointer relay-p-2 relay-transition-[backdrop-filter] relay-duration-[250ms] relay-ease-linear hover:relay-brightness-[0.98] relay-shrink-0 relay-content-center relay-w-full' + 'relay-rounded-lg relay-gap-2 relay-cursor-pointer relay-p-2 relay-transition-[background-color] relay-duration-150 relay-ease-linear hover:relay-bg-[var(--relay-colors-gray3)] relay-shrink-0 relay-content-center relay-w-full' return ( = ({ {isSupportedSelectedWallet && selectedWalletAddress && @@ -192,7 +192,7 @@ export const MultiWalletDropdown: FC = ({ style={{ color: !selectedWallet && selectedWalletAddress ? 'var(--relay-colors-amber11)' - : 'var(--relay-colors-primary9)' + : 'var(--relay-colors-primary11)' }} > diff --git a/packages/ui/src/components/common/TokenSelector/SuggestedTokens.tsx b/packages/ui/src/components/common/TokenSelector/SuggestedTokens.tsx index bfecf6708..2fe86b6d4 100644 --- a/packages/ui/src/components/common/TokenSelector/SuggestedTokens.tsx +++ b/packages/ui/src/components/common/TokenSelector/SuggestedTokens.tsx @@ -64,7 +64,7 @@ export const SuggestedTokens: FC = ({ color="ghost" size="none" corners="pill" - className="relay-flex relay-shrink-0 relay-cursor-pointer relay-outline-none relay-py-1 relay-pl-1 relay-pr-2 relay-gap-[1px] relay-items-center relay-border relay-border-solid relay-border-[var(--relay-colors-gray5)] relay-focus-inset" + className="relay-flex relay-shrink-0 relay-cursor-pointer relay-outline-none relay-py-1 relay-pl-1 relay-pr-2 relay-gap-[1px] relay-items-center relay-border relay-border-solid relay-border-[var(--relay-colors-gray5)] relay-focus-inset relay-transition-none" > const Button = forwardRef< HTMLButtonElement, - Omit, 'ref' | 'color'> & - ButtonVariantProps & { className?: string } ->(({ className, children, ...props }, forwardedRef) => { + Omit, 'ref' | 'color' | 'style'> & + ButtonVariantProps & { className?: string; style?: React.CSSProperties } +>(({ className, children, style, ...props }, forwardedRef) => { const { color, size, corners, cta, ...buttonProps } = { ...props } return ( diff --git a/packages/ui/src/components/primitives/Dialog.tsx b/packages/ui/src/components/primitives/Dialog.tsx index 26c81a9e2..fdeb43b69 100644 --- a/packages/ui/src/components/primitives/Dialog.tsx +++ b/packages/ui/src/components/primitives/Dialog.tsx @@ -34,7 +34,7 @@ const Overlay = forwardRef< const contentBase = [ 'relay-bg-[var(--relay-colors-modal-background)]', - 'relay-rounded-modal', + 'relay-rounded-[var(--relay-radii-modal-border-radius)]', 'relay-border-modal', 'relay-shadow-xl', 'relay-fixed', diff --git a/packages/ui/src/components/primitives/Dropdown.tsx b/packages/ui/src/components/primitives/Dropdown.tsx index 0cd5e7a16..c63755b39 100644 --- a/packages/ui/src/components/primitives/Dropdown.tsx +++ b/packages/ui/src/components/primitives/Dropdown.tsx @@ -48,8 +48,8 @@ const DropdownMenuItem = forwardRef< 'relay-bg-[var(--relay-colors-modal-background)]', 'relay-p-2 relay-outline-none relay-cursor-pointer', 'relay-transition-[background-color] relay-duration-150 relay-ease-linear', - 'hover:relay-bg-[var(--relay-colors-gray-10)]/10', - 'focus:relay-bg-[var(--relay-colors-gray-10)]/10', + 'hover:relay-bg-[var(--relay-colors-gray3)]', + 'focus:relay-bg-[var(--relay-colors-gray3)]', className )} > diff --git a/packages/ui/src/components/primitives/Input.tsx b/packages/ui/src/components/primitives/Input.tsx index a456a8c9c..65984257e 100644 --- a/packages/ui/src/components/primitives/Input.tsx +++ b/packages/ui/src/components/primitives/Input.tsx @@ -55,7 +55,7 @@ const Input = forwardRef< const { size, ellipsify, style, ...inputProps } = props return ( -
+
{icon && (
{content} @@ -62,7 +62,7 @@ const Tooltip = ({ >
{content} diff --git a/packages/ui/src/components/widgets/SwapWidget/index.tsx b/packages/ui/src/components/widgets/SwapWidget/index.tsx index 215bd3050..bac45adc5 100644 --- a/packages/ui/src/components/widgets/SwapWidget/index.tsx +++ b/packages/ui/src/components/widgets/SwapWidget/index.tsx @@ -1034,7 +1034,7 @@ const SwapWidget: FC = ({ aria-label="Swap Tokens Direction" size="none" color="white" - className="relay-mt-[4px] relay-text-[color:var(--relay-colors-gray9)] relay-self-center relay-justify-center relay-w-full relay-h-full relay-z-10 relay-border-[length:var(--relay-borders-widget-swap-currency-button-border-width)] relay-border-solid relay-border-[color:var(--relay-colors-widget-swap-currency-button-border-color)] relay-rounded-swap-btn hover:relay-text-[color:var(--relay-colors-gray11)] hover:relay-bg-[var(--relay-colors-gray-2)]" + className="relay-mt-[4px] relay-text-[color:var(--relay-colors-gray9)] relay-self-center relay-justify-center relay-w-full relay-h-full relay-z-10 relay-border-[length:var(--relay-borders-widget-swap-currency-button-border-width)] relay-border-solid !relay-border-[color:var(--relay-colors-widget-swap-currency-button-border-color)] relay-rounded-swap-btn hover:relay-text-[color:var(--relay-colors-gray11)] hover:relay-bg-[var(--relay-colors-gray-2)]" onClick={() => { if (fromToken || toToken) { if (isUsdInputMode) { diff --git a/packages/ui/src/providers/RelayKitProvider.tsx b/packages/ui/src/providers/RelayKitProvider.tsx index 9dc6f64b3..13e8e28e9 100644 --- a/packages/ui/src/providers/RelayKitProvider.tsx +++ b/packages/ui/src/providers/RelayKitProvider.tsx @@ -85,6 +85,7 @@ export const themeOverrides: ThemeOverridesMap = { success: '--relay-colors-text-success' }, buttons: { + borderRadius: '--relay-radii-button-border-radius', primary: { color: '--relay-colors-primary-button-color', background: '--relay-colors-primary-button-background', diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index 9db442679..401b11d0a 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -215,6 +215,7 @@ /* --- Fonts --- */ --relay-fonts-body: var(--font-inter), sans-serif; --relay-fonts-heading: var(--font-inter), sans-serif; + --relay-fonts-button-cta-font-style: normal; /* --- Gradients --- */ --relay-gradients-success: linear-gradient(to right, #30A46C, #0ADF79); @@ -223,6 +224,7 @@ --relay-radii-widget-border-radius: 16px; --relay-radii-widget-card-border-radius: 12px; --relay-radii-modal-border-radius: 16px; + --relay-radii-button-border-radius: 12px; --relay-radii-input-border-radius: 8px; --relay-radii-dropdown-border-radius: 8px; --relay-radii-widget-swap-currency-button-border-radius: 8px; @@ -820,6 +822,17 @@ /* RGB triplet for composable opacity (dark) */ --relay-colors-gray-rgb: 112, 112, 112; + + /* Widget selector (token trigger) */ + --relay-colors-widget-selector-background: #202425; + --relay-colors-widget-selector-hover-background: #2a2d2e; + + /* Dark mode borders — grey instead of purple/invisible */ + --relay-borders-modal-border: 1px solid var(--relay-colors-gray5); + --relay-borders-widget-border: 1px solid var(--relay-colors-gray5); + --relay-borders-widget-card-border: 1px solid var(--relay-colors-gray5); + --relay-borders-dropdown-border: 1px solid var(--relay-colors-gray5); + --relay-colors-widget-swap-currency-button-border-color: var(--relay-colors-gray5); } } /* end @layer base */ @@ -842,6 +855,9 @@ .relay-border-dropdown { border: var(--relay-borders-dropdown-border); } + .relay-cta-font-style:not(:disabled) { + font-style: var(--relay-fonts-button-cta-font-style); + } } /* High-specificity inset focus ring — matches old PandaCSS :not(#\#) pattern */ diff --git a/packages/ui/src/themes/RelayKitTheme.ts b/packages/ui/src/themes/RelayKitTheme.ts index 9ef82f51d..3340ecee0 100644 --- a/packages/ui/src/themes/RelayKitTheme.ts +++ b/packages/ui/src/themes/RelayKitTheme.ts @@ -23,6 +23,7 @@ export interface RelayKitTheme { success?: string } buttons?: { + borderRadius?: string primary?: Button secondary?: Button disabled?: { From 30eb11a509e53c6324a41d51ac47e120757e1688 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Mon, 2 Mar 2026 12:03:27 -0500 Subject: [PATCH 05/25] Remove api and websocket config from sidebar --- demo/components/CustomizeSidebar.tsx | 50 ++-------------------------- 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/demo/components/CustomizeSidebar.tsx b/demo/components/CustomizeSidebar.tsx index 25ee1019f..5e79a3c7c 100644 --- a/demo/components/CustomizeSidebar.tsx +++ b/demo/components/CustomizeSidebar.tsx @@ -1,11 +1,7 @@ import { FC, useCallback, useEffect, useRef, useState } from 'react' import { useCustomize } from 'context/customizeContext' import { useTheme } from 'next-themes' -import { - MAINNET_RELAY_API, - TESTNET_RELAY_API, - ChainVM -} from '@relayprotocol/relay-sdk' +import { ChainVM } from '@relayprotocol/relay-sdk' const WALLET_VM_TYPES = [ 'evm', @@ -238,11 +234,7 @@ export const CustomizeSidebar: FC = ({ const { themeOverrides, updateThemeValue, - setThemeOverrides, - relayApi, - setRelayApi, - websocketsEnabled, - setWebsocketsEnabled + setThemeOverrides } = useCustomize() const getNestedValue = (path: string): string => { @@ -732,44 +724,6 @@ export const CustomizeSidebar: FC = ({ > Global -
- API - -
- -
- setWebsocketsEnabled(e.target.checked)} - /> - -
From 0f7e4acba6124342b6a5b128773fda492f76057a Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Mon, 2 Mar 2026 15:59:22 -0500 Subject: [PATCH 06/25] clean up --- demo/components/CustomizeSidebar.tsx | 4 +- packages/ui/QA_TEST_PLAN.md | 203 ++++++++++++++++++ .../widgets/OnrampWidget/widget/index.tsx | 9 +- 3 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 packages/ui/QA_TEST_PLAN.md diff --git a/demo/components/CustomizeSidebar.tsx b/demo/components/CustomizeSidebar.tsx index 5e79a3c7c..0cadb7869 100644 --- a/demo/components/CustomizeSidebar.tsx +++ b/demo/components/CustomizeSidebar.tsx @@ -279,7 +279,7 @@ export const CustomizeSidebar: FC = ({ const sidebarText = isDark ? '#e5e5e5' : '#1a1a1a' return ( - <> +
{/* Toggle button */}
- +
) } diff --git a/packages/ui/QA_TEST_PLAN.md b/packages/ui/QA_TEST_PLAN.md new file mode 100644 index 000000000..2db3ae233 --- /dev/null +++ b/packages/ui/QA_TEST_PLAN.md @@ -0,0 +1,203 @@ +# UI Test Plan — Panda CSS → Tailwind Migration + +Goal: Verify zero visual/functional regressions after the CSS migration. This plan is designed to minimize QA effort by testing the highest-impact surfaces first and using a matrix approach to avoid redundant passes. + +--- + +## Testing Matrix + +We have 4 dimensions. Instead of testing every combination (explosion), we use a **primary path + targeted sweeps** approach. + +| Dimension | Options | +|-----------|---------| +| **Viewport** | Desktop (1280px+), Mobile (375px) | +| **Theme** | Light, Dark | +| **Browser** | Chrome, Safari, Firefox | +| **Widget** | SwapWidget, OnrampWidget, TokenWidget | + +**Strategy:** +- Do the full flow walkthroughs on **Chrome Desktop, Light + Dark** +- Do a **mobile sweep** on Chrome + Safari (iOS) for responsive-specific behavior +- Do a **Firefox spot-check** on desktop only (it shares Gecko layout, unlikely to diverge from Chrome on Tailwind) + +--- + +## Phase 1: Core Flows (Chrome Desktop, Light + Dark) + +These are the critical happy paths. Each should be tested in both light and dark mode. + +### 1.1 SwapWidget — Full Swap Flow +**Page:** `/ui/swap` + +- [ ] Widget renders with correct background, border-radius, border, box-shadow +- [ ] Token panels (sell/buy) have correct card background and borders +- [ ] Click "Select Token" → TokenSelector dialog opens (centered, scale animation) +- [ ] ChainFilter sidebar: search works, chains render with square icons, scrollable +- [ ] ChainFilter starring: right-click a chain → star popover appears, chain moves to "Starred" +- [ ] Suggested token pills render with border, no lingering focus ring after click +- [ ] Token list: hover highlights, scroll works, keyboard nav (arrow keys from search) +- [ ] Select an unverified token → UnverifiedTokenModal appears with warning +- [ ] Accept unverified token → modal closes, token selected, persisted in localStorage +- [ ] Enter amount → quote fetches, fee breakdown populates +- [ ] USD mode toggle (⇅ button) switches between token/USD input +- [ ] Swap arrow button: correct border (matches widget card border), hover state works +- [ ] Swap arrow click → tokens swap positions, amounts recalculate +- [ ] SlippageConfig: opens, Auto/Custom tabs work, color-coded rating displays +- [ ] Fee breakdown: collapsible, slippage row always visible, price impact tooltip works +- [ ] CTA button: "Connect Wallet" / "Swap" — italic when enabled, normal when disabled +- [ ] Percentage buttons (25/50/75/100%) apply correct amounts +- [ ] Multi-wallet dropdown: renders for sell side (OriginWalletSelector) and buy side (RecipientSelector) +- [ ] CustomAddressModal: input spans full width, ENS resolution works, recent addresses show + +### 1.2 OnrampWidget +**Page:** `/ui/onramp` + +- [ ] Widget renders with correct theming +- [ ] Quick amount buttons ($100/$300/$1000) work +- [ ] Token selector opens and functions correctly +- [ ] USD/Token toggle works +- [ ] Address entry works +- [ ] CTA button states render correctly + +### 1.3 TokenWidget (Buy/Sell Tabs) +**Page:** `/ui/token` + +- [ ] Tabs render and switch between Buy/Sell +- [ ] Active tab indicator styled correctly +- [ ] Both tab flows work: select token → enter amount → destination → swap +- [ ] AmountModeToggle works in both tabs +- [ ] Fee breakdown displays correctly + +### 1.4 Chain-Locked Mode +**Page:** `/ui/chain` + +- [ ] Widget renders with locked destination chain (Base) +- [ ] Token selector respects chain lock +- [ ] Swap flow completes normally + +### 1.5 Deposit Address Flow +**Page:** `/ui/depositAddresses` + +- [ ] Deposit address modal renders +- [ ] Waiting for deposit step displays correctly +- [ ] Copy address button works + +--- + +## Phase 2: Dark Mode Sweep (Chrome Desktop) + +Run through Phase 1 flows again in dark mode, focusing on these known dark-mode-specific items: + +- [ ] Widget background switches to dark gray (`gray-1`) +- [ ] Token selector trigger background is `#202425` (not the light gray) +- [ ] Borders switch from primary4 (violet) to gray5 (visible gray) +- [ ] Swap arrow border matches widget card border (gray5 in dark) +- [ ] Text colors flip correctly (gray-12 for primary text) +- [ ] All modals have correct dark backgrounds and borders +- [ ] Fee breakdown card background correct +- [ ] Tooltip/popover backgrounds correct +- [ ] Input backgrounds correct (gray-3 dark variant) +- [ ] Skeleton loading states use correct dark gray +- [ ] No white flashes or light-mode colors bleeding through + +--- + +## Phase 3: Mobile Sweep (Chrome + Safari iOS, 375px width) + +Test these on both light and dark mode on at least one pass. + +### 3.1 Dialog Behavior +- [ ] All modals render as **bottom-sheet** (slide up from bottom, not centered) +- [ ] Bottom-sheet animation is smooth (no janky translate conflicts) +- [ ] Full-screen modals (TokenSelector) take full viewport +- [ ] Swipe/tap outside to dismiss works + +### 3.2 TokenSelector — Mobile Layout +- [ ] Renders in **tabbed mode** (not sidebar), with "Tokens" / "Chains" tabs +- [ ] MobileChainSelector strip renders at top with horizontal scroll +- [ ] Chain search works in mobile view +- [ ] Keyboard doesn't cause layout jumps + +### 3.3 Tooltip → Popover Conversion +- [ ] All tooltips render as **click-to-open popovers** on mobile (not hover) +- [ ] Price impact tooltip, fee breakdown tooltips all tappable +- [ ] Popover dismisses on outside tap + +### 3.4 Responsive Layouts +- [ ] Widget fills viewport width correctly +- [ ] Percentage buttons use mobile variant (taller, flex-1) +- [ ] PercentageButtons don't overflow +- [ ] CustomAddressModal input is full width +- [ ] Text doesn't overflow or get clipped +- [ ] Swap arrow button centered correctly between panels + +### 3.5 Safari-Specific +- [ ] No iOS input zoom (font-size >= 16px on inputs) +- [ ] Bottom-sheet doesn't conflict with Safari's bottom bar +- [ ] `-webkit-appearance: none` on inputs (spin buttons hidden) +- [ ] Animations use `-webkit-` prefixes where needed (autoprefixer should handle) + +--- + +## Phase 4: Firefox Spot-Check (Desktop only) + +Quick pass through the SwapWidget flow: + +- [ ] Widget renders correctly (borders, radius, shadows) +- [ ] TokenSelector dialog opens with correct animation +- [ ] All CSS variables resolve correctly +- [ ] Focus ring (box-shadow inset) renders correctly +- [ ] Scrollbar styling acceptable (Firefox uses system scrollbars) +- [ ] Animations play smoothly + +--- + +## Phase 5: Customize Sidebar (Demo-specific) + +**Page:** `/ui/swap` with sidebar open + +- [ ] Theme color pickers work (primary, text, backgrounds) +- [ ] Border radius slider updates ALL elements: widget, card, modal, input, dropdown, swap button, buttons +- [ ] Italic CTA toggle: ON → enabled buttons italic, disabled buttons normal +- [ ] Dark/Light mode toggle updates all theme variables +- [ ] Reset button clears all overrides +- [ ] Single chain mode toggle works +- [ ] Wallet VM type checkboxes work + +--- + +## Quick Reference: What Changed in Migration + +Components that had the most styling changes (highest regression risk): + +| Component | Risk | Why | +|-----------|------|-----| +| **Dialog** | High | Animation system rewritten (keyframes + bottom-sheet) | +| **Button** | High | All variants rewritten with CVA, CTA font-style logic changed | +| **TokenSelector** | High | Complex responsive layout, ChainFilter interactions | +| **SwapWidget** | Medium | Many nested styled components, swap arrow border | +| **FeeBreakdown** | Medium | Collapsible animation, tooltip positioning | +| **Input** | Medium | Focus ring changed from border to box-shadow | +| **Tooltip** | Medium | Dual-mode (hover/click) with different components | +| **SuggestedTokens** | Low | Pill styling, focus ring fix | +| **Dropdown** | Low | Simple Radix wrapper | +| **Pill/Switch** | Low | Minimal styling | + +--- + +## Recommended QA Assignment + +Split across 2 people to minimize total time: + +**Person A (Desktop focus):** +- Phase 1 (all flows, light mode) — ~45 min +- Phase 2 (dark mode sweep) — ~20 min +- Phase 4 (Firefox spot-check) — ~10 min +- Phase 5 (Customize sidebar) — ~15 min + +**Person B (Mobile focus):** +- Phase 1.1 on mobile (swap flow only) — ~20 min +- Phase 3 (full mobile sweep, Chrome + Safari) — ~30 min +- Phase 2 on mobile (dark mode) — ~15 min + +**Total: ~2.5 hours combined, ~1.5 hours per person** diff --git a/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx b/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx index cf7d0dfa7..efc61981d 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx @@ -400,7 +400,8 @@ const OnrampWidget: FC = ({
-
+
+ ) } From 778c35ea03ca643a2ae7eff6163fbdff9d0c3d88 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Mon, 2 Mar 2026 16:22:56 -0500 Subject: [PATCH 08/25] fixes --- packages/ui/src/components/widgets/FeeBreakdown.tsx | 13 ++----------- .../src/components/widgets/PriceImpactTooltip.tsx | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/ui/src/components/widgets/FeeBreakdown.tsx b/packages/ui/src/components/widgets/FeeBreakdown.tsx index ea161ab45..9f8991551 100644 --- a/packages/ui/src/components/widgets/FeeBreakdown.tsx +++ b/packages/ui/src/components/widgets/FeeBreakdown.tsx @@ -184,16 +184,7 @@ const FeeBreakdown: FC = ({ return ( @@ -314,7 +305,7 @@ const FeeBreakdown: FC = ({ - + = ({ href="https://docs.relay.link/references/api/api_core_concepts/fees#relay-fees" target="_blank" rel="noopener noreferrer" - className="relay-text-[color:var(--relay-colors-primary11)] relay-text-[12px]" + className="relay-text-[color:var(--relay-colors-primary11)] !relay-text-[12px]" > Learn more about the fees From ec05df1363b16403f4d9cca2c9848a335f976301 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 10:24:16 -0500 Subject: [PATCH 09/25] feedback --- packages/ui/src/components/common/CustomAddressModal.tsx | 2 +- .../ui/src/components/common/SlippageToleranceConfig.tsx | 8 ++++---- .../components/common/TokenSelector/ChainShortcuts.tsx | 8 ++++---- .../ui/src/components/common/TokenSelector/TokenList.tsx | 6 +++--- .../common/TokenSelector/triggers/TokenTrigger.tsx | 2 +- packages/ui/src/components/primitives/Button.tsx | 2 +- packages/ui/src/components/primitives/Input.tsx | 2 +- packages/ui/src/components/primitives/Text.tsx | 6 +++++- packages/ui/src/components/primitives/Tooltip.tsx | 6 ++---- packages/ui/src/styles/base.css | 5 +++++ 10 files changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/ui/src/components/common/CustomAddressModal.tsx b/packages/ui/src/components/common/CustomAddressModal.tsx index 7c8293e4d..d9e7ea3b3 100644 --- a/packages/ui/src/components/common/CustomAddressModal.tsx +++ b/packages/ui/src/components/common/CustomAddressModal.tsx @@ -210,7 +210,7 @@ export const CustomAddressModal: FC = ({ - -
- { - if (linkWalletPromise) { - linkWalletPromise.reject() - setLinkWalletPromise(undefined) - } - if (chain?.vmType === 'evm') { - setWalletFilter('EVM') - } else if (chain?.id === 792703809) { - setWalletFilter('SOL') - } else if (chain?.id === 8253038) { - setWalletFilter('BTC') - } else if (chain?.id === 9286185) { - setWalletFilter('ECLIPSE') - } else if (chain?.vmType === 'suivm') { - setWalletFilter('SUI') - } else if (chain?.vmType === 'tvm') { - setWalletFilter('TRON') - } else { - setWalletFilter(undefined) - } - const promise = new Promise((resolve, reject) => { - setLinkWalletPromise({ - resolve, - reject, - params: { - chain, - direction - } - }) - }) - setShowLinkNewWalletModal(true) - return promise - }} - onSetPrimaryWallet={async (address: string) => { - //In some cases there's a race condition between connecting the wallet and having it available to switch to so we need to poll for it - const maxAttempts = 20 - let attemptCount = 0 - const timer = setInterval(async () => { - attemptCount++ - const newPrimaryWallet = wallets.current?.find( - (wallet) => - wallet.address === address || - wallet.additionalAddresses.find( - (_address) => _address.address === address - ) - ) - if (attemptCount >= maxAttempts) { - clearInterval(timer) - return - } - if (!newPrimaryWallet || !switchWallet.current) { - return - } - try { - await switchWallet.current(newPrimaryWallet?.id) - clearInterval(timer) - } catch (e) {} - }, 200) - }} - onConnectWallet={() => { - setShowAuthFlow(true) - }} - onAnalyticEvent={handleAnalyticEvent} - onFromTokenChange={(token) => { - setFromToken(token) - if (token) { - setTokenNotFound(false) - } - }} - onToTokenChange={(token) => { - setToToken(token) - if (token) { - setTokenNotFound(false) - } - }} - onSwapError={(e, data) => { - console.log('onSwapError Triggered', e, data) - }} - onSwapSuccess={(data) => { - console.log('onSwapSuccess Triggered', data) - }} - onUnverifiedTokenDecline={( - token: Token, - context: 'from' | 'to' - ) => { - console.log('User declined unverified token:', { - symbol: token.symbol, - address: token.address, - chainId: token.chainId, - context: context - }) - // Redirect to swap page - router.push('/ui/swap') - }} - slippageTolerance={undefined} - onOpenSlippageConfig={() => { - // setSlippageToleranceConfigOpen(true) - }} - /> - {tokenNotFound ? ( -
-

- {!isChainSupported && urlTokenChainId - ? `Chain ${urlTokenChainId} is not supported by Relay. Please select a token from a supported chain.` - : 'Token from URL was not found on the specified chain. Please select a token manually to continue.'} -

-
- ) : null} -
-
- - ) -} - -export const getServerSideProps: GetServerSideProps = async () => { - return { - props: { - ssr: true - } - } -} - -export default TokenWidgetPage diff --git a/demo/tsconfig.json b/demo/tsconfig.json index 259fff311..8698d42ea 100644 --- a/demo/tsconfig.json +++ b/demo/tsconfig.json @@ -35,9 +35,6 @@ "paths": { "@relayprotocol/relay-kit-ui/OnrampWidget": [ "./node_modules/@relayprotocol/relay-kit-ui/_esm/src/components/widgets/OnrampWidget/index.js" - ], - "@relayprotocol/relay-kit-ui/TokenWidget": [ - "./node_modules/@relayprotocol/relay-kit-ui/_esm/src/components/widgets/TokenWidget/index.js" ] } } \ No newline at end of file diff --git a/packages/ui/package.json b/packages/ui/package.json index 63636e52b..7d08633e7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -19,11 +19,6 @@ "import": "./_esm/src/components/widgets/OnrampWidget/index.js", "require": "./_cjs/src/components/widgets/OnrampWidget/index.js" }, - "./TokenWidget": { - "types": "./_types/src/components/widgets/TokenWidget/index.d.ts", - "import": "./_esm/src/components/widgets/TokenWidget/index.js", - "require": "./_cjs/src/components/widgets/TokenWidget/index.js" - }, ".": { "types": "./_types/src/index.d.ts", "import": "./_esm/src/index.js", diff --git a/packages/ui/src/components/common/SlippageToleranceConfig.tsx b/packages/ui/src/components/common/SlippageToleranceConfig.tsx index 8f48144c7..fe2c3a94a 100644 --- a/packages/ui/src/components/common/SlippageToleranceConfig.tsx +++ b/packages/ui/src/components/common/SlippageToleranceConfig.tsx @@ -45,7 +45,6 @@ type SlippageToleranceConfigProps = { onOpenSlippageConfig?: () => void showGearIcon?: boolean showLabel?: boolean - widgetType?: 'token' | 'swap' } type SlippageTabsProps = { @@ -59,7 +58,6 @@ type SlippageTabsProps = { slippageRating: string | undefined slippageRatingColor: string | undefined inputRef: React.RefObject - widgetType?: 'token' | 'swap' } const SlippageTabs: FC = ({ @@ -72,11 +70,9 @@ const SlippageTabs: FC = ({ handleClose, slippageRating, slippageRatingColor, - inputRef, - widgetType + inputRef }) => { const isMobile = useMediaQuery('(max-width: 520px)') - const isTokenWidget = widgetType === 'token' return ( = ({ setDisplayValue(undefined) } }} - className={cn( - 'relay-flex relay-flex-col relay-w-full', - isTokenWidget ? 'relay-gap-1' : 'relay-gap-3 sm:relay-gap-2' - )} + className="relay-flex relay-flex-col relay-w-full relay-gap-3 sm:relay-gap-2" > @@ -102,13 +95,9 @@ const SlippageTabs: FC = ({ We'll set the slippage automatically to minimize the failure rate. @@ -209,8 +198,7 @@ export const SlippageToleranceConfig: FC = ({ label = 'Slippage', onOpenSlippageConfig, showGearIcon = true, - showLabel = false, - widgetType + showLabel = false }) => { const isMobile = useMediaQuery('(max-width: 520px)') const [displayValue, setDisplayValue] = useState(() => @@ -321,40 +309,25 @@ export const SlippageToleranceConfig: FC = ({ }) } - const triggerButton = - widgetType === 'token' ? ( - - ) : ( - - ) + )} + + + ) const slippageTabsProps = { mode, @@ -366,8 +339,7 @@ export const SlippageToleranceConfig: FC = ({ handleClose, slippageRating, slippageRatingColor, - inputRef, - widgetType + inputRef } return ( diff --git a/packages/ui/src/components/widgets/TokenWidget/AmountModeToggle.tsx b/packages/ui/src/components/widgets/TokenWidget/AmountModeToggle.tsx deleted file mode 100644 index 0a079c148..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/AmountModeToggle.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { type ComponentPropsWithoutRef, type FC, type ReactNode } from 'react' -import { Flex, Text, Button } from '../../primitives/index.js' -import { SwitchIcon } from '../../../icons/index.js' - -type TextProps = Omit, 'children'> - -type AmountModeToggleProps = { - children: ReactNode - onToggle: () => void - textProps?: TextProps - buttonAriaLabel?: string -} - -const AmountModeToggle: FC = ({ - children, - onToggle, - textProps, - buttonAriaLabel = 'Switch Input Mode' -}) => { - const mergedTextProps: TextProps = { - style: 'subtitle3', - color: 'subtleSecondary', - ...textProps - } - - return ( - { - onToggle() - }} - > - {children} - - - ) -} - -export default AmountModeToggle diff --git a/packages/ui/src/components/widgets/TokenWidget/AmountSectionHeader.tsx b/packages/ui/src/components/widgets/TokenWidget/AmountSectionHeader.tsx deleted file mode 100644 index 2e9198c83..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/AmountSectionHeader.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { type FC } from 'react' -import { Flex, Text } from '../../primitives/index.js' -import { SlippageToleranceConfig } from '../../common/SlippageToleranceConfig.js' - -type AmountSectionHeaderProps = { - label: string - slippageTolerance?: string - onSlippageToleranceChange?: (value: string | undefined) => void - onAnalyticEvent?: (eventName: string, data?: any) => void - onOpenSlippageConfig?: () => void -} - -const AmountSectionHeader: FC = ({ - label, - slippageTolerance, - onSlippageToleranceChange, - onAnalyticEvent, - onOpenSlippageConfig -}) => ( - - {label} - {})} - onAnalyticEvent={onAnalyticEvent} - onOpenSlippageConfig={onOpenSlippageConfig} - widgetType="token" - /> - -) - -export default AmountSectionHeader diff --git a/packages/ui/src/components/widgets/TokenWidget/BuyTabContent.tsx b/packages/ui/src/components/widgets/TokenWidget/BuyTabContent.tsx deleted file mode 100644 index b1599c5ac..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/BuyTabContent.tsx +++ /dev/null @@ -1,606 +0,0 @@ -import { TabsContent } from '../../primitives/Tabs.js' -import { Flex, Text } from '../../primitives/index.js' -import AmountInput from '../../common/AmountInput.js' -import { cn } from '../../../utils/cn.js' -import { - formatFixedLength, - formatNumber, - formatDollar -} from '../../../utils/numbers.js' -import { PriceImpact } from '../SwapWidget/PriceImpact.js' -import { BalanceDisplay } from '../../common/BalanceDisplay.js' -import { MultiWalletDropdown } from '../../common/MultiWalletDropdown.js' -import PaymentMethod from '../../common/TokenSelector/PaymentMethod.js' -import { EventNames } from '../../../constants/events.js' -import { isChainLocked } from '../../../utils/tokenSelector.js' -import { useState } from 'react' -import type { Dispatch, FC, SetStateAction } from 'react' -import type { TradeType, ChildrenProps } from './widget/TokenWidgetRenderer.js' -import type { Token, LinkedWallet } from '../../../types/index.js' -import type { RelayChain } from '@relayprotocol/relay-sdk' -import { isDeadAddress, tronDeadAddress } from '@relayprotocol/relay-sdk' -import SwapButton from '../SwapButton.js' -import { PaymentMethodTrigger } from '../../common/TokenSelector/triggers/PaymentMethodTrigger.js' -import AmountSectionHeader from './AmountSectionHeader.js' -import AmountModeToggle from './AmountModeToggle.js' -import TransactionDetailsFooter from './TransactionDetailsFooter.js' -import SectionContainer from './SectionContainer.js' -import { WidgetErrorWell } from '../WidgetErrorWell.js' -import { FeeBreakdownInfo } from './FeeBreakdownInfo.js' -import { DestinationWalletSelector } from './DestinationWalletSelector.js' - -type LinkNewWalletHandler = (params: { - chain?: RelayChain - direction: 'to' | 'from' -}) => Promise | void - -type ChildrenPropsSubset = Pick< - ChildrenProps, - | 'quote' - | 'isFetchingQuote' - | 'isLoadingToTokenPrice' - | 'toTokenPriceData' - | 'setTradeType' - | 'setAmountOutputValue' - | 'setAmountInputValue' - | 'debouncedAmountOutputControls' - | 'feeBreakdown' - | 'isLoadingFromBalance' - | 'fromBalance' - | 'fromBalancePending' - | 'toBalance' - | 'isLoadingToBalance' - | 'toBalancePending' - | 'address' - | 'timeEstimate' - | 'toChainWalletVMSupported' - | 'recipient' - | 'setCustomToAddress' - | 'setDestinationAddressOverride' - | 'isValidToAddress' - | 'isRecipientLinked' - | 'toDisplayName' - | 'isValidFromAddress' - | 'fromChainWalletVMSupported' - | 'supportedWalletVMs' - | 'transactionModalOpen' - | 'hasInsufficientBalance' - | 'isInsufficientLiquidityError' - | 'recipientWalletSupportsChain' - | 'isSameCurrencySameRecipientSwap' - | 'debouncedInputAmountValue' - | 'debouncedOutputAmountValue' - | 'error' - | 'relayerFeeProportion' - | 'highRelayerServiceFee' - | 'isCapacityExceededError' - | 'isCouldNotExecuteError' - | 'ctaCopy' -> - -type BuyTabContentProps = ChildrenPropsSubset & { - // Slippage configuration - slippageTolerance?: string - onOpenSlippageConfig?: () => void - onSlippageToleranceChange?: (value: string | undefined) => void - - // Input/output state - isUsdInputMode: boolean - usdOutputValue: string - tradeType: TradeType - amountOutputValue: string - amountInputValue: string - outputAmountUsd: number | null - setUsdOutputValue: (value: string) => void - setUsdInputValue: (value: string) => void - - // Tokens - toToken?: Token - fromToken?: Token - handleSetFromToken: (token?: Token) => void - handleSetToToken: (token?: Token) => void - - // Wallet and address management - multiWalletSupportEnabled: boolean - linkedWallets?: LinkedWallet[] - onSetPrimaryWallet?: (address: string) => void - setOriginAddressOverride: Dispatch> - setAddressModalOpen: Dispatch> - disablePasteWalletAddressOption?: boolean - - // Chain configuration - toChain?: RelayChain - fromChain?: RelayChain - lockToToken: boolean - lockFromToken: boolean - isSingleChainLocked: boolean - lockChainId?: number - popularChainIds?: number[] - - // Modal states - depositAddressModalOpen: boolean - - // UI state and actions - showHighPriceImpactWarning: boolean - disableSwapButton?: boolean - toggleInputMode: () => void - onPrimaryAction: () => void - - // Event handlers - onAnalyticEvent?: (eventName: string, data?: any) => void - onConnectWallet?: () => void - onLinkNewWallet?: LinkNewWalletHandler - - // Additional props not covered by ChildrenProps - recipientLinkedWallet?: LinkedWallet - toChainVmType?: string - - // Payment method configuration - paymentMethodMinHeight?: string -} - -const BuyTabContent: FC = ({ - slippageTolerance, - onOpenSlippageConfig, - onSlippageToleranceChange, - isUsdInputMode, - usdOutputValue, - tradeType, - amountOutputValue, - amountInputValue, - toToken, - fromToken, - quote, - isFetchingQuote, - isLoadingToTokenPrice, - outputAmountUsd, - toTokenPriceData, - setUsdOutputValue, - setTradeType, - setAmountOutputValue, - setAmountInputValue, - setUsdInputValue, - debouncedAmountOutputControls, - onAnalyticEvent, - feeBreakdown, - isLoadingFromBalance, - fromBalance, - fromBalancePending, - toBalance, - isLoadingToBalance, - toBalancePending, - address, - timeEstimate, - multiWalletSupportEnabled, - toChainWalletVMSupported, - fromChain, - disablePasteWalletAddressOption, - recipient, - setCustomToAddress, - setDestinationAddressOverride, - onConnectWallet, - onLinkNewWallet, - linkedWallets, - toChain, - isValidToAddress, - isRecipientLinked, - setAddressModalOpen, - toDisplayName, - isValidFromAddress, - fromChainWalletVMSupported, - supportedWalletVMs, - handleSetFromToken, - handleSetToToken, - onSetPrimaryWallet, - setOriginAddressOverride, - lockToToken, - lockFromToken, - isSingleChainLocked, - lockChainId, - popularChainIds, - transactionModalOpen, - depositAddressModalOpen, - hasInsufficientBalance, - isInsufficientLiquidityError, - recipientWalletSupportsChain, - isSameCurrencySameRecipientSwap, - debouncedInputAmountValue, - debouncedOutputAmountValue, - showHighPriceImpactWarning, - disableSwapButton, - toggleInputMode, - onPrimaryAction, - error, - relayerFeeProportion, - highRelayerServiceFee, - isCapacityExceededError, - isCouldNotExecuteError, - recipientLinkedWallet, - toChainVmType, - ctaCopy, - paymentMethodMinHeight = '85vh' -}) => { - const [isPaymentMethodOpen, setIsPaymentMethodOpen] = useState(false) - const displayCta = [ - 'Swap', - 'Confirm', - 'Bridge', - 'Send', - 'Wrap', - 'Unwrap' - ].includes(ctaCopy) - ? 'Buy' - : ctaCopy - - const fromChainId = fromToken?.chainId - const lockedChainIds = isSingleChainLocked - ? lockChainId !== undefined - ? [lockChainId] - : undefined - : isChainLocked( - fromChainId, - lockChainId, - toToken?.chainId, - lockFromToken - ) && fromChainId !== undefined - ? [fromChainId] - : undefined - - const hasSelectedTokens = Boolean(fromToken && toToken) - const invalidAmount = - !quote || - Number(debouncedInputAmountValue) === 0 || - Number(debouncedOutputAmountValue) === 0 || - !hasSelectedTokens - - const hasValidOutputAmount = - toToken && amountOutputValue && Number(amountOutputValue) > 0 - - const currencyInAmountUsd = quote?.details?.currencyIn?.amountUsd - const currencyInAmountFormatted = quote?.details?.currencyIn?.amountFormatted - - const isLoadingPayWith = - hasValidOutputAmount && isFetchingQuote && fromToken && !currencyInAmountUsd - - const isSameTokenSameRecipient = - fromToken?.address === toToken?.address && - fromToken?.chainId === toToken?.chainId && - address === recipient - - const isInputDisabled = - !toToken || !fromChainWalletVMSupported || isSameTokenSameRecipient - - return ( - - - - - { - if (isUsdInputMode) { - setUsdOutputValue(value) - setTradeType('EXPECTED_OUTPUT') - if (Number(value) === 0) { - setAmountInputValue('') - setUsdInputValue('') - debouncedAmountOutputControls.flush() - } - } else { - setAmountOutputValue(value) - setTradeType('EXPECTED_OUTPUT') - if (Number(value) === 0) { - setAmountInputValue('') - debouncedAmountOutputControls.flush() - } - } - }} - disabled={isInputDisabled} - onClick={() => { - onAnalyticEvent?.(EventNames.SWAP_OUTPUT_FOCUSED) - }} - className={cn( - 'relay-font-bold relay-text-[32px] relay-leading-[32px] relay-py-0', - 'disabled:relay-cursor-not-allowed disabled:relay-text-[color:var(--relay-colors-gray10)] disabled:placeholder:relay-text-[color:var(--relay-colors-gray10)]', - isFetchingQuote && tradeType === 'EXACT_INPUT' - ? 'relay-text-[color:var(--relay-colors-text-subtle)] placeholder:relay-text-[color:var(--relay-colors-text-subtle)]' - : 'relay-text-[color:var(--relay-colors-input-color)] placeholder:relay-text-[color:var(--relay-colors-input-color)]' - )} - /> - - - - - {isUsdInputMode - ? toToken - ? usdOutputValue && Number(usdOutputValue) > 0 - ? amountOutputValue && !isLoadingToTokenPrice - ? `${formatNumber(amountOutputValue, 4, false)} ${toToken.symbol}` - : '...' - : `0 ${toToken.symbol}` - : null - : toToken && - quote?.details?.currencyOut?.amountUsd && - !isFetchingQuote - ? formatDollar(Number(quote.details.currencyOut.amountUsd)) - : toToken && - isLoadingToTokenPrice && - amountOutputValue && - Number(amountOutputValue) > 0 - ? '...' - : toToken && - outputAmountUsd && - outputAmountUsd > 0 && - toTokenPriceData?.price && - toTokenPriceData.price > 0 - ? formatDollar(outputAmountUsd) - : '$0.00'} - - - - - {toToken ? ( - - ) : ( - - )} - - - -
- - - - Pay with - - {multiWalletSupportEnabled && fromChainWalletVMSupported ? ( - { - setOriginAddressOverride(wallet.address) - onSetPrimaryWallet?.(wallet.address) - - if (wallet.address !== address) { - handleSetFromToken(undefined) - } - }} - chain={fromChain} - disableWalletFiltering={true} - onLinkNewWallet={() => { - if (!address && fromChainWalletVMSupported) { - onConnectWallet?.() - } else { - onLinkNewWallet?.({ - chain: fromChain, - direction: 'from' - })?.then((wallet) => { - if (wallet) { - setOriginAddressOverride(wallet.address) - onSetPrimaryWallet?.(wallet.address) - } - }) - } - }} - setAddressModalOpen={setAddressModalOpen} - wallets={linkedWallets ?? []} - onAnalyticEvent={onAnalyticEvent} - testId="origin-wallet-select-button" - /> - ) : null} - - - - { - handleSetFromToken(token) - }} - lockedChainIds={lockedChainIds} - chainIdsFilter={ - !fromChainWalletVMSupported && toToken - ? [toToken.chainId] - : undefined - } - popularChainIds={popularChainIds} - onPaymentMethodOpenChange={setIsPaymentMethodOpen} - trigger={ -
- -
- } - /> - - - -
- -
- - - { - setDestinationAddressOverride(wallet.address) - setCustomToAddress(undefined) - }, - chain: toChain, - disableWalletFiltering: false, - onLinkNewWallet: () => { - if (!address && toChainWalletVMSupported) { - onConnectWallet?.() - } else { - onLinkNewWallet?.({ - chain: toChain, - direction: 'to' - })?.then((wallet) => { - if (!wallet) { - return - } - setDestinationAddressOverride(wallet.address) - setCustomToAddress(undefined) - }) - } - }, - setAddressModalOpen, - wallets: linkedWallets ?? [], - onAnalyticEvent, - testId: 'destination-wallet-select-button' - }} - fallback={{ - highlighted: Boolean( - isValidToAddress && - multiWalletSupportEnabled && - !isRecipientLinked - ), - text: !isValidToAddress ? 'Enter Address' : (toDisplayName ?? ''), - onClick: () => { - setDestinationAddressOverride(undefined) - setAddressModalOpen(true) - onAnalyticEvent?.(EventNames.SWAP_ADDRESS_MODAL_CLICKED) - }, - showClipboard: Boolean( - isValidToAddress && - multiWalletSupportEnabled && - !isRecipientLinked - ) - }} - /> - - - - - { - onAnalyticEvent?.('TOKEN_BUY_CLICKED', { - token: toToken, - amount: amountOutputValue - }) - onPrimaryAction() - }} - onConnectWallet={onConnectWallet} - onAnalyticEvent={onAnalyticEvent} - /> - - - - - -
-
- ) -} - -export default BuyTabContent diff --git a/packages/ui/src/components/widgets/TokenWidget/DestinationWalletSelector.tsx b/packages/ui/src/components/widgets/TokenWidget/DestinationWalletSelector.tsx deleted file mode 100644 index 38bb954ca..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/DestinationWalletSelector.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { FC } from 'react' -import { Flex, Text, Button, Box } from '../../primitives/index.js' -import { - MultiWalletDropdown, - type MultiWalletDropdownProps -} from '../../common/MultiWalletDropdown.js' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faClipboard } from '@fortawesome/free-solid-svg-icons' - -type DestinationWalletSelectorProps = { - label: string - isMultiWalletEnabled: boolean - walletSupported: boolean - dropdownProps: Omit - fallback: { - highlighted: boolean - text: string - onClick: () => void - showClipboard?: boolean - } -} - -export const DestinationWalletSelector: FC = ({ - label, - isMultiWalletEnabled, - walletSupported, - dropdownProps, - fallback -}) => { - return ( - - - {label} - - {isMultiWalletEnabled && walletSupported ? ( - - ) : ( - - )} - - ) -} - -export type { DestinationWalletSelectorProps } diff --git a/packages/ui/src/components/widgets/TokenWidget/FeeBreakdownInfo.tsx b/packages/ui/src/components/widgets/TokenWidget/FeeBreakdownInfo.tsx deleted file mode 100644 index d0f20b1e8..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/FeeBreakdownInfo.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import type { FC } from 'react' -import { Box, Flex, Text } from '../../primitives/index.js' -import Skeleton from '../../primitives/Skeleton.js' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faInfoCircle } from '@fortawesome/free-solid-svg-icons' -import { formatDollarCompact, formatNumber } from '../../../utils/numbers.js' -import { FeeBreakdownTooltip } from './FeeBreakdownTooltip.js' -import type { QuoteResponse } from '@relayprotocol/relay-kit-hooks' -import type { FeeBreakdown } from '../../../types/FeeBreakdown.js' -import type { Token } from '../../../types/index.js' - -type FeeBreakdownInfoProps = { - isLoading: boolean - amountUsd?: string - tokenAmountFormatted?: string - fallbackTokenAmount?: string - quote?: QuoteResponse - feeBreakdown?: FeeBreakdown | null - token?: Token -} - -export const FeeBreakdownInfo: FC = ({ - isLoading, - amountUsd, - tokenAmountFormatted, - fallbackTokenAmount, - quote, - feeBreakdown, - token -}) => { - return ( - - - {isLoading ? ( - - ) : amountUsd && Number(amountUsd) > 0 ? ( - <> - - {formatDollarCompact(Number(amountUsd))} total - - - - - - - - ) : token ? ( - - $0 total - - ) : ( - - - total - - )} - - {isLoading ? ( - - ) : amountUsd && Number(amountUsd) > 0 ? ( - token && tokenAmountFormatted && Number(tokenAmountFormatted) > 0 ? ( - - {formatNumber(tokenAmountFormatted, 4, true)} {token.symbol} - - ) : token && fallbackTokenAmount && Number(fallbackTokenAmount) > 0 ? ( - - {formatNumber(fallbackTokenAmount, 4, true)} {token.symbol} - - ) : null - ) : token ? ( - - 0.00 {token.symbol} - - ) : null} - - ) -} diff --git a/packages/ui/src/components/widgets/TokenWidget/FeeBreakdownTooltip.tsx b/packages/ui/src/components/widgets/TokenWidget/FeeBreakdownTooltip.tsx deleted file mode 100644 index 822197b72..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/FeeBreakdownTooltip.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import type { FC, ReactNode } from 'react' -import { Flex, Text } from '../../primitives/index.js' -import Tooltip from '../../primitives/Tooltip.js' -import type { QuoteResponse } from '@relayprotocol/relay-kit-hooks' -import type { FeeBreakdown } from '../../../types/FeeBreakdown.js' -import { formatDollar, formatNumber } from '../../../utils/numbers.js' - -type FeeBreakdownTooltipProps = { - quote?: QuoteResponse - feeBreakdown?: FeeBreakdown - fromToken?: { symbol: string } - children: ReactNode - tooltipProps?: any -} - -export const FeeBreakdownTooltip: FC = ({ - quote, - feeBreakdown, - fromToken, - children, - tooltipProps -}) => { - const currencyInAmount = quote?.details?.currencyIn?.amountUsd - const currencyInAmountFormatted = quote?.details?.currencyIn?.amountFormatted - const expandedPriceImpact = quote?.details?.expandedPriceImpact - - // Relay fee - const relayFee = feeBreakdown?.breakdown?.find( - (fee) => fee.id === 'relayer-fee' - ) - const relayFeeUsd = - relayFee?.usd.value ?? - (expandedPriceImpact?.relay?.usd !== undefined - ? Number(expandedPriceImpact.relay.usd) - : undefined) - - // Swap impact - const swapImpactUsd = - feeBreakdown?.totalFees?.swapImpact?.value ?? - (expandedPriceImpact?.swap?.usd !== undefined - ? Number(expandedPriceImpact.swap.usd) - : undefined) - - // Execution fee - const executionFee = feeBreakdown?.breakdown?.find( - (fee) => fee.id === 'destination-gas' - ) - const executionFeeUsd = - executionFee?.usd.value ?? - (expandedPriceImpact?.execution?.usd !== undefined - ? Number(expandedPriceImpact.execution.usd) - : undefined) - - const executionFeeLabel = executionFee?.name ?? 'Execution Fee' - - // App fee - const appFee = feeBreakdown?.breakdown?.find((fee) => fee.id === 'app-fee') - const appFeeUsd = - appFee?.usd.value ?? - (expandedPriceImpact?.app?.usd !== undefined - ? Number(expandedPriceImpact.app.usd) - : undefined) - - const tokenAmountFormatted = formatDollar( - currencyInAmount !== undefined ? Number(currencyInAmount) : undefined - ) - const relayFeeFormatted = formatDollar( - relayFeeUsd !== undefined ? Math.abs(relayFeeUsd) : undefined - ) - const swapImpactFormatted = formatDollar( - swapImpactUsd !== undefined ? Math.abs(swapImpactUsd) : undefined - ) - const executionFeeFormatted = formatDollar( - executionFeeUsd !== undefined ? Math.abs(executionFeeUsd) : undefined - ) - const appFeeFormatted = - appFee?.usd.formatted ?? - formatDollar(appFeeUsd !== undefined ? Math.abs(appFeeUsd) : undefined) - - return ( - - {/* Token Amount Row */} - {fromToken && currencyInAmount && tokenAmountFormatted !== '-' && ( - - - {currencyInAmountFormatted && currencyInAmountFormatted !== '-' - ? `${formatNumber(currencyInAmountFormatted, 4, true)} ${fromToken.symbol}` - : fromToken.symbol} - - {tokenAmountFormatted} - - )} - - {/* Relay Fee Row */} - {relayFeeUsd !== undefined && relayFeeFormatted !== '-' && ( - - - {relayFee?.name ?? 'Relay Fee'} - - {feeBreakdown?.isGasSponsored && relayFeeUsd === 0 ? ( - - Free - - ) : ( - {relayFeeFormatted} - )} - - )} - - {/* Swap Impact Row */} - {swapImpactUsd !== undefined && swapImpactFormatted !== '-' && ( - - - Swap Impact - - {swapImpactFormatted} - - )} - - {/* Execution Fee Row */} - {executionFeeUsd !== undefined && executionFeeFormatted !== '-' && ( - - - {executionFeeLabel} - - {feeBreakdown?.isGasSponsored && executionFeeUsd === 0 ? ( - - Free - - ) : ( - {executionFeeFormatted} - )} - - )} - - {/* App Fee Row */} - {appFee && appFeeUsd !== undefined && appFeeFormatted !== '-' && ( - - - {appFee.name} - - {feeBreakdown?.isGasSponsored && appFeeUsd === 0 ? ( - - Free - - ) : ( - {appFeeFormatted} - )} - - )} - - } - {...tooltipProps} - > - {children} - - ) -} diff --git a/packages/ui/src/components/widgets/TokenWidget/SectionContainer.tsx b/packages/ui/src/components/widgets/TokenWidget/SectionContainer.tsx deleted file mode 100644 index 4c6995b8d..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/SectionContainer.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { type FC, type PropsWithChildren } from 'react' -import { Flex } from '../../primitives/index.js' -import { cn } from '../../../utils/cn.js' - -type SectionContainerProps = PropsWithChildren & { - className?: string - id?: string - isPaymentMethodOpen?: boolean - paymentMethodMinHeight?: string -} - -const SectionContainer: FC = ({ - children, - className, - id, - isPaymentMethodOpen = false, - paymentMethodMinHeight = '85vh' -}) => { - return ( - - {children} - - ) -} - -export default SectionContainer diff --git a/packages/ui/src/components/widgets/TokenWidget/SellTabContent.tsx b/packages/ui/src/components/widgets/TokenWidget/SellTabContent.tsx deleted file mode 100644 index 7aa5198e8..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/SellTabContent.tsx +++ /dev/null @@ -1,717 +0,0 @@ -import { TabsContent } from '../../primitives/Tabs.js' -import { Flex, Box } from '../../primitives/index.js' -import AmountInput from '../../common/AmountInput.js' -import { cn } from '../../../utils/cn.js' -import { - formatFixedLength, - formatNumber, - formatDollar -} from '../../../utils/numbers.js' -import { EventNames } from '../../../constants/events.js' -import { MultiWalletDropdown } from '../../common/MultiWalletDropdown.js' -import PaymentMethod from '../../common/TokenSelector/PaymentMethod.js' -import { PaymentMethodTrigger } from '../../common/TokenSelector/triggers/PaymentMethodTrigger.js' -import { useMemo, useRef, useEffect, useState } from 'react' -import type { Dispatch, FC, SetStateAction } from 'react' -import type { TradeType, ChildrenProps } from './widget/TokenWidgetRenderer.js' -import type { Token, LinkedWallet } from '../../../types/index.js' -import { - isDeadAddress, - tronDeadAddress, - type RelayChain, - type ChainVM -} from '@relayprotocol/relay-sdk' -import SwapButton from '../SwapButton.js' -import { BalanceDisplay } from '../../common/BalanceDisplay.js' -import AmountSectionHeader from './AmountSectionHeader.js' -import AmountModeToggle from './AmountModeToggle.js' -import TransactionDetailsFooter from './TransactionDetailsFooter.js' -import SectionContainer from './SectionContainer.js' -import { WidgetErrorWell } from '../WidgetErrorWell.js' -import { FeeBreakdownInfo } from './FeeBreakdownInfo.js' -import { DestinationWalletSelector } from './DestinationWalletSelector.js' -import { PercentageButtons } from '../../common/PercentageButtons.js' -import type { PublicClient } from 'viem' - -type LinkNewWalletHandler = (params: { - chain?: RelayChain - direction: 'to' | 'from' -}) => Promise | void - -type SellChildrenPropsSubset = Pick< - ChildrenProps, - | 'quote' - | 'isFetchingQuote' - | 'fromTokenPriceData' - | 'isLoadingFromTokenPrice' - | 'setTradeType' - | 'setAmountInputValue' - | 'setAmountOutputValue' - | 'debouncedAmountInputControls' - | 'feeBreakdown' - | 'fromBalance' - | 'isLoadingFromBalance' - | 'toBalance' - | 'isLoadingToBalance' - | 'toBalancePending' - | 'hasInsufficientBalance' - | 'address' - | 'timeEstimate' - | 'fromBalancePending' - | 'fromChainWalletVMSupported' - | 'transactionModalOpen' - | 'isValidFromAddress' - | 'isValidToAddress' - | 'toChainWalletVMSupported' - | 'isInsufficientLiquidityError' - | 'recipientWalletSupportsChain' - | 'recipient' - | 'setCustomToAddress' - | 'setDestinationAddressOverride' - | 'isRecipientLinked' - | 'isSameCurrencySameRecipientSwap' - | 'debouncedInputAmountValue' - | 'debouncedOutputAmountValue' - | 'toDisplayName' - | 'error' - | 'relayerFeeProportion' - | 'highRelayerServiceFee' - | 'isCapacityExceededError' - | 'isCouldNotExecuteError' - | 'supportedWalletVMs' - | 'ctaCopy' -> - -type SellTabContentProps = SellChildrenPropsSubset & { - // Slippage configuration - slippageTolerance?: string - onOpenSlippageConfig?: () => void - onSlippageToleranceChange?: (value: string | undefined) => void - - // Input/output state - isUsdInputMode: boolean - usdInputValue: string - tradeType: TradeType - amountInputValue: string - amountOutputValue: string - conversionRate: number | null - inputAmountUsd: number | null - toggleInputMode: () => void - setUsdInputValue: (value: string) => void - setTokenInputCache: (value: string) => void - setUsdOutputValue: (value: string) => void - - // Tokens - fromToken?: Token - toToken?: Token - handleSetFromToken: (token?: Token) => void - handleSetToToken: (token?: Token) => void - - // Wallet and address management - multiWalletSupportEnabled: boolean - linkedWallets?: LinkedWallet[] - onSetPrimaryWallet?: (address: string) => void - setOriginAddressOverride: Dispatch> - setAddressModalOpen: Dispatch> - disablePasteWalletAddressOption?: boolean - - // Chain configuration - fromChain?: RelayChain - toChain?: RelayChain - lockToToken: boolean - lockFromToken: boolean - isSingleChainLocked: boolean - lockChainId?: number - popularChainIds?: number[] - - // Modal states - depositAddressModalOpen: boolean - - // UI state and actions - showHighPriceImpactWarning: boolean - disableSwapButton?: boolean - percentOptions?: number[] - onMaxAmountClicked?: ( - amount: bigint, - label: string, - feeBuffer?: bigint - ) => void - onPrimaryAction: () => void - publicClient?: PublicClient | null - isFromNative?: boolean - getFeeBufferAmount?: ( - vmType: ChainVM | undefined | null, - chainId: number | undefined | null, - balance: bigint, - publicClient: PublicClient | null - ) => Promise - - // Event handlers - onAnalyticEvent?: (eventName: string, data?: any) => void - onConnectWallet?: () => void - onLinkNewWallet?: LinkNewWalletHandler - - // Additional props not covered by ChildrenProps - recipientLinkedWallet?: LinkedWallet - toChainVmType?: string - - // Payment method configuration - paymentMethodMinHeight?: string -} - -const SellTabContent: FC = ({ - slippageTolerance, - onOpenSlippageConfig, - onSlippageToleranceChange, - isUsdInputMode, - usdInputValue, - tradeType, - amountInputValue, - amountOutputValue, - conversionRate, - fromToken, - toToken, - quote, - isFetchingQuote, - inputAmountUsd, - fromTokenPriceData, - isLoadingFromTokenPrice, - toggleInputMode, - setUsdInputValue, - setTradeType, - setTokenInputCache, - setAmountInputValue, - setAmountOutputValue, - setUsdOutputValue, - debouncedAmountInputControls, - onAnalyticEvent, - feeBreakdown, - fromBalance, - isLoadingFromBalance, - toBalance, - isLoadingToBalance, - toBalancePending, - hasInsufficientBalance, - address, - timeEstimate, - fromBalancePending, - multiWalletSupportEnabled, - fromChainWalletVMSupported, - disablePasteWalletAddressOption, - onSetPrimaryWallet, - setOriginAddressOverride, - fromChain, - toChain, - onConnectWallet, - onLinkNewWallet, - linkedWallets, - setAddressModalOpen, - transactionModalOpen, - depositAddressModalOpen, - isValidFromAddress, - isValidToAddress, - toChainWalletVMSupported, - isInsufficientLiquidityError, - recipientWalletSupportsChain, - recipient, - setCustomToAddress, - setDestinationAddressOverride, - isRecipientLinked, - isSameCurrencySameRecipientSwap, - debouncedInputAmountValue, - debouncedOutputAmountValue, - showHighPriceImpactWarning, - disableSwapButton, - percentOptions, - onMaxAmountClicked, - publicClient, - isFromNative, - getFeeBufferAmount, - onPrimaryAction, - toDisplayName, - error, - relayerFeeProportion, - highRelayerServiceFee, - isCapacityExceededError, - isCouldNotExecuteError, - recipientLinkedWallet, - toChainVmType, - supportedWalletVMs, - lockToToken, - lockFromToken, - isSingleChainLocked, - lockChainId, - popularChainIds, - handleSetFromToken, - handleSetToToken, - ctaCopy, - paymentMethodMinHeight = '85vh' -}) => { - const [isPaymentMethodOpen, setIsPaymentMethodOpen] = useState(false) - const selectedPaymentVmType = useMemo( - () => toChain?.vmType ?? toChainVmType, - [toChain, toChainVmType] - ) - const recipientVmType = recipientLinkedWallet?.vmType - - // Keep a ref to track the current toToken to avoid infinite loops - const toTokenRef = useRef(toToken) - toTokenRef.current = toToken - - const hasAutoSelectedDestination = useRef(false) - - // Auto-select the user's primary wallet as the destination for selling - useEffect(() => { - if ( - !hasAutoSelectedDestination.current && - multiWalletSupportEnabled && - address && - isValidFromAddress && - !recipient - ) { - setDestinationAddressOverride(address) - hasAutoSelectedDestination.current = true - } - }, [ - multiWalletSupportEnabled, - address, - isValidFromAddress, - recipient, - setDestinationAddressOverride - ]) - - // Smart auto-selection for destination token when selling to same wallet - useEffect(() => { - if ( - recipient === address && - fromToken && - !toToken && - isValidToAddress && - multiWalletSupportEnabled - ) { - // let user manually select the destination token - } - }, [ - recipient, - address, - fromToken, - toToken, - isValidToAddress, - multiWalletSupportEnabled - ]) - - const displayCta = [ - 'Swap', - 'Confirm', - 'Bridge', - 'Send', - 'Wrap', - 'Unwrap' - ].includes(ctaCopy) - ? 'Sell' - : ctaCopy - - const hasSelectedTokens = Boolean(fromToken) - const invalidAmount = - !quote || - Number(debouncedInputAmountValue) === 0 || - Number(debouncedOutputAmountValue) === 0 || - !hasSelectedTokens - - const chainIdsFilterForDestination = - !toChainWalletVMSupported && fromToken ? [fromToken.chainId] : undefined - - const hasValidInputAmount = - fromToken && amountInputValue && Number(amountInputValue) > 0 - - const currencyOutAmountUsd = quote?.details?.currencyOut?.amountUsd - const currencyOutAmountFormatted = - quote?.details?.currencyOut?.amountFormatted - - // Only show skeleton on initial load, not on subsequent fetches - const isLoadingOutput = - hasValidInputAmount && isFetchingQuote && toToken && !currencyOutAmountUsd - - return ( - - - - - { - if (isUsdInputMode) { - setUsdInputValue(value) - setTradeType('EXACT_INPUT') - setTokenInputCache('') - if (Number(value) === 0) { - setAmountOutputValue('') - setUsdOutputValue('') - debouncedAmountInputControls.flush() - } - } else { - setAmountInputValue(value) - setTradeType('EXACT_INPUT') - if (Number(value) === 0) { - setAmountOutputValue('') - debouncedAmountInputControls.flush() - } - } - }} - onClick={() => { - onAnalyticEvent?.(EventNames.SWAP_INPUT_FOCUSED) - }} - className={cn( - 'relay-font-bold relay-text-[32px] relay-leading-[32px] relay-py-0', - isFetchingQuote && tradeType === 'EXPECTED_OUTPUT' - ? 'relay-text-[color:var(--relay-colors-text-subtle)] placeholder:relay-text-[color:var(--relay-colors-text-subtle)]' - : 'relay-text-[color:var(--relay-colors-input-color)] placeholder:relay-text-[color:var(--relay-colors-input-color)]' - )} - /> - - - - - {isUsdInputMode - ? fromToken - ? usdInputValue && Number(usdInputValue) > 0 - ? amountInputValue && - conversionRate && - !isLoadingFromTokenPrice - ? `${formatNumber(amountInputValue, 4, false)} ${fromToken.symbol}` - : '...' - : `0 ${fromToken.symbol}` - : null - : quote?.details?.currencyIn?.amountUsd && !isFetchingQuote - ? formatDollar(Number(quote.details.currencyIn.amountUsd)) - : isLoadingFromTokenPrice && - amountInputValue && - Number(amountInputValue) > 0 - ? '...' - : inputAmountUsd && - inputAmountUsd > 0 && - fromTokenPriceData?.price && - fromTokenPriceData.price > 0 - ? formatDollar(inputAmountUsd) - : '$0.00'} - - - - - {multiWalletSupportEnabled === true ? ( - { - setOriginAddressOverride(wallet.address) - onSetPrimaryWallet?.(wallet.address) - }} - chain={fromChain} - disableWalletFiltering={false} - onLinkNewWallet={() => { - if (!address && fromChainWalletVMSupported) { - onConnectWallet?.() - } else { - onLinkNewWallet?.({ - chain: fromChain, - direction: 'from' - })?.then((wallet) => { - if (wallet) { - setOriginAddressOverride(wallet.address) - onSetPrimaryWallet?.(wallet.address) - } - }) - } - }} - setAddressModalOpen={setAddressModalOpen} - wallets={linkedWallets ?? []} - onAnalyticEvent={onAnalyticEvent} - testId="origin-wallet-select-button" - /> - ) : ( - - )} - - - {(() => { - const displayToken = fromToken || toToken - const displayBalance = fromToken - ? fromBalance - : toToken && recipient !== address - ? fromBalance - : toBalance - const displayBalancePending = fromToken - ? fromBalancePending - : toToken && recipient !== address - ? fromBalancePending - : toBalancePending - const isLoadingDisplayBalance = fromToken - ? isLoadingFromBalance - : toToken && recipient !== address - ? isLoadingFromBalance - : isLoadingToBalance - - return displayToken ? ( - - ) : ( - - ) - })()} - {/* Desktop Percentage Buttons - Hidden on Mobile */} - {fromBalance && fromBalance > 0n && onMaxAmountClicked ? ( - - - - ) : null} - - - - {/* Mobile Percentage Buttons - Hidden on Desktop */} - {fromBalance && fromBalance > 0n && onMaxAmountClicked ? ( - - - - ) : null} - - -
- - { - setDestinationAddressOverride(wallet.address) - setCustomToAddress(undefined) - handleSetToToken(undefined) - }, - chain: toChain, - disableWalletFiltering: true, - onLinkNewWallet: () => { - if (!address && toChainWalletVMSupported) { - onConnectWallet?.() - } else { - onLinkNewWallet?.({ - chain: toChain, - direction: 'to' - })?.then((wallet) => { - if (!wallet) return - setDestinationAddressOverride(wallet.address) - setCustomToAddress(undefined) - // Always reset payment method when linking new wallets (like buy tab) - handleSetToToken(undefined) - }) - } - }, - setAddressModalOpen, - wallets: linkedWallets ?? [], - onAnalyticEvent, - testId: 'destination-wallet-select-button' - }} - fallback={{ - highlighted: Boolean( - isValidToAddress && - multiWalletSupportEnabled && - !isRecipientLinked - ), - text: !isValidToAddress - ? 'Enter Address' - : (toDisplayName ?? recipient ?? 'Select wallet'), - onClick: () => { - setDestinationAddressOverride(undefined) - setAddressModalOpen(true) - onAnalyticEvent?.(EventNames.SWAP_ADDRESS_MODAL_CLICKED) - }, - showClipboard: Boolean( - isValidToAddress && - multiWalletSupportEnabled && - !isRecipientLinked - ) - }} - /> - - - - { - if ( - token?.address === fromToken?.address && - token?.chainId === fromToken?.chainId && - recipient === address - ) { - return - } - handleSetToToken(token) - }} - trigger={ -
- -
- } - /> - - - -
- -
- - - { - onAnalyticEvent?.('TOKEN_SELL_CLICKED', { - token: fromToken, - amount: amountInputValue - }) - onPrimaryAction() - }} - onConnectWallet={onConnectWallet} - onAnalyticEvent={onAnalyticEvent} - /> - - - - - -
-
- ) -} - -export default SellTabContent diff --git a/packages/ui/src/components/widgets/TokenWidget/TransactionDetailsFooter.tsx b/packages/ui/src/components/widgets/TokenWidget/TransactionDetailsFooter.tsx deleted file mode 100644 index 0c45583e0..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/TransactionDetailsFooter.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { type FC } from 'react' -import { Flex, Box, Text } from '../../primitives/index.js' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faClock, faGasPump } from '@fortawesome/free-solid-svg-icons' -import type { QuoteResponse } from '@relayprotocol/relay-kit-hooks' -import type { FeeBreakdown } from '../../../types/FeeBreakdown.js' -import { formatDollar } from '../../../utils/numbers.js' - -type TransactionDetailsFooterProps = { - timeEstimate?: { time: number; formattedTime: string } - feeBreakdown?: FeeBreakdown | null - quote?: QuoteResponse -} - -const TransactionDetailsFooter: FC = ({ - timeEstimate, - feeBreakdown, - quote -}) => { - const hasEstimate = timeEstimate !== undefined && (timeEstimate.time ?? 0) > 0 - const isLongEstimate = hasEstimate && (timeEstimate?.time ?? 0) >= 600 - const clockColor = hasEstimate - ? isLongEstimate - ? 'amber9' - : 'green9' - : 'gray9' - - const timeLabel = hasEstimate ? `~${timeEstimate?.formattedTime}` : '—' - - const originGas = feeBreakdown?.breakdown?.find( - (fee) => fee.id === 'origin-gas' - ) - const networkCostUsd = - originGas?.usd.value ?? - (quote?.fees?.gas?.amountUsd !== undefined - ? -Number(quote.fees.gas.amountUsd) - : undefined) - - const networkCostLabel = formatDollar( - networkCostUsd !== undefined ? Math.abs(networkCostUsd) : undefined - ) - - const hasNetworkCost = - networkCostUsd !== undefined && networkCostLabel !== '-' - const showDivider = timeEstimate && timeEstimate.time !== 0 && hasNetworkCost - - return ( - - {timeEstimate && timeEstimate.time !== 0 ? ( - <> - - - - - - {timeLabel} - - - {showDivider ? ( - - • - - ) : null} - - ) : null} - {hasNetworkCost ? ( - - - - - - {networkCostLabel} - - - ) : null} - - ) -} - -export default TransactionDetailsFooter diff --git a/packages/ui/src/components/widgets/TokenWidget/hooks/useWalletGuards.ts b/packages/ui/src/components/widgets/TokenWidget/hooks/useWalletGuards.ts deleted file mode 100644 index dd096852e..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/hooks/useWalletGuards.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { useEffect } from 'react' -import type { Dispatch, SetStateAction } from 'react' -import type { RelayChain } from '@relayprotocol/relay-sdk' -import type { LinkedWallet } from '../../../../types/index.js' -import type { RelayKitProviderProps } from '../../../../providers/RelayKitProvider.js' -import { - addressesEqual, - findSupportedWallet -} from '../../../../utils/address.js' - -type Params = { - multiWalletSupportEnabled: boolean - allowUnsupportedOrigin: boolean - allowUnsupportedRecipient: boolean - fromChain?: RelayChain - toChain?: RelayChain - address?: string - recipient?: string - linkedWallets?: LinkedWallet[] - connectorKeyOverrides?: RelayKitProviderProps['options']['vmConnectorKeyOverrides'] - onSetPrimaryWallet?: (address: string) => void - isValidFromAddress: boolean - isValidToAddress: boolean - setOriginAddressOverride: Dispatch> - setCustomToAddress: Dispatch> - disablePasteWalletAddressOption?: boolean - customToAddress?: string - originAddressOverride?: string - destinationAddressOverride?: string - setDestinationAddressOverride: Dispatch> -} - -export const useWalletGuards = ({ - multiWalletSupportEnabled, - allowUnsupportedOrigin, - allowUnsupportedRecipient, - fromChain, - toChain, - address, - recipient, - linkedWallets, - connectorKeyOverrides, - onSetPrimaryWallet, - isValidFromAddress, - isValidToAddress, - setOriginAddressOverride, - setCustomToAddress, - disablePasteWalletAddressOption, - customToAddress, - originAddressOverride, - destinationAddressOverride, - setDestinationAddressOverride -}: Params) => { - useEffect(() => { - if ( - !allowUnsupportedOrigin && - multiWalletSupportEnabled && - fromChain && - address && - linkedWallets && - !isValidFromAddress - ) { - const supportedAddress = findSupportedWallet( - fromChain, - address, - linkedWallets, - connectorKeyOverrides - ) - if (supportedAddress) { - setOriginAddressOverride(undefined) - onSetPrimaryWallet?.(supportedAddress) - } - } - - if ( - !allowUnsupportedRecipient && - multiWalletSupportEnabled && - toChain && - recipient && - linkedWallets && - !isValidToAddress - ) { - const supportedAddress = findSupportedWallet( - toChain, - recipient, - linkedWallets, - connectorKeyOverrides - ) - if (supportedAddress) { - setCustomToAddress(supportedAddress) - } else { - setCustomToAddress(undefined) - } - } - }, [ - allowUnsupportedOrigin, - allowUnsupportedRecipient, - multiWalletSupportEnabled, - fromChain?.id, - toChain?.id, - address, - recipient, - linkedWallets, - onSetPrimaryWallet, - isValidFromAddress, - isValidToAddress, - connectorKeyOverrides, - setOriginAddressOverride, - setCustomToAddress - ]) - - useEffect(() => { - if (!disablePasteWalletAddressOption || !customToAddress) { - return - } - - const isLinkedWallet = linkedWallets?.some((wallet) => - addressesEqual(wallet.vmType, wallet.address, customToAddress) - ) - - if (!isLinkedWallet) { - setCustomToAddress(undefined) - } - }, [ - disablePasteWalletAddressOption, - customToAddress, - linkedWallets, - setCustomToAddress - ]) - - useEffect(() => { - if ( - destinationAddressOverride && - customToAddress && - destinationAddressOverride !== customToAddress - ) { - setDestinationAddressOverride(undefined) - } - }, [ - destinationAddressOverride, - customToAddress, - setDestinationAddressOverride - ]) - - useEffect(() => { - if (!multiWalletSupportEnabled) { - if (originAddressOverride !== undefined) { - setOriginAddressOverride(undefined) - } - if (destinationAddressOverride !== undefined) { - setDestinationAddressOverride(undefined) - } - return - } - - if (originAddressOverride) { - const originExists = linkedWallets?.some((wallet) => - addressesEqual(wallet.vmType, wallet.address, originAddressOverride) - ) - - if (!originExists) { - setOriginAddressOverride(undefined) - } - } - - if (destinationAddressOverride) { - const destinationMatches = linkedWallets?.some((wallet) => - addressesEqual( - wallet.vmType, - wallet.address, - destinationAddressOverride - ) - ) - - if (!destinationMatches) { - setDestinationAddressOverride(undefined) - } - } - }, [ - multiWalletSupportEnabled, - linkedWallets, - originAddressOverride, - destinationAddressOverride, - setOriginAddressOverride, - setDestinationAddressOverride, - allowUnsupportedOrigin - ]) -} - -export type { Params as UseWalletGuardsParams } diff --git a/packages/ui/src/components/widgets/TokenWidget/index.ts b/packages/ui/src/components/widgets/TokenWidget/index.ts deleted file mode 100644 index d981eeb9f..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as TokenWidget } from './widget/index.js' -export type { TokenWidgetProps } from './widget/index.js' \ No newline at end of file diff --git a/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx b/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx deleted file mode 100644 index 617b7b00b..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx +++ /dev/null @@ -1,1244 +0,0 @@ -import type { Dispatch, FC, ReactNode, SetStateAction } from 'react' -import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { - useCurrencyBalance, - useRelayClient, - useDebounceState, - useWalletAddress, - useDisconnected, - usePreviousValueChange, - useIsWalletCompatible, - useFallbackState, - useDisplayName, - useLighterAccount, - useExplicitDeposit -} from '../../../../hooks/index.js' -import type { Address, WalletClient } from 'viem' -import { formatUnits, parseUnits } from 'viem' -import { useAccount, useWalletClient } from 'wagmi' -import { useCapabilities } from 'wagmi/experimental' -import type { Token } from '../../../../types/index.js' -import { useQueryClient } from '@tanstack/react-query' -import type { ChainVM, Execute } from '@relayprotocol/relay-sdk' -import { - calculatePriceTimeEstimate, - calculateRelayerFeeProportionUsd, - extractQuoteId, - getCurrentStep, - getSwapEventData, - isHighRelayerServiceFeeUsd, - parseFees -} from '../../../../utils/quote.js' -import { useQuote, useTokenPrice } from '@relayprotocol/relay-kit-hooks' -import { EventNames } from '../../../../constants/events.js' -import { ProviderOptionsContext } from '../../../../providers/RelayKitProvider.js' -import type { DebouncedState } from 'usehooks-ts' -import type { AdaptedWallet } from '@relayprotocol/relay-sdk' -import type { LinkedWallet } from '../../../../types/index.js' -import { - addressesEqual, - addressWithFallback, - isValidAddress, - isChainVmTypeSupported, - isWalletVmTypeCompatible -} from '../../../../utils/address.js' -import { adaptViemWallet } from '@relayprotocol/relay-sdk' -import { errorToJSON } from '../../../../utils/errors.js' -import { useSwapButtonCta } from '../../../../hooks/widget/useSwapButtonCta.js' -import { sha256 } from '../../../../utils/hashing.js' -import { get15MinuteInterval } from '../../../../utils/time.js' -import { isLighterAddress } from '../../../../utils/lighter.js' -import type { FeeBreakdown } from '../../../../types/FeeBreakdown.js' - -export type TradeType = 'EXACT_INPUT' | 'EXPECTED_OUTPUT' - -type TokenWidgetRendererProps = { - transactionModalOpen: boolean - setTransactionModalOpen: React.Dispatch> - depositAddressModalOpen: boolean - children: (props: ChildrenProps) => ReactNode - fromToken?: Token - setFromToken?: (token?: Token) => void - toToken?: Token - setToToken?: (token?: Token) => void - defaultToAddress?: Address - defaultAmount?: string - defaultTradeType?: TradeType - slippageTolerance?: string - context: 'Swap' | 'Deposit' | 'Withdraw' - wallet?: AdaptedWallet - linkedWallets?: LinkedWallet[] - multiWalletSupportEnabled?: boolean - supportedWalletVMs: Omit[] - onConnectWallet?: () => void - onAnalyticEvent?: (eventName: string, data?: any) => void - onSwapError?: (error: string, data?: Execute) => void - useSecureBaseUrl?: (parameters: Parameters['2']) => boolean -} - -export type ChildrenProps = { - quote?: ReturnType['data'] - steps: Execute['steps'] | null - setSteps: Dispatch> - swap: () => void - transactionModalOpen: boolean - details: null | Execute['details'] - feeBreakdown: FeeBreakdown | null - fromToken?: Token - setFromToken: Dispatch> - toToken?: Token - setToToken: Dispatch> - swapError: Error | null - error: Error | null - toDisplayName?: string - address?: Address | string - originAddressOverride?: Address | string - setOriginAddressOverride: Dispatch< - React.SetStateAction
- > - recipient?: Address | string - destinationAddressOverride?: Address | string - customToAddress?: Address | string - setCustomToAddress: Dispatch< - React.SetStateAction
- > - setDestinationAddressOverride: Dispatch< - React.SetStateAction
- > - tradeType: TradeType - setTradeType: Dispatch> - isSameCurrencySameRecipientSwap: boolean - allowUnsupportedOrigin: boolean - setAllowUnsupportedOrigin: Dispatch> - allowUnsupportedRecipient: boolean - setAllowUnsupportedRecipient: Dispatch> - amountInputValue: string - debouncedInputAmountValue: string - setAmountInputValue: (value: string) => void - debouncedAmountInputControls: DebouncedState<(value: string) => void> - amountOutputValue: string - debouncedOutputAmountValue: string - setAmountOutputValue: (value: string) => void - debouncedAmountOutputControls: DebouncedState<(value: string) => void> - toBalance?: bigint - toBalancePending?: boolean - fromBalance?: bigint - fromBalancePending?: boolean - isFetchingQuote: boolean - isLoadingToBalance: boolean - isLoadingFromBalance: boolean - highRelayerServiceFee: boolean - relayerFeeProportion: bigint - hasInsufficientBalance: boolean - isInsufficientLiquidityError?: boolean - isCapacityExceededError?: boolean - isCouldNotExecuteError?: boolean - ctaCopy: string - isFromNative: boolean - slippageTolerance?: string - timeEstimate?: { time: number; formattedTime: string } - isSvmSwap: boolean - isBvmSwap: boolean - isValidFromAddress: boolean - isValidToAddress: boolean - supportedWalletVMs: Omit[] - fromChainWalletVMSupported: boolean - toChainWalletVMSupported: boolean - isRecipientLinked?: boolean - recipientWalletSupportsChain?: boolean - quoteInProgress: null | Execute - setQuoteInProgress: Dispatch> - linkedWallet?: LinkedWallet - quoteParameters?: Parameters['2'] - invalidateBalanceQueries: () => void - invalidateQuoteQuery: () => void - setDetails: Dispatch> - setSwapError: Dispatch> - abortController: AbortController | null - fromTokenPriceData: ReturnType['data'] - isLoadingFromTokenPrice: boolean - toTokenPriceData: ReturnType['data'] - isLoadingToTokenPrice: boolean -} - -// shared query options for useTokenPrice -const tokenPriceQueryOptions = { - staleTime: 60 * 1000, // 1 minute - refetchInterval: 30 * 1000, // 30 seconds - refetchOnWindowFocus: false -} - -const TokenWidgetRenderer: FC = ({ - transactionModalOpen, - setTransactionModalOpen, - depositAddressModalOpen, - fromToken: _fromToken, - setFromToken: _setFromToken, - toToken: _toToken, - setToToken: _setToToken, - defaultToAddress, - defaultAmount, - defaultTradeType, - slippageTolerance, - context, - wallet, - multiWalletSupportEnabled = false, - linkedWallets, - supportedWalletVMs, - useSecureBaseUrl, - children, - onAnalyticEvent, - onSwapError -}) => { - const [fromToken, setFromToken] = useFallbackState( - _setFromToken ? _fromToken : undefined, - _setFromToken - ? [ - _fromToken, - _setFromToken as Dispatch> - ] - : undefined - ) - const [toToken, setToToken] = useFallbackState( - _setToToken ? _toToken : undefined, - _setToToken - ? [_toToken, _setToToken as Dispatch>] - : undefined - ) - const providerOptionsContext = useContext(ProviderOptionsContext) - const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides - const relayClient = useRelayClient() - const { connector } = useAccount() - const walletClient = useWalletClient() - const [customToAddress, setCustomToAddress] = useState< - Address | string | undefined - >(defaultToAddress) - const [destinationAddressOverride, setDestinationAddressOverride] = useState< - Address | string | undefined - >(undefined) - const [originAddressOverride, setOriginAddressOverride] = useState< - Address | string | undefined - >(undefined) - const [allowUnsupportedOrigin, setAllowUnsupportedOrigin] = - useState(false) - const [allowUnsupportedRecipient, setAllowUnsupportedRecipient] = - useState(false) - const defaultAddress = useWalletAddress(wallet, linkedWallets) - - const [tradeType, setTradeType] = useState<'EXACT_INPUT' | 'EXPECTED_OUTPUT'>( - defaultTradeType ?? 'EXACT_INPUT' - ) - const queryClient = useQueryClient() - const [steps, setSteps] = useState(null) - const [quoteInProgress, setQuoteInProgress] = useState(null) - const [waitingForSteps, setWaitingForSteps] = useState(false) - const [details, setDetails] = useState(null) - const [abortController, setAbortController] = - useState(null) - - const { - value: amountInputValue, - debouncedValue: debouncedInputAmountValue, - setValue: setAmountInputValue, - debouncedControls: debouncedAmountInputControls - } = useDebounceState( - !defaultTradeType || defaultTradeType === 'EXACT_INPUT' - ? (defaultAmount ?? '') - : '', - 500 - ) - const { - value: amountOutputValue, - debouncedValue: debouncedOutputAmountValue, - setValue: setAmountOutputValue, - debouncedControls: debouncedAmountOutputControls - } = useDebounceState( - defaultTradeType === 'EXPECTED_OUTPUT' ? (defaultAmount ?? '') : '', - 500 - ) - - const [swapError, setSwapError] = useState(null) - - const toChain = useMemo( - () => relayClient?.chains?.find((chain) => chain.id === toToken?.chainId), - [relayClient?.chains, toToken?.chainId] - ) - - const fromChain = useMemo( - () => relayClient?.chains?.find((chain) => chain.id === fromToken?.chainId), - [relayClient?.chains, fromToken?.chainId] - ) - - const fromChainWalletVMSupported = useMemo( - () => - isChainVmTypeSupported(fromChain?.vmType, supportedWalletVMs) || - fromChain?.id === 1337, - [fromChain?.vmType, fromChain?.id, supportedWalletVMs] - ) - - const toChainWalletVMSupported = useMemo( - () => isChainVmTypeSupported(toChain?.vmType, supportedWalletVMs), - [toChain?.vmType, supportedWalletVMs] - ) - - // Automatically select the correct wallet address based on fromToken's chain VM - // In sell mode (no fromChain), use toChain to find compatible wallet - const address = useMemo(() => { - if (!multiWalletSupportEnabled || !linkedWallets?.length) { - return defaultAddress - } - - if (originAddressOverride) { - return originAddressOverride - } - - const targetChain = fromChain || toChain - if (!targetChain) { - return defaultAddress - } - - const isCompatibleWallet = (wallet: LinkedWallet) => - isWalletVmTypeCompatible(wallet.vmType, targetChain.vmType) && - isValidAddress( - targetChain.vmType, - wallet.address, - targetChain.id, - wallet.connector, - connectorKeyOverrides - ) - - const activeLinkedWallet = linkedWallets.find((wallet) => - addressesEqual(wallet.vmType, wallet.address, defaultAddress) - ) - - if (activeLinkedWallet && isCompatibleWallet(activeLinkedWallet)) { - return activeLinkedWallet.address - } - - const compatibleWallet = linkedWallets.find(isCompatibleWallet) - - return compatibleWallet?.address || defaultAddress - }, [ - multiWalletSupportEnabled, - originAddressOverride, - fromChain, - toChain, - linkedWallets, - defaultAddress, - connectorKeyOverrides - ]) - - const defaultRecipient = useMemo(() => { - const _linkedWallet = linkedWallets?.find( - (linkedWallet) => - address === - (linkedWallet.vmType === 'evm' - ? linkedWallet.address.toLowerCase() - : linkedWallet.address) - ) - const _isValidToAddress = isValidAddress( - toChain?.vmType, - customToAddress ?? '', - toChain?.id, - !customToAddress && _linkedWallet?.address === address - ? _linkedWallet?.connector - : undefined, - connectorKeyOverrides - ) - - if ( - multiWalletSupportEnabled && - toChain && - linkedWallets && - !_isValidToAddress - ) { - // Find a compatible wallet, excluding the origin wallet - const supportedWallet = linkedWallets.find( - (wallet) => - wallet.address !== address && - isValidAddress( - toChain.vmType, - wallet.address, - toChain.id, - wallet.connector, - connectorKeyOverrides - ) - ) - - return supportedWallet?.address - } - - return undefined - }, [ - multiWalletSupportEnabled, - toChain, - customToAddress, - address, - linkedWallets, - setCustomToAddress, - connectorKeyOverrides - ]) - - const recipient = - destinationAddressOverride ?? - customToAddress ?? - defaultRecipient ?? - (!allowUnsupportedRecipient && address ? address : undefined) - - const { - value: fromBalance, - queryKey: fromBalanceQueryKey, - isLoading: isLoadingFromBalance, - isError: fromBalanceErrorFetching, - isDuneBalance: fromBalanceIsDune, - hasPendingBalance: fromBalancePending - } = useCurrencyBalance({ - chain: fromChain, - address: address, - currency: fromToken?.address ? (fromToken.address as Address) : undefined, - enabled: fromToken !== undefined, - refreshInterval: undefined, - wallet - }) - - const { - value: toBalance, - queryKey: toBalanceQueryKey, - isLoading: isLoadingToBalance, - isDuneBalance: toBalanceIsDune, - hasPendingBalance: toBalancePending - } = useCurrencyBalance({ - chain: toChain, - address: recipient, - currency: toToken?.address ? (toToken.address as Address) : undefined, - enabled: - toToken !== undefined && recipient !== undefined && recipient !== '', - refreshInterval: undefined, - wallet - }) - - const invalidateBalanceQueries = useCallback(() => { - const invalidatePeriodically = (invalidateFn: () => void) => { - let maxRefreshes = 4 - let refreshCount = 0 - const timer = setInterval(() => { - if (maxRefreshes === refreshCount) { - clearInterval(timer) - return - } - refreshCount++ - invalidateFn() - }, 3000) - } - - queryClient.invalidateQueries({ queryKey: ['useDuneBalances'] }) - - // Dune balances are sometimes stale, because of this we need to aggressively fetch them - // for a predetermined period to make sure we get back a fresh response - if (fromBalanceIsDune) { - invalidatePeriodically(() => { - queryClient.invalidateQueries({ queryKey: fromBalanceQueryKey }) - }) - } else { - queryClient.invalidateQueries({ queryKey: fromBalanceQueryKey }) - } - if (toBalanceIsDune) { - invalidatePeriodically(() => { - queryClient.invalidateQueries({ queryKey: toBalanceQueryKey }) - }) - } else { - queryClient.invalidateQueries({ queryKey: toBalanceQueryKey }) - } - }, [ - queryClient, - fromBalanceQueryKey, - toBalanceQueryKey, - toBalanceIsDune, - fromBalanceIsDune, - address - ]) - const { data: capabilities } = useCapabilities({ - query: { - enabled: - connector && - (connector.id === 'coinbaseWalletSDK' || connector.id === 'coinbase') - } - }) - const hasAuxiliaryFundsSupport = Boolean( - fromToken?.chainId - ? capabilities?.[fromToken?.chainId]?.auxiliaryFunds?.supported - : false - ) - - const isSvmSwap = fromChain?.vmType === 'svm' || toChain?.vmType === 'svm' - const isBvmSwap = fromChain?.vmType === 'bvm' || toChain?.vmType === 'bvm' - const linkedWallet = linkedWallets?.find( - (linkedWallet) => - address === - (linkedWallet.vmType === 'evm' - ? linkedWallet.address.toLowerCase() - : linkedWallet.address) || linkedWallet.address === address - ) - - const isRecipientLinked = - (recipient - ? linkedWallets?.find((wallet) => wallet.address === recipient) - : undefined) !== undefined - - const isValidFromAddress = useMemo( - () => - isValidAddress( - fromChain?.vmType, - address ?? '', - fromChain?.id, - linkedWallet?.connector, - connectorKeyOverrides - ), - [ - fromChain?.vmType, - address, - fromChain?.id, - linkedWallet?.connector, - connectorKeyOverrides - ] - ) - const fromAddressWithFallback = addressWithFallback( - fromChain?.vmType, - address, - fromChain?.id, - linkedWallet?.connector, - connectorKeyOverrides - ) - - const isValidToAddress = useMemo( - () => isValidAddress(toChain?.vmType, recipient ?? '', toChain?.id), - [toChain?.vmType, recipient, toChain?.id] - ) - - const toAddressWithFallback = addressWithFallback( - toChain?.vmType, - recipient, - toChain?.id - ) - - const { displayName: toDisplayName } = useDisplayName( - recipient, - toChain?.vmType, - toChain?.id - ) - - const [currentSlippageTolerance, setCurrentSlippageTolerance] = useState< - string | undefined - >(slippageTolerance) - - useEffect(() => { - setCurrentSlippageTolerance(slippageTolerance) - }, [slippageTolerance]) - - // Retrieve the price of the `from` token - const { data: fromTokenPriceData, isLoading: isLoadingFromTokenPrice } = - useTokenPrice( - relayClient?.baseApiUrl, - { - address: fromToken?.address ?? '', - chainId: fromToken?.chainId ?? 0, - referrer: relayClient?.source - }, - { - enabled: !!(fromToken?.address && fromToken.chainId), - ...tokenPriceQueryOptions - } - ) - - // Retrieve the price of the `to` token - const { data: toTokenPriceData, isLoading: isLoadingToTokenPrice } = - useTokenPrice( - relayClient?.baseApiUrl, - { - address: toToken?.address ?? '', - chainId: toToken?.chainId ?? 0, - referrer: relayClient?.source - }, - { - enabled: !!(toToken?.address && toToken.chainId), - ...tokenPriceQueryOptions - } - ) - - const isFromNative = fromToken?.address === fromChain?.currency?.address - - const explicitDeposit = useExplicitDeposit( - wallet, - fromToken?.chainId, - fromChain?.vmType, - address - ) - - const shouldSetQuoteParameters = fromToken && toToken - - const quoteParameters: Parameters['2'] = - shouldSetQuoteParameters - ? { - user: fromAddressWithFallback, - originChainId: fromToken.chainId, - destinationChainId: toToken.chainId, - originCurrency: fromToken.address, - destinationCurrency: toToken.address, - recipient: toAddressWithFallback, - tradeType, - appFees: providerOptionsContext.appFees, - amount: - tradeType === 'EXACT_INPUT' - ? parseUnits( - debouncedInputAmountValue, - fromToken.decimals - ).toString() - : parseUnits( - debouncedOutputAmountValue, - toToken.decimals - ).toString(), - referrer: relayClient?.source ?? undefined, - useDepositAddress: - !fromChainWalletVMSupported || fromToken?.chainId === 1337, - refundTo: fromToken?.chainId === 1337 ? address : undefined, - slippageTolerance: slippageTolerance, - ...(linkedWallet?.vmType === 'bvm' && wallet?.metadata?.publicKey - ? { - additionalData: { - userPublicKey: wallet?.metadata?.publicKey - } - } - : {}), - ...(explicitDeposit !== undefined && - fromChain?.vmType === 'evm' && { - explicitDeposit: explicitDeposit - }) - } - : undefined - - const onQuoteRequested: Parameters['3'] = ( - options, - config - ) => { - const interval = get15MinuteInterval() - const quoteRequestId = sha256({ ...options, interval }) - onAnalyticEvent?.(EventNames.QUOTE_REQUESTED, { - parameters: options, - wallet_connector: linkedWallet?.connector, - chain_id_in: options?.originChainId, - chain_id_out: options?.destinationChainId, - http_config: config, - quote_request_id: quoteRequestId - }) - } - - const onQuoteReceived: Parameters['4'] = ( - { details, steps }, - options - ) => { - const interval = get15MinuteInterval() - const quoteRequestId = sha256({ ...options, interval }) - onAnalyticEvent?.(EventNames.QUOTE_RECEIVED, { - parameters: options, - wallet_connector: linkedWallet?.connector, - amount_in: details?.currencyIn?.amountFormatted, - amount_in_raw: details?.currencyIn?.amount, - currency_in: details?.currencyIn?.currency?.symbol, - chain_id_in: details?.currencyIn?.currency?.chainId, - amount_out: details?.currencyOut?.amountFormatted, - amount_out_raw: details?.currencyOut?.amount, - currency_out: details?.currencyOut?.currency?.symbol, - chain_id_out: details?.currencyOut?.currency?.chainId, - slippage_tolerance_destination_percentage: - details?.slippageTolerance?.destination?.percent, - slippage_tolerance_origin_percentage: - details?.slippageTolerance?.origin?.percent, - steps, - quote_request_id: quoteRequestId, - quote_id: steps ? extractQuoteId(steps) : undefined - }) - } - - const quoteFetchingEnabled = Boolean( - relayClient && - ((tradeType === 'EXACT_INPUT' && - debouncedInputAmountValue && - debouncedInputAmountValue.length > 0 && - Number(debouncedInputAmountValue) !== 0) || - (tradeType === 'EXPECTED_OUTPUT' && - debouncedOutputAmountValue && - debouncedOutputAmountValue.length > 0 && - Number(debouncedOutputAmountValue) !== 0)) && - fromToken !== undefined && - toToken !== undefined && - !transactionModalOpen && - !depositAddressModalOpen - ) - - const quoteRefetchInterval = - !transactionModalOpen && - !depositAddressModalOpen && - debouncedInputAmountValue === amountInputValue && - debouncedOutputAmountValue === amountOutputValue - ? 12000 - : undefined - - const handleQuoteError = (e: any) => { - const errorMessage = errorToJSON( - e?.response?.data?.message ? new Error(e?.response?.data?.message) : e - ) - const interval = get15MinuteInterval() - const quoteRequestId = sha256({ ...quoteParameters, interval }) - onAnalyticEvent?.(EventNames.QUOTE_ERROR, { - wallet_connector: linkedWallet?.connector, - error_message: errorMessage, - parameters: quoteParameters, - quote_request_id: quoteRequestId, - status_code: e.response.status ?? e.status ?? '' - }) - } - - const { - data: quote, - error: quoteError, - isFetching: isFetchingQuote, - executeQuote: executeSwap, - queryKey: quoteQueryKey - } = useQuote( - relayClient ? relayClient : undefined, - wallet, - quoteParameters, - onQuoteRequested, - onQuoteReceived, - { - refetchOnWindowFocus: false, - enabled: Boolean(quoteFetchingEnabled && quoteParameters !== undefined), - refetchInterval: quoteRefetchInterval - }, - handleQuoteError, - undefined, - useSecureBaseUrl?.(quoteParameters) - ? providerOptionsContext?.secureBaseUrl - : undefined - ) - - const invalidateQuoteQuery = useCallback(() => { - queryClient.invalidateQueries({ queryKey: quoteQueryKey }) - }, [queryClient, quoteQueryKey]) - - const derivedError = - quote || - (isFetchingQuote && - Boolean(quoteFetchingEnabled && quoteParameters !== undefined)) - ? null - : quoteError - const error = derivedError - - useDisconnected(address, () => { - setCustomToAddress(undefined) - setOriginAddressOverride(undefined) - setDestinationAddressOverride(undefined) - }) - - // Auto-select Lighter account when switching to LVM chain - const isLighterChain = toChain?.vmType === 'lvm' - const { data: connectedLighterAccount } = useLighterAccount( - isLighterChain && address ? address : undefined - ) - - useEffect(() => { - if ( - isLighterChain && - connectedLighterAccount?.index && - // Only auto-set if no valid Lighter address is already set - (!customToAddress || !isLighterAddress(customToAddress)) - ) { - setCustomToAddress(connectedLighterAccount.index.toString()) - } - }, [isLighterChain, connectedLighterAccount, customToAddress]) - - useEffect(() => { - if (tradeType === 'EXACT_INPUT') { - const amountOut = quote?.details?.currencyOut?.amount ?? '' - setAmountOutputValue( - amountOut !== '' - ? formatUnits( - BigInt(amountOut), - Number(quote?.details?.currencyOut?.currency?.decimals ?? 18) - ) - : '' - ) - } else if (tradeType === 'EXPECTED_OUTPUT') { - const amountIn = quote?.details?.currencyIn?.amount ?? '' - setAmountInputValue( - amountIn !== '' - ? formatUnits( - BigInt(amountIn), - Number(quote?.details?.currencyIn?.currency?.decimals ?? 18) - ) - : '' - ) - } - debouncedAmountInputControls.flush() - debouncedAmountOutputControls.flush() - }, [quote, tradeType]) - - const feeBreakdown = useMemo(() => { - const chains = relayClient?.chains - const fromChain = chains?.find((chain) => chain.id === fromToken?.chainId) - const toChain = chains?.find((chain) => chain.id === toToken?.chainId) - return fromToken && toToken && fromChain && toChain && quote - ? parseFees(toChain, fromChain, quote) - : null - }, [quote, fromToken, toToken, relayClient]) - - const totalAmount = BigInt(quote?.details?.currencyIn?.amount ?? 0n) - - const hasInsufficientBalance = Boolean( - !fromBalanceErrorFetching && - totalAmount && - address && - (fromBalance ?? 0n) < totalAmount && - !hasAuxiliaryFundsSupport && - fromChainWalletVMSupported - ) - - const fetchQuoteErrorMessage = error - ? error?.message - ? (error?.message as string) - : 'Unknown Error' - : null - const fetchQuoteDataErrorMessage = error - ? (error as any)?.response?.data?.message - ? ((error as any)?.response?.data.message as string) - : 'Unknown Error' - : null - const isInsufficientLiquidityError = Boolean( - fetchQuoteErrorMessage?.includes('No quotes available') - ) - const isCapacityExceededError = - fetchQuoteDataErrorMessage?.includes( - 'Amount is higher than the available liquidity' - ) || fetchQuoteDataErrorMessage?.includes('Insufficient relayer liquidity') - const isCouldNotExecuteError = - fetchQuoteDataErrorMessage?.includes('Could not execute') - const highRelayerServiceFee = isHighRelayerServiceFeeUsd(quote) - const relayerFeeProportion = calculateRelayerFeeProportionUsd(quote) - const timeEstimate = calculatePriceTimeEstimate(quote?.details) - - const recipientWalletSupportsChain = useIsWalletCompatible( - toChain?.id, - recipient, - linkedWallets, - onAnalyticEvent - ) - - const isSameCurrencySameRecipientSwap = - fromToken?.address === toToken?.address && - fromToken?.chainId === toToken?.chainId && - address === recipient - - const ctaCopy = useSwapButtonCta({ - fromToken, - toToken, - multiWalletSupportEnabled, - isValidFromAddress, - fromChainWalletVMSupported, - isValidToAddress, - toChainWalletVMSupported, - fromChain, - toChain, - isSameCurrencySameRecipientSwap, - debouncedInputAmountValue, - debouncedOutputAmountValue, - hasInsufficientBalance, - isInsufficientLiquidityError, - quote, - operation: quote?.details?.operation - }) - - usePreviousValueChange( - isCapacityExceededError, - !isFetchingQuote, - (capacityExceeded) => { - if (capacityExceeded) { - onAnalyticEvent?.(EventNames.CTA_MAX_CAPACITY_PROMPTED, { - inputAmount: debouncedInputAmountValue, - outputAmount: debouncedOutputAmountValue - }) - } - } - ) - - const swap = useCallback(async () => { - let submittedEvents: string[] = [] - - const swapErrorHandler = ( - error: any, - currentSteps?: Execute['steps'] | null - ) => { - const errorMessage = errorToJSON( - error?.response?.data?.message - ? new Error(error?.response?.data?.message) - : error - ) - if ( - error && - ((typeof error.message === 'string' && - error.message.includes('rejected')) || - (typeof error === 'string' && error.includes('rejected')) || - (typeof error === 'string' && error.includes('Approval Denied')) || - (typeof error === 'string' && error.includes('denied transaction')) || - (typeof error.message === 'string' && - error.message.includes('Approval Denied')) || - (typeof error.message === 'string' && - error.message.includes('Plugin Closed')) || - (typeof error.message === 'string' && - error.message.includes('denied transaction')) || - (typeof error.message === 'string' && - error.message.includes('Failed to initialize request') && - fromChain?.id === 2741)) // Abstract @TODO: remove once privy improves handling rejected transactions - ) { - // Close the transaction modal if the user rejects the tx - setTransactionModalOpen(false) - onAnalyticEvent?.(EventNames.USER_REJECTED_WALLET, { - error_message: errorMessage - }) - return - } - - const { step, stepItem } = getCurrentStep(currentSteps) - const swapEventData = { - ...getSwapEventData( - quote?.details, - quote?.fees, - currentSteps ?? null, - linkedWallet?.connector, - quoteParameters - ), - error_message: errorMessage - } - const isApproval = step?.id === 'approve' - const errorEvent = isApproval - ? EventNames.APPROVAL_ERROR - : EventNames.DEPOSIT_ERROR - - //Filter out receipt/deposit transaction errors, those are approval/deposit errors - const isTransactionConfirmationError = - (error && - typeof error.message === 'string' && - error.message.includes('TransactionConfirmationError')) || - (error.name && error.name.includes('TransactionConfirmationError')) - if ( - stepItem?.receipt && - stepItem.check && - !isTransactionConfirmationError && - (typeof stepItem.receipt === 'object' && 'status' in stepItem.receipt - ? stepItem.receipt.status !== 'reverted' - : true) && - (!stepItem.checkStatus || stepItem.checkStatus !== 'unknown') - ) { - //In some cases there's a race condition where an error is thrown before the steps get a chance to call - //the callback which triggers the success event. This is a workaround to ensure the success event is triggered when - //we have a receipt and require a fill check if we haven't already send the success event. - const successEvent = isApproval - ? EventNames.APPROVAL_SUCCESS - : EventNames.DEPOSIT_SUCCESS - if (!submittedEvents.includes(successEvent)) { - onAnalyticEvent?.(successEvent, swapEventData) - submittedEvents.push(successEvent) - //To preserve the order of events we need to delay sending the fill error event but mark that we did send it to avoid duplicates - setTimeout(() => { - onAnalyticEvent?.(EventNames.FILL_ERROR, swapEventData) - }, 20) - } else { - onAnalyticEvent?.(EventNames.FILL_ERROR, swapEventData) - } - } else if ( - !stepItem?.receipt || - (typeof stepItem.receipt === 'object' && - 'status' in stepItem.receipt && - stepItem.receipt.status === 'reverted') - ) { - onAnalyticEvent?.(errorEvent, swapEventData) - } else { - onAnalyticEvent?.(EventNames.SWAP_ERROR, swapEventData) - } - - setSwapError(errorMessage) - onSwapError?.(errorMessage, { ...quote, steps: currentSteps } as Execute) - } - - try { - const swapEventData = getSwapEventData( - quote?.details, - quote?.fees, - quote?.steps ? (quote?.steps as Execute['steps']) : null, - linkedWallet?.connector, - quoteParameters - ) - onAnalyticEvent?.(EventNames.SWAP_CTA_CLICKED, swapEventData) - setWaitingForSteps(true) - - if (!executeSwap) { - throw 'Missing a quote' - } - - if (!wallet && !walletClient.data) { - throw 'Missing a wallet' - } - - setSteps(quote?.steps as Execute['steps']) - setQuoteInProgress(quote as Execute) - setTransactionModalOpen(true) - - const _wallet = - wallet ?? adaptViemWallet(walletClient.data as WalletClient) - - const activeWalletChainId = await _wallet?.getChainId() - const activeWalletChain = relayClient?.chains?.find( - (chain) => chain.id === activeWalletChainId - ) - let targetChainId = fromToken?.chainId - //Special case for Hyperliquid, to sign txs on an evm chain - if (fromToken?.chainId === 1337) { - targetChainId = - activeWalletChain?.vmType !== 'evm' ? 1 : activeWalletChainId - } - - if (fromToken && targetChainId && targetChainId !== activeWalletChainId) { - onAnalyticEvent?.(EventNames.SWAP_SWITCH_NETWORK, { - activeWalletChainId, - ...swapEventData - }) - await _wallet?.switchChain(targetChainId) - } - - let _currentSteps: Execute['steps'] | undefined = undefined - - const execPromise = executeSwap(({ steps: currentSteps }) => { - setSteps(currentSteps) - _currentSteps = currentSteps - - const { step, stepItem } = getCurrentStep(currentSteps) - const swapEventData = getSwapEventData( - quote?.details, - quote?.fees, - currentSteps, - linkedWallet?.connector, - quoteParameters - ) - if (step && stepItem) { - //@ts-ignore - const isApproval = step.id === 'approve' || step.id === 'approval' - let submittedEvent = isApproval - ? EventNames.APPROVAL_SUBMITTED - : EventNames.DEPOSIT_SUBMITTED - const successEvent = isApproval - ? EventNames.APPROVAL_SUCCESS - : EventNames.DEPOSIT_SUCCESS - const isBatchTransaction = Boolean( - Array.isArray(step.items) && - step.items.length > 1 && - wallet?.handleBatchTransactionStep - ) - if (!isApproval && isBatchTransaction) { - submittedEvent = EventNames.BATCH_TX_SUBMITTED - } - if ( - !submittedEvents.includes(submittedEvent) && - !stepItem.receipt && - stepItem?.txHashes && - stepItem?.txHashes?.length > 0 - ) { - submittedEvents.push(submittedEvent) - onAnalyticEvent?.(submittedEvent, swapEventData) - } else if ( - !submittedEvents.includes(successEvent) && - ((stepItem.receipt && - !( - typeof stepItem.receipt === 'object' && - 'status' in stepItem.receipt && - stepItem.receipt.status === 'reverted' - )) || - stepItem.checkStatus === 'pending') - ) { - onAnalyticEvent?.(successEvent, swapEventData) - submittedEvents.push(successEvent) - } - - if ( - stepItem.status === 'complete' && - stepItem.check && - !submittedEvents.includes(EventNames.FILL_SUCCESS) - ) { - //Sometimes a fill may be quicker than the tx receipt is available, so we need to handle this scenario - if ( - !submittedEvents.includes(EventNames.DEPOSIT_SUCCESS) && - !isBatchTransaction - ) { - onAnalyticEvent?.(EventNames.DEPOSIT_SUCCESS, swapEventData) - submittedEvents.push(EventNames.DEPOSIT_SUCCESS) - //To preserve the order of events we need to delay sending the fill success event but mark that we did send it to avoid duplicates - setTimeout(() => { - onAnalyticEvent?.(EventNames.FILL_SUCCESS, swapEventData) - }, 20) - } else { - onAnalyticEvent?.(EventNames.FILL_SUCCESS, swapEventData) - } - submittedEvents.push(EventNames.FILL_SUCCESS) - } - } else if ( - currentSteps?.every((step) => - step.items?.every((item) => item.status === 'complete') - ) && - !submittedEvents.includes(EventNames.FILL_SUCCESS) - ) { - //Sometimes a fill may be quicker than the tx receipt is available, so we need to handle this scenario - if ( - !submittedEvents.includes(EventNames.DEPOSIT_SUCCESS) && - !submittedEvents.includes(EventNames.BATCH_TX_SUBMITTED) - ) { - onAnalyticEvent?.(EventNames.DEPOSIT_SUCCESS, swapEventData) - submittedEvents.push(EventNames.DEPOSIT_SUCCESS) - //To preserve the order of events we need to delay sending the fill success event but mark that we did send it to avoid duplicates - setTimeout(() => { - onAnalyticEvent?.(EventNames.FILL_SUCCESS, swapEventData) - }, 20) - } else { - onAnalyticEvent?.(EventNames.FILL_SUCCESS, swapEventData) - } - submittedEvents.push(EventNames.FILL_SUCCESS) - } - }) - - // Store the AbortController for potential cancellation immediately - if ( - execPromise && - typeof execPromise === 'object' && - 'abortController' in execPromise - ) { - setAbortController((execPromise as any).abortController) - } - - execPromise - ?.catch((error: any) => { - swapErrorHandler(error, _currentSteps) - }) - .finally(() => { - setWaitingForSteps(false) - setAbortController(null) - invalidateBalanceQueries() - }) - } catch (error: any) { - swapErrorHandler(error) - setWaitingForSteps(false) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - relayClient, - address, - connector, - wallet, - walletClient, - fromToken, - toToken, - customToAddress, - recipient, - debouncedInputAmountValue, - debouncedOutputAmountValue, - tradeType, - waitingForSteps, - executeSwap, - setSteps, - setQuoteInProgress, - invalidateBalanceQueries, - linkedWallet, - abortController - ]) - - return ( - <> - {children({ - quote, - steps, - setSteps, - swap, - transactionModalOpen, - feeBreakdown, - fromToken, - setFromToken, - toToken, - setToToken, - swapError, - error, - toDisplayName, - address, - originAddressOverride, - setOriginAddressOverride, - recipient, - destinationAddressOverride, - customToAddress, - setCustomToAddress, - setDestinationAddressOverride, - tradeType, - setTradeType, - details, - isSameCurrencySameRecipientSwap, - allowUnsupportedOrigin, - setAllowUnsupportedOrigin, - allowUnsupportedRecipient, - setAllowUnsupportedRecipient, - debouncedInputAmountValue, - debouncedAmountInputControls, - setAmountInputValue, - amountInputValue, - amountOutputValue, - debouncedOutputAmountValue, - debouncedAmountOutputControls, - setAmountOutputValue, - toBalance, - toBalancePending, - isLoadingToBalance, - isFetchingQuote, - isLoadingFromBalance, - fromBalance, - fromBalancePending, - highRelayerServiceFee, - relayerFeeProportion, - hasInsufficientBalance, - isInsufficientLiquidityError, - isCapacityExceededError, - isCouldNotExecuteError, - ctaCopy, - isFromNative, - slippageTolerance: currentSlippageTolerance, - timeEstimate, - isSvmSwap, - isBvmSwap, - isValidFromAddress, - isValidToAddress, - supportedWalletVMs, - fromChainWalletVMSupported, - toChainWalletVMSupported, - isRecipientLinked, - recipientWalletSupportsChain, - invalidateBalanceQueries, - invalidateQuoteQuery, - setDetails, - setSwapError, - quoteInProgress, - setQuoteInProgress, - linkedWallet, - quoteParameters, - abortController, - fromTokenPriceData, - toTokenPriceData, - isLoadingFromTokenPrice, - isLoadingToTokenPrice - })} - - ) -} - -export default TokenWidgetRenderer diff --git a/packages/ui/src/components/widgets/TokenWidget/widget/index.tsx b/packages/ui/src/components/widgets/TokenWidget/widget/index.tsx deleted file mode 100644 index 253845230..000000000 --- a/packages/ui/src/components/widgets/TokenWidget/widget/index.tsx +++ /dev/null @@ -1,1576 +0,0 @@ -import { Flex, Text } from '../../../primitives/index.js' -import { TabsRoot, TabsList, TabsTrigger } from '../../../primitives/Tabs.js' -import { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, - type FC, - type Dispatch, - type SetStateAction -} from 'react' -import { useRelayClient } from '../../../../hooks/index.js' -import useFallbackState from '../../../../hooks/useFallbackState.js' -import type { Address } from 'viem' -import { formatUnits } from 'viem' -import { usePublicClient } from 'wagmi' -import type { LinkedWallet, Token } from '../../../../types/index.js' -import { - ASSETS_RELAY_API, - type ChainVM, - type Execute, - type RelayChain -} from '@relayprotocol/relay-sdk' -import { EventNames } from '../../../../constants/events.js' -import WidgetContainer from '../../WidgetContainer.js' -import type { AdaptedWallet } from '@relayprotocol/relay-sdk' -import { ProviderOptionsContext } from '../../../../providers/RelayKitProvider.js' -import { findBridgableToken } from '../../../../utils/tokens.js' -import { UnverifiedTokenModal } from '../../../common/UnverifiedTokenModal.js' -import { alreadyAcceptedToken } from '../../../../utils/localStorage.js' -import { calculateUsdValue, getSwapEventData } from '../../../../utils/quote.js' -import { getFeeBufferAmount } from '../../../../utils/nativeMaxAmount.js' -import TokenWidgetRenderer, { type TradeType } from './TokenWidgetRenderer.js' -import BuyTabContent from '../BuyTabContent.js' -import SellTabContent from '../SellTabContent.js' -import { useQuote } from '@relayprotocol/relay-kit-hooks' -import { useWalletGuards } from '../hooks/useWalletGuards.js' -import { - isChainVmTypeSupported, - isWalletVmTypeCompatible -} from '../../../../utils/address.js' - -type BaseTokenWidgetProps = { - fromToken?: Token - setFromToken?: (token?: Token) => void - toToken?: Token - setToToken?: (token?: Token) => void - activeTab?: 'buy' | 'sell' - setActiveTab?: (tab: 'buy' | 'sell') => void - defaultToAddress?: Address - defaultAmount?: string - defaultTradeType?: 'EXACT_INPUT' | 'EXPECTED_OUTPUT' - slippageTolerance?: string - lockToToken?: boolean - lockFromToken?: boolean - lockChainId?: number - singleChainMode?: boolean - wallet?: AdaptedWallet - supportedWalletVMs: Omit[] - disableInputAutoFocus?: boolean - popularChainIds?: number[] - disablePasteWalletAddressOption?: boolean - useSecureBaseUrl?: (parameters: Parameters['2']) => boolean - onOpenSlippageConfig?: () => void - walletsLoading?: boolean - paymentMethodMinHeight?: string - onFromTokenChange?: (token?: Token) => void - onToTokenChange?: (token?: Token) => void - onConnectWallet?: () => void - onAnalyticEvent?: (eventName: string, data?: any) => void - onSwapValidating?: (data: Execute) => void - onSwapSuccess?: (data: Execute) => void - onSwapError?: (error: string, data?: Execute) => void - onUnverifiedTokenDecline?: (token: Token, context: 'from' | 'to') => void -} - -type MultiWalletDisabledProps = BaseTokenWidgetProps & { - multiWalletSupportEnabled?: false - linkedWallets?: never - onSetPrimaryWallet?: never - onLinkNewWallet?: never -} - -type MultiWalletEnabledProps = BaseTokenWidgetProps & { - multiWalletSupportEnabled: true - linkedWallets?: LinkedWallet[] - onSetPrimaryWallet?: (address: string) => void - onLinkNewWallet: (params: { - chain?: RelayChain - direction: 'to' | 'from' - }) => Promise | void -} - -export type TokenWidgetProps = - | MultiWalletDisabledProps - | MultiWalletEnabledProps - -const BASE_USDC_ADDRESS = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' - -const BASE_USDC_TOKEN: Token = { - chainId: 8453, - address: BASE_USDC_ADDRESS, - name: 'USD Coin', - symbol: 'USDC', - decimals: 6, - logoURI: `${ASSETS_RELAY_API}/icons/currencies/usdc.png`, - verified: true -} - -const isBaseUsdcToken = (token?: Token) => - token?.chainId === BASE_USDC_TOKEN.chainId && - token?.address?.toLowerCase() === BASE_USDC_ADDRESS - -const COMPATIBLE_WALLET_VMS: ChainVM[] = [ - 'evm', - 'suivm', - 'tvm', - 'hypevm', - 'svm', - 'bvm' -] - -const TokenWidget: FC = ({ - fromToken: externalFromToken, - setFromToken: setExternalFromToken, - toToken: externalToToken, - setToToken: setExternalToToken, - activeTab: externalActiveTab, - setActiveTab: setExternalActiveTab, - defaultToAddress, - defaultAmount, - defaultTradeType, - slippageTolerance, - onOpenSlippageConfig, - lockToToken = false, - lockFromToken = false, - lockChainId, - singleChainMode = false, - wallet, - multiWalletSupportEnabled = false, - linkedWallets, - supportedWalletVMs, - disableInputAutoFocus = false, - popularChainIds, - disablePasteWalletAddressOption, - useSecureBaseUrl, - paymentMethodMinHeight = '85vh', - onSetPrimaryWallet, - onLinkNewWallet, - onFromTokenChange, - onToTokenChange, - onConnectWallet, - onAnalyticEvent: _onAnalyticEvent, - onSwapSuccess, - onSwapValidating, - onSwapError, - onUnverifiedTokenDecline, - walletsLoading = false -}) => { - const onAnalyticEvent = useCallback( - (eventName: string, data?: any) => { - try { - _onAnalyticEvent?.(eventName, data) - } catch (e) { - console.error('Error in onAnalyticEvent', eventName, data, e) - } - }, - [_onAnalyticEvent] - ) - const relayClient = useRelayClient() - const providerOptionsContext = useContext(ProviderOptionsContext) - const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides - const [transactionModalOpen, setTransactionModalOpen] = useState(false) - const [depositAddressModalOpen, setDepositAddressModalOpen] = useState(false) - const [addressModalOpen, setAddressModalOpen] = useState(false) - const [pendingSuccessFlush, setPendingSuccessFlush] = useState(false) - const [unverifiedTokens, setUnverifiedTokens] = useState< - { token: Token; context: 'to' | 'from' }[] - >([]) - const declinedTokensRef = useRef>(new Set()) - - const [fromToken, setFromToken] = useState( - externalFromToken - ) - const [toToken, setToToken] = useState(externalToToken) - - useEffect(() => { - setFromToken(externalFromToken) - }, [externalFromToken]) - - useEffect(() => { - setToToken(externalToToken) - }, [externalToToken]) - - const updateFromToken = useCallback( - (token: Token | undefined) => { - setFromToken(token) - setExternalFromToken?.(token) - }, - [setExternalFromToken] - ) - - const updateToToken = useCallback( - (token: Token | undefined) => { - setToToken(token) - setExternalToToken?.(token) - }, - [setExternalToToken] - ) - - const [activeTab, setActiveTab] = useFallbackState<'buy' | 'sell'>( - setExternalActiveTab && externalActiveTab ? externalActiveTab : 'buy', - setExternalActiveTab && externalActiveTab - ? [ - externalActiveTab, - setExternalActiveTab as Dispatch> - ] - : undefined - ) - const [isUsdInputMode, setIsUsdInputMode] = useState(activeTab === 'buy') - const [usdInputValue, setUsdInputValue] = useState('') - const [usdOutputValue, setUsdOutputValue] = useState('') - const [tokenInputCache, setTokenInputCache] = useState('') - const tabTokenStateRef = useRef<{ - buy: { fromToken?: Token; toToken?: Token } - sell: { fromToken?: Token; toToken?: Token } - }>({ buy: {}, sell: {} }) - const prevActiveTabRef = useRef<'buy' | 'sell'>(activeTab) - const autoSelectedFromTokenRef = useRef(false) - const tabRecipientRef = useRef<{ - buy: { override?: string; custom?: string } - sell: { override?: string; custom?: string } - }>({ buy: {}, sell: {} }) - const setTradeTypeRef = useRef<((tradeType: TradeType) => void) | null>(null) - const tradeTypeRef = useRef(defaultTradeType ?? 'EXPECTED_OUTPUT') - - const hasLockedToken = lockFromToken || lockToToken - const isSingleChainLocked = singleChainMode && lockChainId !== undefined - const [localSlippageTolerance, setLocalSlippageTolerance] = useState< - string | undefined - >(slippageTolerance) - - useEffect(() => { - setLocalSlippageTolerance(slippageTolerance) - }, [slippageTolerance]) - - useEffect(() => { - const desiredTradeType: TradeType = - activeTab === 'buy' ? 'EXPECTED_OUTPUT' : 'EXACT_INPUT' - - if (tradeTypeRef.current !== desiredTradeType) { - setTradeTypeRef.current?.(desiredTradeType) - } - }, [activeTab]) - - const handleOpenSlippageConfig = () => { - onOpenSlippageConfig?.() - } - - const handleSlippageToleranceChange = (value: string | undefined) => { - setLocalSlippageTolerance(value) - } - - const computedDefaultTradeType: TradeType = - defaultTradeType ?? - (activeTab === 'buy' ? 'EXPECTED_OUTPUT' : 'EXACT_INPUT') - - //Handle unverified tokens - useEffect(() => { - const tokensToVerify: { token: Token; context: 'to' | 'from' }[] = [] - - const getTokenKey = (token: Token) => - `${token.chainId}:${token.address.toLowerCase()}` - - if (fromToken && 'verified' in fromToken && !fromToken.verified) { - if (alreadyAcceptedToken(fromToken)) { - setFromToken({ ...fromToken, verified: true }) - } else if (!declinedTokensRef.current.has(getTokenKey(fromToken))) { - tokensToVerify.push({ token: fromToken, context: 'from' }) - } - } - - if (toToken && 'verified' in toToken && !toToken.verified) { - if (alreadyAcceptedToken(toToken)) { - setToToken({ ...toToken, verified: true }) - } else if (!declinedTokensRef.current.has(getTokenKey(toToken))) { - tokensToVerify.push({ token: toToken, context: 'to' }) - } - } - - if (tokensToVerify.length > 0) { - setUnverifiedTokens((prev) => [...prev, ...tokensToVerify]) - - tokensToVerify.forEach(({ context }) => { - if (context === 'from') { - setFromToken(undefined) - } else { - setToToken(undefined) - } - }) - } - }, [fromToken, toToken]) - - return ( - - {({ - quote, - steps, - swap, - setSteps, - feeBreakdown, - fromToken, - setFromToken, - toToken, - setToToken, - error, - toDisplayName, - address, - originAddressOverride: _originAddressOverride, - setOriginAddressOverride, - recipient, - customToAddress, - setCustomToAddress, - destinationAddressOverride, - setDestinationAddressOverride, - tradeType, - setTradeType, - isSameCurrencySameRecipientSwap, - allowUnsupportedOrigin, - setAllowUnsupportedOrigin, - allowUnsupportedRecipient, - setAllowUnsupportedRecipient, - debouncedInputAmountValue, - debouncedAmountInputControls, - setAmountInputValue, - amountInputValue, - amountOutputValue, - debouncedOutputAmountValue, - debouncedAmountOutputControls, - setAmountOutputValue, - toBalance, - toBalancePending, - isLoadingToBalance, - isFetchingQuote, - isLoadingFromBalance, - fromBalance, - fromBalancePending, - highRelayerServiceFee, - relayerFeeProportion, - hasInsufficientBalance, - isInsufficientLiquidityError, - isCapacityExceededError, - isCouldNotExecuteError, - ctaCopy, - isFromNative, - timeEstimate, - isSvmSwap, - isBvmSwap, - isValidFromAddress, - isValidToAddress, - slippageTolerance, - fromChainWalletVMSupported, - toChainWalletVMSupported, - isRecipientLinked, - swapError, - recipientWalletSupportsChain, - linkedWallet, - quoteParameters, - setSwapError, - invalidateBalanceQueries, - invalidateQuoteQuery, - quoteInProgress, - setQuoteInProgress, - abortController, - fromTokenPriceData, - toTokenPriceData, - isLoadingFromTokenPrice, - isLoadingToTokenPrice - }) => { - setTradeTypeRef.current = setTradeType - tradeTypeRef.current = tradeType - - useEffect(() => { - if (prevActiveTabRef.current !== activeTab) { - return - } - tabTokenStateRef.current[activeTab] = { - fromToken, - toToken - } - }, [activeTab, fromToken, toToken]) - - useEffect(() => { - tabRecipientRef.current[activeTab] = { - override: - typeof destinationAddressOverride === 'string' - ? destinationAddressOverride - : undefined, - custom: - typeof customToAddress === 'string' ? customToAddress : undefined - } - }, [activeTab, destinationAddressOverride, customToAddress]) - - useEffect(() => { - setAllowUnsupportedOrigin(activeTab === 'buy') - setAllowUnsupportedRecipient(activeTab === 'sell') - }, [activeTab, setAllowUnsupportedOrigin, setAllowUnsupportedRecipient]) - - // Auto-select first compatible wallet in buy tab when toToken is set and no destination is selected - useEffect(() => { - if ( - activeTab === 'buy' && - toToken && - multiWalletSupportEnabled && - linkedWallets && - linkedWallets.length > 0 && - !destinationAddressOverride && - !customToAddress - ) { - // Find the destination chain for filtering compatible wallets - const toChain = relayClient?.chains?.find( - (c) => c.id === toToken.chainId - ) - - if (toChain) { - // Filter wallets compatible with the destination chain VM type - const compatibleWallets = linkedWallets.filter((wallet) => { - return isWalletVmTypeCompatible(wallet.vmType, toChain.vmType) - }) - - // Auto-select the first compatible wallet (prefer the current address if compatible) - const currentAddressWallet = compatibleWallets.find( - (w) => w.address.toLowerCase() === address?.toLowerCase() - ) - const walletToSelect = - currentAddressWallet || compatibleWallets[0] - - if (walletToSelect) { - setDestinationAddressOverride(walletToSelect.address) - } - } - } - }, [ - activeTab, - toToken, - multiWalletSupportEnabled, - linkedWallets, - destinationAddressOverride, - customToAddress, - relayClient?.chains, - setDestinationAddressOverride, - address - ]) - - // Calculate the USD value of the input amount - const inputAmountUsd = useMemo(() => { - return ( - calculateUsdValue(fromTokenPriceData?.price, amountInputValue) ?? - null - ) - }, [fromTokenPriceData, amountInputValue]) - - // Calculate the USD value of the output amount - const outputAmountUsd = useMemo(() => { - return ( - calculateUsdValue(toTokenPriceData?.price, amountOutputValue) ?? - null - ) - }, [toTokenPriceData, amountOutputValue]) - - const percentageOptions = [20, 50] - - const handleMaxAmountClicked = async ( - amount: bigint, - percent: string, - bufferAmount?: bigint - ) => { - if (fromToken) { - const formattedAmount = formatUnits(amount, fromToken?.decimals) - setAmountInputValue(formattedAmount) - setTradeType('EXACT_INPUT') - debouncedAmountOutputControls.cancel() - debouncedAmountInputControls.flush() - onAnalyticEvent?.(EventNames.MAX_AMOUNT_CLICKED, { - percent: percent, - bufferAmount: bufferAmount ? bufferAmount.toString() : '0', - chainType: fromChain?.vmType - }) - - if (isUsdInputMode && conversionRate) { - const numericTokenAmount = Number(formattedAmount) - if (!isNaN(numericTokenAmount)) { - const usdEquivalent = numericTokenAmount * conversionRate - setUsdInputValue(usdEquivalent.toFixed(2)) - } - } - } - } - - const fromChain = relayClient?.chains?.find( - (chain) => chain.id === fromToken?.chainId - ) - - const toChain = relayClient?.chains?.find( - (chain) => chain.id === toToken?.chainId - ) - - const handleSetToToken = useCallback( - (token?: Token) => { - if (!token) { - updateToToken(undefined) - onToTokenChange?.(undefined) - return - } - - let _token = token - if (!fromChainWalletVMSupported) { - const newToChain = relayClient?.chains.find( - (chain) => token?.chainId == chain.id - ) - if (newToChain) { - const _toToken = findBridgableToken(newToChain, _token) - if (_toToken && _toToken.address != _token?.address) { - _token = _toToken - } - } - } - updateToToken(_token) - onToTokenChange?.(_token) - }, - [ - fromChainWalletVMSupported, - onToTokenChange, - relayClient, - updateToToken - ] - ) - - const handleSetFromToken = useCallback( - (token?: Token) => { - if (!token) { - updateFromToken(undefined) - onFromTokenChange?.(undefined) - return - } - - let _token = token - const newFromChain = relayClient?.chains.find( - (chain) => token?.chainId == chain.id - ) - - if ( - newFromChain?.vmType && - !isChainVmTypeSupported(newFromChain?.vmType, supportedWalletVMs) - ) { - setTradeType('EXACT_INPUT') - - const _toToken = findBridgableToken(toChain, toToken) - - if (_toToken && _toToken?.address != toToken?.address) { - handleSetToToken(_toToken) - } - - const _fromToken = findBridgableToken(newFromChain, _token) - if (_fromToken && _fromToken.address != _token?.address) { - _token = _fromToken - } - } - - updateFromToken(_token) - onFromTokenChange?.(_token) - }, - [ - handleSetToToken, - onFromTokenChange, - relayClient, - updateFromToken, - setTradeType, - supportedWalletVMs, - toChain, - toToken - ] - ) - - // Get public client for the fromChain to estimate gas - const publicClient = usePublicClient({ chainId: fromChain?.id }) - - // Seed fromToken on sell tab if empty but we have a token available - useEffect(() => { - if (activeTab !== 'sell') return - if (fromToken) return - - const candidateFromToken = - tabTokenStateRef.current.sell?.fromToken ?? - tabTokenStateRef.current.buy?.toToken ?? - toToken - - if (candidateFromToken) { - handleSetFromToken(candidateFromToken) - } - }, [activeTab, fromToken, toToken, handleSetFromToken]) - - useWalletGuards({ - multiWalletSupportEnabled, - allowUnsupportedOrigin, - allowUnsupportedRecipient, - fromChain, - toChain, - address, - recipient, - linkedWallets, - connectorKeyOverrides, - onSetPrimaryWallet, - isValidFromAddress, - isValidToAddress, - setOriginAddressOverride, - setCustomToAddress, - disablePasteWalletAddressOption, - customToAddress: customToAddress as string | undefined, - originAddressOverride: _originAddressOverride as string | undefined, - destinationAddressOverride: destinationAddressOverride as - | string - | undefined, - setDestinationAddressOverride - }) - - const isAutoSlippage = slippageTolerance === undefined - - const isHighPriceImpact = - Number(quote?.details?.totalImpact?.percent) < -3.5 - const totalImpactUsd = quote?.details?.totalImpact?.usd - const showHighPriceImpactWarning = Boolean( - isHighPriceImpact && totalImpactUsd && Number(totalImpactUsd) <= -10 - ) - - // Calculate conversion rate - const conversionRate = useMemo(() => { - if (isUsdInputMode) { - // When in USD input mode, the conversion rate is the price of the fromToken. - if (fromTokenPriceData?.price && fromTokenPriceData.price > 0) { - return fromTokenPriceData.price - } else { - // If no price data, or price is 0, return null to avoid stale calculations. - return null - } - } else { - // When in token input mode, calculate rate from quote if available. - if ( - amountInputValue && - Number(amountInputValue) > 0 && - quote?.details?.currencyIn?.amountUsd - ) { - const tokenVal = Number(amountInputValue) - const usdVal = Number(quote.details.currencyIn.amountUsd) - if (tokenVal > 0 && usdVal > 0) { - const rate = usdVal / tokenVal - return rate - } else { - return null - } - } else { - // If in token mode and token input is cleared or zero, return null - return null - } - } - }, [ - isUsdInputMode, - fromTokenPriceData?.price, - quote?.details?.currencyIn?.amountUsd, - amountInputValue - ]) - - // toggle between token and usd input mode - const toggleInputMode = () => { - if (!isUsdInputMode) { - // Switching TO USD mode - let newUsdInputValue = '' - let newUsdOutputValue = '' - - // Calculate USD input value - if ( - quote?.details?.currencyIn?.amountUsd && - Number(quote.details.currencyIn.amountUsd) > 0 - ) { - newUsdInputValue = String( - Number(quote.details.currencyIn.amountUsd) - ) - } else if (inputAmountUsd && inputAmountUsd > 0) { - newUsdInputValue = inputAmountUsd.toFixed(2) - } else if ( - amountInputValue && - Number(amountInputValue) > 0 && - conversionRate && - conversionRate > 0 - ) { - newUsdInputValue = ( - Number(amountInputValue) * conversionRate - ).toFixed(2) - } - - // Calculate USD output value - if ( - quote?.details?.currencyOut?.amountUsd && - Number(quote.details.currencyOut.amountUsd) > 0 - ) { - newUsdOutputValue = String( - Number(quote.details.currencyOut.amountUsd) - ) - } else if (outputAmountUsd && outputAmountUsd > 0) { - newUsdOutputValue = outputAmountUsd.toFixed(2) - } else if ( - amountOutputValue && - Number(amountOutputValue) > 0 && - toTokenPriceData?.price && - toTokenPriceData.price > 0 - ) { - newUsdOutputValue = ( - Number(amountOutputValue) * toTokenPriceData.price - ).toFixed(2) - } - - setTokenInputCache(amountInputValue) - setUsdInputValue(newUsdInputValue) - setUsdOutputValue(newUsdOutputValue) - setIsUsdInputMode(true) - - // Default to EXACT_INPUT unless we're currently in EXPECTED_OUTPUT mode with a valid USD output value - if (tradeType !== 'EXPECTED_OUTPUT' || !newUsdOutputValue) { - setTradeType('EXACT_INPUT') - } - } else { - // Switching FROM USD mode - if (!usdInputValue && tokenInputCache) { - setAmountInputValue(tokenInputCache) - } - setUsdInputValue('') - setUsdOutputValue('') - setIsUsdInputMode(false) - // Maintain current trade type when switching back to token mode - } - } - - //Update token input value when USD input changes in USD mode - useEffect(() => { - if (isUsdInputMode) { - if (conversionRate && conversionRate > 0 && usdInputValue) { - const usdValue = Number(usdInputValue) - if (!isNaN(usdValue) && usdValue > 0) { - const tokenEquivalent = (usdValue / conversionRate).toFixed( - fromToken?.decimals ?? 8 - ) - setAmountInputValue(tokenEquivalent) - } - } else if (usdInputValue === '' || Number(usdInputValue) === 0) { - setAmountInputValue('') - } - } - }, [ - isUsdInputMode, - usdInputValue, - conversionRate, - setAmountInputValue, - fromToken?.decimals - ]) - - //Update token output value when USD output changes in USD mode - useEffect(() => { - if (isUsdInputMode && tradeType === 'EXPECTED_OUTPUT') { - if ( - toTokenPriceData?.price && - toTokenPriceData.price > 0 && - usdOutputValue - ) { - const usdValue = Number(usdOutputValue) - if (!isNaN(usdValue) && usdValue > 0) { - const tokenEquivalent = ( - usdValue / toTokenPriceData.price - ).toFixed(toToken?.decimals ?? 8) - setAmountOutputValue(tokenEquivalent) - } - } else if (usdOutputValue === '' || Number(usdOutputValue) === 0) { - setAmountOutputValue('') - } - } - }, [ - isUsdInputMode, - tradeType, - usdOutputValue, - toTokenPriceData?.price, - setAmountOutputValue, - toToken?.decimals - ]) - - //Update USD output value when in USD mode - useEffect(() => { - if (isUsdInputMode) { - // For EXPECTED_OUTPUT, don't override user's typed value - if (tradeType === 'EXPECTED_OUTPUT') { - // User is controlling the output value directly - return - } - - // For EXACT_INPUT, update based on quote or calculations - if (quote?.details?.currencyOut?.amountUsd && !isFetchingQuote) { - // Use quote USD value when available - const quoteUsdValue = Number(quote.details.currencyOut.amountUsd) - if (!isNaN(quoteUsdValue) && quoteUsdValue >= 0) { - setUsdOutputValue(quoteUsdValue.toFixed(2)) - } - } else if ( - toTokenPriceData?.price && - toTokenPriceData.price > 0 && - amountOutputValue && - Number(amountOutputValue) > 0 - ) { - // Fallback to direct token price calculation - const tokenAmount = Number(amountOutputValue) - const usdEquivalent = tokenAmount * toTokenPriceData.price - if (!isNaN(usdEquivalent) && usdEquivalent >= 0) { - setUsdOutputValue(usdEquivalent.toFixed(2)) - } - } else if (!amountOutputValue || Number(amountOutputValue) === 0) { - setUsdOutputValue('') - } - } - }, [ - isUsdInputMode, - tradeType, - quote?.details?.currencyOut?.amountUsd, - isFetchingQuote, - toTokenPriceData?.price, - amountOutputValue - ]) - - //Update USD input value when in EXPECTED_OUTPUT mode - useEffect(() => { - if (isUsdInputMode && tradeType === 'EXPECTED_OUTPUT') { - if (quote?.details?.currencyIn?.amountUsd && !isFetchingQuote) { - // Use quote USD value when available - const quoteUsdValue = Number(quote.details.currencyIn.amountUsd) - if (!isNaN(quoteUsdValue) && quoteUsdValue >= 0) { - setUsdInputValue(quoteUsdValue.toFixed(2)) - } - } else if (!amountInputValue || Number(amountInputValue) === 0) { - setUsdInputValue('') - } - } - }, [ - isUsdInputMode, - tradeType, - quote?.details?.currencyIn?.amountUsd, - isFetchingQuote, - amountInputValue - ]) - - const recipientLinkedWallet = linkedWallets?.find( - (wallet) => wallet.address === recipient - ) - - const handlePrimaryAction = () => { - if (fromChainWalletVMSupported) { - if (!isValidToAddress || !isValidFromAddress) { - if ( - multiWalletSupportEnabled && - (isValidToAddress || - (!isValidToAddress && toChainWalletVMSupported)) - ) { - const chain = !isValidFromAddress ? fromChain : toChain - if (!address) { - onConnectWallet?.() - } else { - onLinkNewWallet?.({ - chain, - direction: !isValidFromAddress ? 'from' : 'to' - })?.then((wallet) => { - if (!isValidFromAddress) { - onSetPrimaryWallet?.(wallet.address) - } else { - setDestinationAddressOverride(wallet.address) - setCustomToAddress(undefined) - } - }) - } - } else { - setAddressModalOpen(true) - } - } else { - swap() - } - } else { - if (!isValidToAddress) { - if (multiWalletSupportEnabled && toChainWalletVMSupported) { - if (!address) { - onConnectWallet?.() - } else { - onLinkNewWallet?.({ - chain: toChain, - direction: 'to' - })?.then((wallet) => { - if (!wallet) { - return - } - setDestinationAddressOverride(wallet.address) - setCustomToAddress(undefined) - }) - } - } else { - setAddressModalOpen(true) - } - } else { - const swapEventData = getSwapEventData( - quote?.details, - quote?.fees, - quote?.steps ? (quote?.steps as Execute['steps']) : null, - linkedWallet?.connector, - quoteParameters - ) - onAnalyticEvent?.(EventNames.SWAP_CTA_CLICKED, swapEventData) - setDepositAddressModalOpen(true) - } - } - } - - const linkedWalletsLoading = - multiWalletSupportEnabled && linkedWallets === undefined - const walletAddressLoading = Boolean(wallet && !address) - - useEffect(() => { - if (walletsLoading || linkedWalletsLoading || walletAddressLoading) { - return - } - - const hasLinkedCompatibleWallet = - linkedWallets?.some( - (wallet) => - wallet?.vmType && COMPATIBLE_WALLET_VMS.includes(wallet.vmType) - ) ?? false - const hasCompatibleWallet = - Boolean(address && isValidFromAddress) || hasLinkedCompatibleWallet - const baseSelected = isBaseUsdcToken(fromToken) - - if (activeTab === 'buy' && !fromToken && !hasCompatibleWallet) { - handleSetFromToken(BASE_USDC_TOKEN) - autoSelectedFromTokenRef.current = true - return - } - - if (autoSelectedFromTokenRef.current && !baseSelected) { - autoSelectedFromTokenRef.current = false - } - - if ( - autoSelectedFromTokenRef.current && - (activeTab !== 'buy' || hasCompatibleWallet) - ) { - if (baseSelected) { - handleSetFromToken(undefined) - } - autoSelectedFromTokenRef.current = false - } - }, [ - activeTab, - address, - handleSetFromToken, - linkedWallets, - fromToken, - isValidFromAddress, - linkedWalletsLoading, - multiWalletSupportEnabled, - walletAddressLoading, - walletsLoading - ]) - - const handleTabChange = useCallback( - (nextTab: 'buy' | 'sell', updateActiveTab: boolean) => { - const prevTab = prevActiveTabRef.current ?? activeTab - - if (nextTab === prevTab && !updateActiveTab) { - return - } - - setAllowUnsupportedOrigin(nextTab === 'buy') - setAllowUnsupportedRecipient(nextTab === 'sell') - - if (nextTab !== prevTab) { - const storedNextState = tabTokenStateRef.current[nextTab] ?? {} - const storedNextRecipient = tabRecipientRef.current[nextTab] ?? {} - - const prevFromToken = fromToken - const prevToToken = toToken - - tabTokenStateRef.current[prevTab] = { - fromToken: prevFromToken, - toToken: prevToToken - } - tabRecipientRef.current[prevTab] = { - override: - typeof destinationAddressOverride === 'string' - ? destinationAddressOverride - : undefined, - custom: - typeof customToAddress === 'string' - ? customToAddress - : undefined - } - - let nextFromToken: Token | undefined - let nextToToken: Token | undefined - - if (nextTab === 'sell') { - // Selling the page token: default to previously viewed token (prevToToken) - nextFromToken = - storedNextState.fromToken ?? - prevToToken ?? - prevFromToken ?? - undefined - // Payout token should remain empty unless user explicitly selected it on sell - nextToToken = storedNextState.toToken ?? undefined - } else { - // Buying the page token: default output token is prev page token - nextToToken = - storedNextState.toToken ?? - prevFromToken ?? - prevToToken ?? - undefined - // Payment method stays empty unless explicitly chosen on buy - nextFromToken = storedNextState.fromToken ?? undefined - } - - handleSetFromToken(nextFromToken) - handleSetToToken(nextToToken) - setDestinationAddressOverride(storedNextRecipient.override) - setCustomToAddress(storedNextRecipient.custom) - - // Auto-select first compatible wallet in buy tab if no destination is set - if ( - nextTab === 'buy' && - multiWalletSupportEnabled && - linkedWallets && - linkedWallets.length > 0 && - !storedNextRecipient.override && - !storedNextRecipient.custom - ) { - const toChainForRecipient = relayClient?.chains?.find( - (c) => c.id === nextToToken?.chainId - ) - - if (toChainForRecipient) { - const compatibleWallets = linkedWallets.filter( - (wallet) => - isWalletVmTypeCompatible( - wallet.vmType, - toChainForRecipient.vmType - ) - ) - - if (compatibleWallets.length > 0) { - setDestinationAddressOverride(compatibleWallets[0].address) - } - } - } - - setAmountInputValue('') - setAmountOutputValue('') - setUsdInputValue('') - setUsdOutputValue('') - setTokenInputCache('') - setIsUsdInputMode(nextTab === 'buy') - debouncedAmountInputControls.cancel() - debouncedAmountOutputControls.cancel() - setOriginAddressOverride(undefined) - } - - if (updateActiveTab) { - setActiveTab(nextTab) - } - - const desiredTradeType: TradeType = - nextTab === 'buy' ? 'EXPECTED_OUTPUT' : 'EXACT_INPUT' - - if (tradeType !== desiredTradeType) { - setTradeType(desiredTradeType) - } - - prevActiveTabRef.current = nextTab - }, - [ - activeTab, - customToAddress, - debouncedAmountInputControls, - debouncedAmountOutputControls, - destinationAddressOverride, - fromToken, - handleSetFromToken, - handleSetToToken, - linkedWallets, - multiWalletSupportEnabled, - relayClient?.chains, - setActiveTab, - setAllowUnsupportedOrigin, - setAllowUnsupportedRecipient, - setAmountInputValue, - setAmountOutputValue, - setCustomToAddress, - setDestinationAddressOverride, - setIsUsdInputMode, - setOriginAddressOverride, - setTokenInputCache, - setTradeType, - setUsdInputValue, - setUsdOutputValue, - toToken, - tradeType - ] - ) - - useEffect(() => { - if (prevActiveTabRef.current === activeTab) { - return - } - - handleTabChange(activeTab, false) - }, [activeTab, handleTabChange]) - - return ( - <> - { - if (!open) { - if (pendingSuccessFlush) { - setPendingSuccessFlush(false) - } else if (steps) { - invalidateQuoteQuery() - } - // Abort ongoing execution - if (abortController) { - abortController.abort() - } - setSwapError(null) - setSteps(null) - setQuoteInProgress(null) - } else if (pendingSuccessFlush) { - setPendingSuccessFlush(false) - } - }} - onDepositAddressModalOpenChange={(open) => { - if (!open) { - setSwapError(null) - if (pendingSuccessFlush) { - setPendingSuccessFlush(false) - } else { - invalidateQuoteQuery() - } - } else if (pendingSuccessFlush) { - setPendingSuccessFlush(false) - } - }} - slippageTolerance={localSlippageTolerance} - swapError={swapError} - setSwapError={setSwapError} - onSwapSuccess={(data) => { - setPendingSuccessFlush(true) - setAmountInputValue('') - setAmountOutputValue('') - onSwapSuccess?.(data) - }} - onSwapValidating={onSwapValidating} - onAnalyticEvent={onAnalyticEvent} - invalidateBalanceQueries={invalidateBalanceQueries} - invalidateQuoteQuery={invalidateQuoteQuery} - customToAddress={customToAddress} - setCustomToAddress={setCustomToAddress} - timeEstimate={timeEstimate} - wallet={wallet} - linkedWallets={linkedWallets} - multiWalletSupportEnabled={multiWalletSupportEnabled} - > - {() => { - return ( - - { - const nextTab = value as 'buy' | 'sell' - handleTabChange(nextTab, true) - - onAnalyticEvent?.('TAB_SWITCHED', { - tab: value - }) - }} - > - - - - - Buy - - - - - Sell - - - - - - - - - - - ) - }} - - 0} - onOpenChange={() => {}} - data={ - unverifiedTokens.length > 0 ? unverifiedTokens[0] : undefined - } - onDecline={(token, context) => { - if (token) { - // Track declined tokens to prevent re-prompting - const tokenKey = `${token.chainId}:${token.address.toLowerCase()}` - declinedTokensRef.current.add(tokenKey) - - onUnverifiedTokenDecline?.(token, context as 'from' | 'to') - } - setUnverifiedTokens((prev) => - prev.filter( - (unverifiedToken) => - !( - unverifiedToken.context === context && - unverifiedToken.token.address === token?.address && - unverifiedToken.token.chainId === token?.chainId - ) - ) - ) - }} - onAcceptToken={(token, context) => { - if (token) { - if (context === 'to') { - onAnalyticEvent?.(EventNames.SWAP_TOKEN_SELECT, { - direction: 'output', - token_symbol: token.symbol - }) - if ( - token.address === fromToken?.address && - token.chainId === fromToken?.chainId && - address === recipient && - (!lockToToken || !fromToken) - ) { - handleSetToToken(fromToken) - handleSetFromToken(toToken) - } else { - handleSetToToken(token) - } - } else if (context === 'from') { - onAnalyticEvent?.(EventNames.SWAP_TOKEN_SELECT, { - direction: 'input', - token_symbol: token.symbol - }) - if ( - token.address === toToken?.address && - token.chainId === toToken?.chainId && - address === recipient && - (!lockToToken || !fromToken) - ) { - handleSetFromToken(toToken) - handleSetToToken(fromToken) - } else { - handleSetFromToken(token) - } - } - } - setUnverifiedTokens((prev) => - prev.filter( - (unverifiedToken) => - !( - unverifiedToken.token.address === token?.address && - unverifiedToken.token.chainId === token?.chainId - ) - ) - ) - }} - /> - - ) - }} - - ) -} - -export default TokenWidget From 5d0487af6934807ce45e535a7b36b2e27203ea9d Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 12:38:34 -0500 Subject: [PATCH 12/25] Remove design-system --- .gitignore | 5 ---- packages/design-system/package.json | 42 ----------------------------- packages/ui/src/styles/base.css | 6 +++-- packages/ui/tsconfig.json | 4 +-- 4 files changed, 5 insertions(+), 52 deletions(-) delete mode 100644 packages/design-system/package.json diff --git a/.gitignore b/.gitignore index ddc181637..da905d254 100644 --- a/.gitignore +++ b/.gitignore @@ -179,10 +179,5 @@ dist _cjs _esm _types -## Panda -styled-system -styled-system-studio dist -design-system -!design-system/package.json **/src/version.ts \ No newline at end of file diff --git a/packages/design-system/package.json b/packages/design-system/package.json deleted file mode 100644 index 0588b9f77..000000000 --- a/packages/design-system/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@relayprotocol/relay-design-system", - "description": "This package is auto-generated by Panda CSS", - "version": "1.0.0", - "type": "module", - "exports": { - "./css": { - "types": "./css/index.d.ts", - "require": "./css/index.mjs", - "import": "./css/index.mjs" - }, - "./jsx": { - "types": "./jsx/index.d.ts", - "require": "./jsx/index.mjs", - "import": "./jsx/index.mjs" - }, - "./patterns": { - "types": "./patterns/index.d.ts", - "require": "./patterns/index.mjs", - "import": "./patterns/index.mjs" - }, - "./recipes": { - "types": "./recipes/index.d.ts", - "require": "./recipes/index.mjs", - "import": "./recipes/index.mjs" - }, - "./themes": { - "types": "./themes/index.d.ts", - "require": "./themes/index.mjs", - "import": "./themes/index.mjs" - }, - "./tokens": { - "types": "./tokens/index.d.ts", - "require": "./tokens/index.mjs", - "import": "./tokens/index.mjs" - }, - "./types": { - "types": "./types/index.d.ts" - }, - "./styles.css": "./styles.css" - } -} \ No newline at end of file diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index 4e1a671bd..45534e17a 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -865,8 +865,10 @@ } } -/* High-specificity inset focus ring — matches old PandaCSS :not(#\#) pattern */ -.relay-focus-inset:is(:focus-visible, [data-focus]) { +/* Inset focus ring for keyboard-navigable list items. + Uses :focus (not :focus-visible) for Firefox compatibility — + Firefox won't trigger :focus-visible on programmatic .focus() calls. */ +.relay-focus-inset:is(:focus, [data-focus]) { box-shadow: inset 0 0 0 2px var(--relay-colors-focus-color) !important; outline: none !important; --tw-ring-shadow: 0 0 #0000 !important; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index c11dc19ae..53466b5de 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -9,9 +9,7 @@ "~sdk/*": ["./src/*"], "@relayprotocol/relay-sdk": ["./packages/sdk/src"], "@relayprotocol/relay-kit-ui": ["./packages/ui/src"], - "@relayprotocol/relay-kit-hooks": ["./packages/hooks/src"], - "@relayprotocol/relay-design-system": ["./packages/design-system"], - "@relayprotocol/relay-design-system/OnrampWidget": ["./packages/ui/src/components/widgets/OnrampWidget"], + "@relayprotocol/relay-kit-hooks": ["./packages/hooks/src"] } }, "watchOptions": { From 8b11996b80c9f920fd2d00f80cd1347dcb66cd9a Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 13:27:07 -0500 Subject: [PATCH 13/25] Fix widget container bg and token selector --- .../providers/RelayKitProviderWrapper.tsx | 2 +- demo/pages/_app.tsx | 7 ++++++- demo/pages/ui/swap.tsx | 17 +++++++++-------- .../TokenSelector/triggers/TokenTrigger.tsx | 4 ++-- packages/ui/src/styles/base.css | 3 ++- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/demo/components/providers/RelayKitProviderWrapper.tsx b/demo/components/providers/RelayKitProviderWrapper.tsx index e2b58e159..586a7e793 100644 --- a/demo/components/providers/RelayKitProviderWrapper.tsx +++ b/demo/components/providers/RelayKitProviderWrapper.tsx @@ -18,7 +18,7 @@ const DEFAULT_APP_FEES = [ const BASE_THEME = { font: 'var(--font-inter), -apple-system, Helvetica, sans-serif', - fontHeading: 'var(--font-inter), -apple-system, Helvetica, sans-serif' + fontHeading: 'Chivo, -apple-system, Helvetica, sans-serif' } export const RelayKitProviderWrapper: FC<{ diff --git a/demo/pages/_app.tsx b/demo/pages/_app.tsx index de4be4fb3..3a0d205fc 100644 --- a/demo/pages/_app.tsx +++ b/demo/pages/_app.tsx @@ -131,7 +131,12 @@ const AppWrapper: FC = ({ children, dynamicChains }) => { }, []) return ( -
+
{ setLinkWalletPromise(undefined) } }) - const [fromToken, setFromToken] = useState({ - chainId: 8453, - address: '0x0000000000000000000000000000000000000000', - decimals: 18, - name: 'ETH', - symbol: 'ETH', - logoURI: 'https://assets.relay.link/icons/currencies/eth.png' - }) + const [fromToken, setFromToken] = useState() + // { + // chainId: 8453, + // address: '0x0000000000000000000000000000000000000000', + // decimals: 18, + // name: 'ETH', + // symbol: 'ETH', + // logoURI: 'https://assets.relay.link/icons/currencies/eth.png' + // } const [toToken, setToToken] = useState({ chainId: 10, address: '0x0000000000000000000000000000000000000000', diff --git a/packages/ui/src/components/common/TokenSelector/triggers/TokenTrigger.tsx b/packages/ui/src/components/common/TokenSelector/triggers/TokenTrigger.tsx index ae5bf1e3f..f713f1d2f 100644 --- a/packages/ui/src/components/common/TokenSelector/triggers/TokenTrigger.tsx +++ b/packages/ui/src/components/common/TokenSelector/triggers/TokenTrigger.tsx @@ -34,7 +34,7 @@ export const TokenTrigger: FC = ({ corners="pill" size="none" disabled={locked} - className="relay-w-max relay-shrink-0 relay-overflow-hidden relay-px-3 relay-py-2 relay-bg-[var(--relay-colors-widget-selector-background)] relay-border-none hover:relay-bg-[var(--relay-colors-widget-selector-hover-background)] disabled:relay-bg-[var(--relay-colors-widget-selector-background)] relay-transition-colors relay-duration-150" + className="relay-w-max relay-h-[50px] relay-shrink-0 relay-overflow-hidden relay-px-3 relay-bg-[var(--relay-colors-widget-selector-background)] relay-border-none hover:relay-bg-[var(--relay-colors-widget-selector-hover-background)] disabled:relay-bg-[var(--relay-colors-widget-selector-background)] relay-transition-colors relay-duration-150" data-testid={testId} > @@ -74,7 +74,7 @@ export const TokenTrigger: FC = ({ corners="pill" size="none" cta={true} - className="relay-w-max relay-shrink-0 relay-overflow-hidden relay-px-3 relay-py-2 relay-font-bold relay-text-[16px]" + className="relay-w-max relay-h-[50px] relay-shrink-0 relay-overflow-hidden relay-px-3 relay-font-bold relay-text-[16px]" > Select Token diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index 45534e17a..c2854f9af 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -691,7 +691,7 @@ /* Widget */ --relay-colors-widget-background: white; - --relay-colors-widget-card-background: var(--relay-colors-gray-1); + --relay-colors-widget-card-background: white; --relay-colors-widget-selector-background: var(--relay-colors-gray-2); --relay-colors-widget-selector-hover-background: var(--relay-colors-gray-3); --relay-colors-widget-swap-currency-button-border-color: var(--relay-colors-primary4); @@ -726,6 +726,7 @@ /* Widget/Modal backgrounds */ --relay-colors-widget-background: var(--relay-colors-gray-1); + --relay-colors-widget-card-background: var(--relay-colors-gray-1); --relay-colors-modal-background: var(--relay-colors-gray-1); /* Radix scale dark overrides */ From c86ffd18a00d9e8b509343a864fa5781da163f70 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 14:15:28 -0500 Subject: [PATCH 14/25] fix buton issue --- packages/ui/src/components/primitives/Button.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/ui/src/components/primitives/Button.tsx b/packages/ui/src/components/primitives/Button.tsx index 3a357156c..55b320e79 100644 --- a/packages/ui/src/components/primitives/Button.tsx +++ b/packages/ui/src/components/primitives/Button.tsx @@ -8,10 +8,8 @@ const buttonVariants = cva( 'relay-transition-all relay-duration-200 relay-ease-out relay-select-none', 'relay-gap-2 relay-inline-flex relay-items-center relay-leading-[20px]', 'focus-visible:relay-ring-2 focus-visible:relay-ring-[var(--relay-colors-focus-color)] focus-visible:relay-ring-offset-2', - 'active:relay-scale-[0.98]', 'disabled:relay-cursor-not-allowed disabled:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:relay-text-[color:var(--relay-colors-button-disabled-color)]', - 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]', - 'disabled:active:relay-scale-100' + 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]' ].join(' '), { variants: { From 346d984bbffcfb066739b6467065ae76ce2ab945 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 14:17:22 -0500 Subject: [PATCH 15/25] add back from token to demo --- demo/pages/ui/swap.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/demo/pages/ui/swap.tsx b/demo/pages/ui/swap.tsx index 4b101228c..28be9ae45 100644 --- a/demo/pages/ui/swap.tsx +++ b/demo/pages/ui/swap.tsx @@ -46,15 +46,14 @@ const SwapWidgetPage: NextPage = () => { setLinkWalletPromise(undefined) } }) - const [fromToken, setFromToken] = useState() - // { - // chainId: 8453, - // address: '0x0000000000000000000000000000000000000000', - // decimals: 18, - // name: 'ETH', - // symbol: 'ETH', - // logoURI: 'https://assets.relay.link/icons/currencies/eth.png' - // } + const [fromToken, setFromToken] = useState({ + chainId: 8453, + address: '0x0000000000000000000000000000000000000000', + decimals: 18, + name: 'ETH', + symbol: 'ETH', + logoURI: 'https://assets.relay.link/icons/currencies/eth.png' + }) const [toToken, setToToken] = useState({ chainId: 10, address: '0x0000000000000000000000000000000000000000', From 86845aab78cbe036adc0655ec34e146cc0ea0703 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 14:25:33 -0500 Subject: [PATCH 16/25] Update dark3 color --- packages/ui/src/styles/base.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index c2854f9af..ccdaa1f66 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -257,7 +257,7 @@ --relay-colors-violet-dark-1: #0E0E23; --relay-colors-violet-dark-2: #141331; - --relay-colors-violet-dark-3: #002216; + --relay-colors-violet-dark-3: #216; --relay-colors-violet-dark-4: #2F0093; --relay-colors-violet-dark-5: #3800A8; --relay-colors-violet-dark-6: #4016B8; From fcd4bc75b8617b7b032625b3cf17990ef63aa1ed Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 14:27:55 -0500 Subject: [PATCH 17/25] Add back but down scale on press --- packages/ui/src/components/primitives/Button.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/primitives/Button.tsx b/packages/ui/src/components/primitives/Button.tsx index 55b320e79..3a357156c 100644 --- a/packages/ui/src/components/primitives/Button.tsx +++ b/packages/ui/src/components/primitives/Button.tsx @@ -8,8 +8,10 @@ const buttonVariants = cva( 'relay-transition-all relay-duration-200 relay-ease-out relay-select-none', 'relay-gap-2 relay-inline-flex relay-items-center relay-leading-[20px]', 'focus-visible:relay-ring-2 focus-visible:relay-ring-[var(--relay-colors-focus-color)] focus-visible:relay-ring-offset-2', + 'active:relay-scale-[0.98]', 'disabled:relay-cursor-not-allowed disabled:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:relay-text-[color:var(--relay-colors-button-disabled-color)]', - 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]' + 'disabled:hover:relay-bg-[var(--relay-colors-button-disabled-background)] disabled:hover:relay-text-[color:var(--relay-colors-button-disabled-color)]', + 'disabled:active:relay-scale-100' ].join(' '), { variants: { From 7628f360d673b540466fe43920fb2fb1e1c8ae3a Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 14:58:39 -0500 Subject: [PATCH 18/25] Update animate in icon --- .../common/TransactionModal/steps/ErrorStep.tsx | 2 +- .../common/TransactionModal/steps/SwapSuccessStep.tsx | 4 ++-- packages/ui/tailwind.config.ts | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx index 310824d7f..14b0d6d02 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx @@ -110,7 +110,7 @@ export const ErrorStep: FC = ({ justify="between" className="relay-w-full" > -
+
{isRefund ? ( diff --git a/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx index 5aaaa4bcc..eb0a7845a 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx @@ -167,7 +167,7 @@ export const SwapSuccessStep: FC = ({ return isDelayedTx ? ( <> -
+
= ({ ) : ( <> -
+
Date: Tue, 3 Mar 2026 15:01:36 -0500 Subject: [PATCH 19/25] feat: changeset --- .changeset/rich-eagles-roll.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rich-eagles-roll.md diff --git a/.changeset/rich-eagles-roll.md b/.changeset/rich-eagles-roll.md new file mode 100644 index 000000000..03646df12 --- /dev/null +++ b/.changeset/rich-eagles-roll.md @@ -0,0 +1,5 @@ +--- +'@relayprotocol/relay-kit-ui': major +--- + +Migrate styling system from Panda CSS to Tailwind CSS From 71425cf0c62630f9f1193e76773a811903158eb7 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Tue, 3 Mar 2026 16:50:07 -0500 Subject: [PATCH 20/25] Upgrade to tailwind v4 --- packages/ui/package.json | 11 +- packages/ui/postcss.config.mjs | 3 +- packages/ui/scripts/scope-css.mjs | 52 +- .../ui/src/components/common/AmountInput.tsx | 8 +- .../src/components/common/BalanceDisplay.tsx | 4 +- .../src/components/common/CopyToClipBoard.tsx | 2 +- .../components/common/CustomAddressModal.tsx | 28 +- .../ui/src/components/common/ErrorWell.tsx | 10 +- .../src/components/common/LoadingSpinner.tsx | 2 +- packages/ui/src/components/common/Modal.tsx | 8 +- .../components/common/MultiWalletDropdown.tsx | 22 +- .../components/common/PercentageButtons.tsx | 28 +- .../common/SlippageToleranceConfig.tsx | 50 +- .../ui/src/components/common/StepIcon.tsx | 2 +- .../common/TokenSelector/ChainFilter.tsx | 52 +- .../common/TokenSelector/ChainFilterRow.tsx | 24 +- .../TokenSelector/ChainFilterSidebar.tsx | 40 +- .../common/TokenSelector/ChainShortcuts.tsx | 14 +- .../TokenSelector/CompactChainFilter.tsx | 28 +- .../TokenSelector/MobileChainSelector.tsx | 46 +- .../common/TokenSelector/PaymentMethod.tsx | 38 +- .../common/TokenSelector/PaymentTokenList.tsx | 38 +- .../common/TokenSelector/SuggestedTokens.tsx | 4 +- .../common/TokenSelector/TagPill.tsx | 2 +- .../common/TokenSelector/TokenList.tsx | 40 +- .../common/TokenSelector/TokenSelector.tsx | 48 +- .../triggers/PaymentMethodTrigger.tsx | 24 +- .../TokenSelector/triggers/TokenTrigger.tsx | 18 +- .../TransactionModal/DepositAddressModal.tsx | 6 +- .../DepositAddressModalRenderer.tsx | 2 +- .../TransactionModal/TransactionModal.tsx | 6 +- .../steps/ApprovalPlusSwapStep.tsx | 46 +- .../steps/DepositAddressValidatingStep.tsx | 10 +- .../TransactionModal/steps/ErrorStep.tsx | 46 +- .../steps/SwapConfirmationStep.tsx | 54 +- .../steps/SwapSuccessStep.tsx | 80 +- .../steps/TransactionsByChain.tsx | 12 +- .../steps/WaitingForDepositStep.tsx | 36 +- .../common/UnverifiedTokenModal.tsx | 32 +- .../components/primitives/AccessibleList.tsx | 4 +- .../ui/src/components/primitives/Anchor.tsx | 20 +- .../ui/src/components/primitives/Button.tsx | 84 +- .../src/components/primitives/ChainIcon.tsx | 2 +- .../components/primitives/ChainTokenIcon.tsx | 6 +- .../src/components/primitives/Collapsible.tsx | 10 +- .../ui/src/components/primitives/Dialog.tsx | 42 +- .../ui/src/components/primitives/Dropdown.tsx | 24 +- .../ui/src/components/primitives/Flex.tsx | 36 +- .../ui/src/components/primitives/Input.tsx | 30 +- .../ui/src/components/primitives/Pill.tsx | 24 +- .../ui/src/components/primitives/Skeleton.tsx | 2 +- .../components/primitives/SlippageButton.tsx | 2 +- .../ui/src/components/primitives/Switch.tsx | 24 +- .../ui/src/components/primitives/Tabs.tsx | 16 +- .../ui/src/components/primitives/Text.tsx | 58 +- .../ui/src/components/primitives/Tooltip.tsx | 10 +- .../src/components/widgets/FeeBreakdown.tsx | 52 +- .../widgets/FetchingQuoteLoader.tsx | 4 +- .../OnrampWidget/modals/OnrampModal.tsx | 2 +- .../modals/steps/OnrampConfirmingStep.tsx | 16 +- .../modals/steps/OnrampMoonPayStep.tsx | 12 +- .../steps/OnrampProcessingPassthroughStep.tsx | 26 +- .../modals/steps/OnrampProcessingStepUI.tsx | 42 +- .../modals/steps/OnrampSuccessStep.tsx | 34 +- .../OnrampWidget/widget/FiatCurrencyModal.tsx | 44 +- .../widgets/OnrampWidget/widget/index.tsx | 50 +- .../components/widgets/PriceImpactTooltip.tsx | 20 +- .../ui/src/components/widgets/SwapButton.tsx | 8 +- .../widgets/SwapWidget/GasTopUpSection.tsx | 6 +- .../widgets/SwapWidget/PriceImpact.tsx | 6 +- .../components/widgets/SwapWidget/index.tsx | 78 +- .../widgets/TokenSelectorContainer.tsx | 2 +- .../components/widgets/WidgetErrorWell.tsx | 32 +- packages/ui/src/styles/base.css | 116 ++- packages/ui/src/utils/cn.ts | 6 +- packages/ui/src/utils/steps.ts | 4 +- packages/ui/tailwind.config.ts | 140 --- pnpm-lock.yaml | 853 ++++++++++-------- 78 files changed, 1472 insertions(+), 1451 deletions(-) delete mode 100644 packages/ui/tailwind.config.ts diff --git a/packages/ui/package.json b/packages/ui/package.json index 7d08633e7..af3ccfc22 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -32,12 +32,12 @@ "build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./_cjs --removeComments --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./_cjs/package.json", "build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./_esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./_esm/package.json", "build:types": "tsc --project ./tsconfig.build.json --module esnext --outDir ./_types --declarationDir ./_types --emitDeclarationOnly --declaration --declarationMap", - "build:css": "mkdir -p dist && tailwindcss -i src/styles/base.css -o dist/styles.css --config tailwind.config.ts --minify && node scripts/scope-css.mjs", + "build:css": "mkdir -p dist && npx @tailwindcss/cli -i src/styles/base.css -o dist/styles.css --minify && node scripts/scope-css.mjs", "copy:css": "cp dist/styles.css _cjs/src && cp dist/styles.css _esm/src", "dev": "concurrently \"pnpm run clean\" \"pnpm run dev:esm\" \"pnpm run dev:types\" \"pnpm run dev:css\"", "dev:esm": "tsc --watch --project ./tsconfig.build.json --module es2020 --outDir ./_esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./_esm/package.json", "dev:types": "tsc --watch --project ./tsconfig.build.json --module esnext --outDir ./_types --declarationDir ./_types --emitDeclarationOnly --declaration --declarationMap", - "dev:css": "tailwindcss -i src/styles/base.css -o dist/styles.css --config tailwind.config.ts --watch", + "dev:css": "npx @tailwindcss/cli -i src/styles/base.css -o dist/styles.css --watch", "clean": "rimraf ./_esm ./_cjs ./_types", "typecheck": "tsc --noEmit", "lint": "pnpm eslint" @@ -45,7 +45,8 @@ "devDependencies": { "@eslint/js": "^9.5.0", "@types/eslint__js": "^8.42.3", - "autoprefixer": "^10.4.0", + "@tailwindcss/cli": "^4.0.0", + "@tailwindcss/postcss": "^4.0.0", "eslint": "~8.57.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-react": "^7.34.2", @@ -54,7 +55,7 @@ "node-fetch": "^3.3.2", "postcss": "^8", "rimraf": "^5.0.5", - "tailwindcss": "^3.4.0", + "tailwindcss": "^4.0.0", "typescript": "5.4.5", "typescript-eslint": "^7.13.1", "@types/react": "^19.0.0", @@ -95,7 +96,7 @@ "dayjs": "^1.11.11", "fuse.js": "^7.0.0", "qrcode.react": "^4.1.0", - "tailwind-merge": "^2.2.0", + "tailwind-merge": "^3.0.0", "usehooks-ts": "^3.1.0" }, "keywords": [ diff --git a/packages/ui/postcss.config.mjs b/packages/ui/postcss.config.mjs index 2b75bd8a7..1d5a3b24f 100644 --- a/packages/ui/postcss.config.mjs +++ b/packages/ui/postcss.config.mjs @@ -1,6 +1,5 @@ export default { plugins: { - tailwindcss: {}, - autoprefixer: {} + '@tailwindcss/postcss': {} } } diff --git a/packages/ui/scripts/scope-css.mjs b/packages/ui/scripts/scope-css.mjs index bf921919f..9f1fae746 100644 --- a/packages/ui/scripts/scope-css.mjs +++ b/packages/ui/scripts/scope-css.mjs @@ -1,8 +1,16 @@ /** - * Post-processes dist/styles.css to scope Tailwind's global - * `*, ::before, ::after` and `::backdrop` CSS variable initialization - * blocks to `.relay-kit-reset`. This prevents the component library - * from polluting the consuming app's global CSS namespace. + * Post-processes dist/styles.css to scope Tailwind v4's global + * CSS declarations to `.relay-kit-reset`. This prevents the component + * library from polluting the consuming app's global CSS namespace. + * + * Tailwind v4 generates: + * 1. @layer theme { :root, :host { --relay-* } } + * → Scope :root,:host to .relay-kit-reset + * 2. @property --tw-* { ... } + * → Cannot be scoped (global by CSS spec). Low risk since + * they use inherits:false and --tw-* names. + * 3. @layer properties { @supports(...) { *, ::before, ::after, ::backdrop { --tw-* } } } + * → Scope to .relay-kit-reset (fallback for browsers without @property) */ import { readFileSync, writeFileSync } from 'fs' import { resolve, dirname } from 'path' @@ -13,38 +21,32 @@ const cssPath = resolve(__dirname, '../dist/styles.css') let css = readFileSync(cssPath, 'utf-8') -// Pattern: `*, :after, :before { --tw-*: ...; --tw-*: ...; }` -// This block only contains --tw-* CSS custom property declarations. -// Scope it to .relay-kit-reset so it doesn't apply globally. +// 1. Scope the theme layer's `:root, :host` block. +// Pattern: `@layer theme { :root, :host { ... } }` +// We scope each selector to `.relay-kit-reset`. css = css.replace( - /(\*\s*,\s*(?::after|::after)\s*,\s*(?::before|::before))\s*\{([^}]*)\}/g, - (match, selector, body) => { - // Only scope if the block contains exclusively --tw-* declarations - const declarations = body.split(';').map((d) => d.trim()).filter(Boolean) - const allTwVars = declarations.every((d) => d.startsWith('--tw-')) - if (allTwVars) { - const scoped = selector - .split(',') - .map((s) => `.relay-kit-reset ${s.trim()}`) - .join(',') - return `${scoped}{${body}}` - } - return match - } + /(@layer\s+theme\s*\{)\s*(:root\s*,\s*:host)\s*\{/, + '$1\n .relay-kit-reset {' ) -// Pattern: `::backdrop { --tw-*: ...; }` +// 2. Scope the properties layer's `*, ::before, ::after, ::backdrop` block. +// Pattern: `*, ::before, ::after, ::backdrop { --tw-*: ...; }` +// This is inside @layer properties { @supports(...) { ... } } css = css.replace( - /(? { const declarations = body.split(';').map((d) => d.trim()).filter(Boolean) const allTwVars = declarations.every((d) => d.startsWith('--tw-')) if (allTwVars) { - return `.relay-kit-reset ${selector}{${body}}` + const scoped = selector + .split(',') + .map((s) => `.relay-kit-reset ${s.trim()}`) + .join(', ') + return `${scoped} {${body}}` } return match } ) writeFileSync(cssPath, css) -console.log('Scoped Tailwind base variables to .relay-kit-reset') +console.log('Scoped Tailwind v4 base variables to .relay-kit-reset') diff --git a/packages/ui/src/components/common/AmountInput.tsx b/packages/ui/src/components/common/AmountInput.tsx index 27121bc24..2e2747ce5 100644 --- a/packages/ui/src/components/common/AmountInput.tsx +++ b/packages/ui/src/components/common/AmountInput.tsx @@ -26,10 +26,10 @@ const AmountInput: FC = ({ size="large" className={cn( 'ph-no-capture', - 'relay-w-full relay-bg-none relay-bg-transparent relay-font-bold relay-text-[32px]', - '!relay-px-0 !relay-py-0', - 'focus:relay-shadow-none focus:relay-outline-none', - 'placeholder:relay-text-[color:var(--relay-colors-gray12)]', + 'relay:w-full relay:bg-none relay:bg-transparent relay:font-bold relay:text-[32px]', + 'relay:!px-0 relay:!py-0', + 'relay:focus:shadow-none relay:focus:outline-none', + 'relay:placeholder:text-[color:var(--relay-colors-gray12)]', inputProps.className )} placeholder={inputProps.placeholder ?? '0'} diff --git a/packages/ui/src/components/common/BalanceDisplay.tsx b/packages/ui/src/components/common/BalanceDisplay.tsx index e723abf70..9056f4517 100644 --- a/packages/ui/src/components/common/BalanceDisplay.tsx +++ b/packages/ui/src/components/common/BalanceDisplay.tsx @@ -35,7 +35,7 @@ export const BalanceDisplay: FC = ({ if (pending) { return ( - + {hideBalanceLabel ? 'pending' : 'Balance: pending'} @@ -44,7 +44,7 @@ export const BalanceDisplay: FC = ({ } return ( - + {isConnected ? ( <> {isLoading ? ( diff --git a/packages/ui/src/components/common/CopyToClipBoard.tsx b/packages/ui/src/components/common/CopyToClipBoard.tsx index 7114f83f4..f469be220 100644 --- a/packages/ui/src/components/common/CopyToClipBoard.tsx +++ b/packages/ui/src/components/common/CopyToClipBoard.tsx @@ -44,7 +44,7 @@ export const CopyToClipBoard: FC = ({ text }) => { style={{ color: 'var(--relay-colors-gray9)' }} className="hover:!relay-text-[color:var(--relay-colors-gray11)]" > - + {isCopied ? ( ) : ( diff --git a/packages/ui/src/components/common/CustomAddressModal.tsx b/packages/ui/src/components/common/CustomAddressModal.tsx index d9e7ea3b3..563448f50 100644 --- a/packages/ui/src/components/common/CustomAddressModal.tsx +++ b/packages/ui/src/components/common/CustomAddressModal.tsx @@ -172,23 +172,23 @@ export const CustomAddressModal: FC = ({ trigger={null} open={open} onOpenChange={onOpenChange} - className="relay-overflow-hidden" + className="relay:overflow-hidden" > To Address - + 0 ? '42px' : '16px' }} @@ -210,7 +210,7 @@ export const CustomAddressModal: FC = ({ )} {isLoading && ( -
+
)} @@ -246,7 +246,7 @@ export const CustomAddressModal: FC = ({ {didResolveLighterFromEvm && resolvedLighterIndex ? ( = ({ {!connectedAddressSet && address && isConnected ? ( = ({ color="#FFA01C" width={16} height={16} - className="relay-shrink-0" + className="relay:shrink-0" /> This isn't the connected wallet address. Please ensure that the @@ -283,7 +283,7 @@ export const CustomAddressModal: FC = ({ {!multiWalletSupportEnabled && isConnected ? ( connectedAddressSet ? ( = ({ {filteredRecentCustomAddresses.length > 0 ? ( <> Recent addresses - + {filteredRecentCustomAddresses.map((address) => ( { onConfirmed(address) onOpenChange(false) @@ -344,7 +344,7 @@ export const CustomAddressModal: FC = ({ disabled={ isLoading || !isValidAddress(toChain?.vmType, address, toChain?.id) } - className="relay-justify-center" + className="relay:justify-center" onClick={() => { if (isValidAddress(toChain?.vmType, address, toChain?.id)) { // Save the address to custom addresses if it's not a connected wallet address diff --git a/packages/ui/src/components/common/ErrorWell.tsx b/packages/ui/src/components/common/ErrorWell.tsx index 62337670d..2c0e0693d 100644 --- a/packages/ui/src/components/common/ErrorWell.tsx +++ b/packages/ui/src/components/common/ErrorWell.tsx @@ -50,15 +50,15 @@ const ErrorWell: React.FC = ({ error, hasTxHashes, fromChain }) => { diff --git a/packages/ui/src/components/common/LoadingSpinner.tsx b/packages/ui/src/components/common/LoadingSpinner.tsx index 2839e1168..410a1adaf 100644 --- a/packages/ui/src/components/common/LoadingSpinner.tsx +++ b/packages/ui/src/components/common/LoadingSpinner.tsx @@ -34,7 +34,7 @@ export const LoadingSpinner: FC<{ className?: string }> = ({ className }) => { return ( diff --git a/packages/ui/src/components/common/Modal.tsx b/packages/ui/src/components/common/Modal.tsx index a95f5d978..6365273b9 100644 --- a/packages/ui/src/components/common/Modal.tsx +++ b/packages/ui/src/components/common/Modal.tsx @@ -47,12 +47,12 @@ export const Modal: FC< - + = ({ } }} placeholder="2" - containerClassName="relay-w-full" + containerClassName="relay:w-full" className={cn( - 'relay-h-9 !relay-pr-7 relay-border-none relay-text-right relay-w-full', - 'max-[520px]:relay-bg-transparent max-[520px]:relay-border max-[520px]:relay-border-solid max-[520px]:relay-border-[var(--relay-colors-gray-5)]' + 'relay:h-9 relay:!pr-7 relay:border-none relay:text-right relay:w-full', + 'relay:max-[520px]:bg-transparent relay:max-[520px]:border relay:max-[520px]:border-solid relay:max-[520px]:border-[var(--relay-colors-gray-5)]' )} inputStyle={{ color: tokenToColor(slippageRatingColor) }} /> % @@ -314,7 +314,7 @@ export const SlippageToleranceConfig: FC = ({ aria-label="Slippage Tolerance Configuration" color="ghost" size="none" - className="relay-items-center relay-justify-center relay-gap-1 relay-p-2 relay-rounded-[12px] relay-border relay-border-solid relay-border-[var(--relay-colors-gray5)] relay-h-9 relay-px-[10px]" + className="relay:items-center relay:justify-center relay:gap-1 relay:p-2 relay:rounded-[12px] relay:border relay:border-solid relay:border-[var(--relay-colors-gray5)] relay:h-9 relay:px-[10px]" style={{ color: tokenToColor(slippageRatingColor) ?? 'var(--relay-colors-gray9)', backgroundColor: 'var(--relay-colors-widget-card-background)' @@ -343,7 +343,7 @@ export const SlippageToleranceConfig: FC = ({ } return ( -
+
{isMobile ? ( = ({ } }} trigger={triggerButton} - className="relay-w-full relay-min-h-[262px] relay-max-h-[90vh]" + className="relay:w-full relay:min-h-[262px] relay:max-h-[90vh]" > - + Max Slippage @@ -380,7 +380,7 @@ export const SlippageToleranceConfig: FC = ({ contentProps={{ align: 'end', sideOffset: 5, - className: 'relay-max-w-[188px] relay-mx-0 relay-p-3', + className: 'relay:max-w-[188px] relay:mx-0 relay:p-3', avoidCollisions: false, onCloseAutoFocus: (e) => { e.preventDefault() @@ -389,22 +389,22 @@ export const SlippageToleranceConfig: FC = ({ > - + Max Slippage If the price exceeds the maximum slippage percentage, the transaction will revert. } > - + diff --git a/packages/ui/src/components/common/StepIcon.tsx b/packages/ui/src/components/common/StepIcon.tsx index 01b2ee6f9..96157202b 100644 --- a/packages/ui/src/components/common/StepIcon.tsx +++ b/packages/ui/src/components/common/StepIcon.tsx @@ -39,7 +39,7 @@ export const StepIcon: FC = ({ stepId, chainId }) => { return ( {getIconForStep()} diff --git a/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx b/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx index cb51af085..78f292ec0 100644 --- a/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx +++ b/packages/ui/src/components/common/TokenSelector/ChainFilter.tsx @@ -91,15 +91,15 @@ const ChainFilter: FC = ({ aria-label={`Chain filter`} color="ghost" size="none" - className="relay-gap-2 relay-h-[40px] relay-w-full !relay-px-4 relay-cursor-pointer relay-flex relay-content-center relay-leading-[20px] relay-bg-[var(--relay-colors-dropdown-background)] relay-rounded-dropdown" + className="relay:gap-2 relay:h-[40px] relay:w-full relay:!px-4 relay:cursor-pointer relay:flex relay:content-center relay:leading-[20px] relay:bg-[var(--relay-colors-dropdown-background)] relay:rounded-dropdown" > - + {value.id ? ( ) : ( @@ -111,8 +111,8 @@ const ChainFilter: FC = ({ @@ -122,14 +122,14 @@ const ChainFilter: FC = ({ contentProps={{ align: 'start', avoidCollisions: false, - className: 'relay-p-0 relay-mx-0', + className: 'relay:p-0 relay:mx-0', style: { width: 'var(--radix-popper-anchor-width)', minWidth: 'var(--radix-popper-anchor-width)' } }} > - + = ({ /> {filteredChains ? ( @@ -152,7 +152,7 @@ const ChainFilter: FC = ({ onSelect(chain) setChainSearchInput('') }} - className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" + className="relay:p-[8px] relay:rounded-[4px] relay:cursor-pointer relay:bg-[var(--relay-colors-modal-background)] relay:hover:bg-[var(--relay-colors-gray3)]" > = ({ ) }) ) : ( - + No results. ) @@ -177,7 +177,7 @@ const ChainFilter: FC = ({ onSelect(allChainsOption) setChainSearchInput('') }} - className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" + className="relay:p-[8px] relay:rounded-[4px] relay:cursor-pointer relay:bg-[var(--relay-colors-modal-background)] relay:hover:bg-[var(--relay-colors-gray3)]" > = ({ {starredChains.length > 0 && ( <> - - + + @@ -200,7 +200,7 @@ const ChainFilter: FC = ({ Long-press to star a chain } > - + = ({ onSelect(chain) setChainSearchInput('') }} - className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" + className="relay:p-[8px] relay:rounded-[4px] relay:cursor-pointer relay:bg-[var(--relay-colors-modal-background)] relay:hover:bg-[var(--relay-colors-gray3)]" > = ({ )} - + Chains A-Z {alphabeticalChains.map((chain) => { @@ -247,7 +247,7 @@ const ChainFilter: FC = ({ onSelect(chain) setChainSearchInput('') }} - className="relay-p-[8px] relay-rounded-[4px] relay-cursor-pointer relay-bg-[var(--relay-colors-modal-background)] hover:relay-bg-[var(--relay-colors-gray3)]" + className="relay:p-[8px] relay:rounded-[4px] relay:cursor-pointer relay:bg-[var(--relay-colors-modal-background)] relay:hover:bg-[var(--relay-colors-gray3)]" > = ({ return ( {chain.name} @@ -380,7 +380,7 @@ const ChainFilterRow: FC = ({ } return ( -
+
{ @@ -396,14 +396,14 @@ const ChainFilterRow: FC = ({ onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} onTouchMove={handleTouchMove} - className="relay-gap-2 relay-cursor-pointer relay-shrink-0 relay-content-center relay-w-full relay-relative relay-select-none" + className="relay:gap-2 relay:cursor-pointer relay:shrink-0 relay:content-center relay:w-full relay:relative relay:select-none" > {('displayName' in chain && chain.displayName) || chain.name} {showStar && isStarred && ( - + )} @@ -413,7 +413,7 @@ const ChainFilterRow: FC = ({ {dropdownOpen && (
{ e.stopPropagation() handleToggleStar() @@ -422,18 +422,18 @@ const ChainFilterRow: FC = ({ onTouchStart={(e) => e.stopPropagation()} > - + {isStarred ? 'Unstar chain' : 'Star chain'} diff --git a/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx b/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx index 0198f411a..979202d76 100644 --- a/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx +++ b/packages/ui/src/components/common/TokenSelector/ChainFilterRow.tsx @@ -90,7 +90,7 @@ export const ChainFilterRow: FC = ({ return ( {chain.name} @@ -99,21 +99,21 @@ export const ChainFilterRow: FC = ({ } return ( -
+
{ e.preventDefault() setDropdownOpen(true) }} - className="relay-gap-2 relay-cursor-pointer relay-shrink-0 relay-content-center relay-w-full relay-relative relay-select-none" + className="relay:gap-2 relay:cursor-pointer relay:shrink-0 relay:content-center relay:w-full relay:relative relay:select-none" > {('displayName' in chain && chain.displayName) || chain.name} {showStar && isStarred && ( - + )} @@ -124,7 +124,7 @@ export const ChainFilterRow: FC = ({ {dropdownOpen && (
{ e.stopPropagation() handleToggleStar() @@ -133,18 +133,18 @@ export const ChainFilterRow: FC = ({ onTouchStart={(e) => e.stopPropagation()} > - + {isStarred ? 'Unstar chain' : 'Star chain'} @@ -169,12 +169,12 @@ export const ChainSearchInput = forwardRef< ref={ref} placeholder={placeholder} icon={ - + } - containerClassName="relay-w-full relay-h-[40px] relay-mb-2" - className="relay-w-full relay-border relay-border-solid relay-border-[var(--relay-colors-subtle-border-color)] relay-bg-[var(--relay-colors-modal-background)] [&::placeholder]:relay-text-ellipsis" + containerClassName="relay:w-full relay:h-[40px] relay:mb-2" + className="relay:w-full relay:border relay:border-solid relay:border-[var(--relay-colors-subtle-border-color)] relay:bg-[var(--relay-colors-modal-background)] relay:[&::placeholder]:text-ellipsis" value={value} onChange={(e) => onChange((e.target as HTMLInputElement).value)} onKeyDown={onKeyDown} diff --git a/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx b/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx index 5f40d855b..557f2ae39 100644 --- a/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx +++ b/packages/ui/src/components/common/TokenSelector/ChainFilterSidebar.tsx @@ -112,7 +112,7 @@ export const ChainFilterSidebar: FC = ({ return ( { @@ -144,7 +144,7 @@ export const ChainFilterSidebar: FC = ({ } } }} - className="relay-flex relay-flex-col relay-w-full relay-h-full" + className="relay:flex relay:flex-col relay:w-full relay:h-full" > = ({ {filteredChains ? ( @@ -212,8 +212,8 @@ export const ChainFilterSidebar: FC = ({ }} ref={isSameChainSelected ? activeChainRef : null} className={cn( - 'relay-p-2 relay-flex relay-items-center relay-gap-2 relay-relative relay-transition-colors relay-duration-150 relay-outline-none relay-rounded-lg relay-focus-inset', - !isSameChainSelected && 'hover:relay-bg-[rgba(var(--relay-colors-gray-rgb,0,0,0),0.1)]' + 'relay:p-2 relay:flex relay:items-center relay:gap-2 relay:relative relay:transition-colors relay:duration-150 relay:outline-none relay:rounded-lg relay:focus-inset', + !isSameChainSelected && 'relay:hover:bg-[rgba(var(--relay-colors-gray-rgb,0,0,0),0.1)]' )} style={isSameChainSelected ? { backgroundColor: 'var(--relay-colors-gray6)' } : undefined} > @@ -234,8 +234,8 @@ export const ChainFilterSidebar: FC = ({ {starredChains.length > 0 && ( <> - - + + @@ -246,7 +246,7 @@ export const ChainFilterSidebar: FC = ({ Right-click to star a chain } > - + = ({ )} - + Chains A-Z {alphabeticalChains.map((chain) => { @@ -403,8 +403,8 @@ const ChainFilterRow: FC = ({ onClick={onClick} ref={isActive ? activeChainRef : null} className={cn( - 'relay-p-2 relay-flex relay-items-center relay-gap-2 relay-relative relay-transition-colors relay-duration-150 relay-outline-none relay-rounded-lg relay-focus-inset', - !isActive && 'hover:relay-bg-[rgba(var(--relay-colors-gray-rgb,0,0,0),0.1)]' + 'relay:p-2 relay:flex relay:items-center relay:gap-2 relay:relative relay:transition-colors relay:duration-150 relay:outline-none relay:rounded-lg relay:focus-inset', + !isActive && 'relay:hover:bg-[rgba(var(--relay-colors-gray-rgb,0,0,0),0.1)]' )} style={{ ...(isActive ? { backgroundColor: 'var(--relay-colors-gray6)' } : undefined), @@ -421,7 +421,7 @@ const ChainFilterRow: FC = ({ } return ( - + @@ -126,7 +126,7 @@ export const MobileChainSelector: FC = ({ + = ({ /> } - containerClassName="relay-flex-1 relay-h-[40px]" - className="relay-w-full [&::placeholder]:relay-text-ellipsis" + containerClassName="relay:flex-1 relay:h-[40px]" + className="relay:w-full relay:[&::placeholder]:text-ellipsis" value={chainSearchInput} onChange={(e) => setChainSearchInput((e.target as HTMLInputElement).value) @@ -146,7 +146,7 @@ export const MobileChainSelector: FC = ({ color="ghost" size="none" onClick={onClose} - className="relay-p-2 relay-rounded-[8px] relay-flex relay-items-center relay-justify-center relay-min-w-[40px] relay-h-[40px] relay-text-[color:var(--relay-colors-gray9)]" + className="relay:p-2 relay:rounded-[8px] relay:flex relay:items-center relay:justify-center relay:min-w-[40px] relay:h-[40px] relay:text-[color:var(--relay-colors-gray9)]" > @@ -182,7 +182,7 @@ export const MobileChainSelector: FC = ({ } } }} - className="relay-flex relay-flex-col relay-w-full relay-flex-1 relay-overflow-y-auto [scrollbar-color:var(--relay-colors-gray5)_transparent]" + className="relay:flex relay:flex-col relay:w-full relay:flex-1 relay:overflow-y-auto [scrollbar-color:var(--relay-colors-gray5)_transparent]" > {filteredChains ? ( // Show search results without sections @@ -214,7 +214,7 @@ export const MobileChainSelector: FC = ({ diff --git a/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx b/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx index 78d461d3e..249393238 100644 --- a/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx +++ b/packages/ui/src/components/common/TransactionModal/DepositAddressModal.tsx @@ -167,7 +167,7 @@ const InnerDepositAddressModal: FC = ({ trigger={null} open={open} onOpenChange={onOpenChange} - className="relay-overflow-hidden relay-p-4 !relay-max-w-[412px]" + className="relay:overflow-hidden relay:p-4 relay:!max-w-[412px]" showCloseButton={true} onPointerDownOutside={(e) => { const dynamicModalElements = Array.from( @@ -184,9 +184,9 @@ const InnerDepositAddressModal: FC = ({ > - + {isWaitingForDeposit ? 'Manual Transfer' : 'Trade Details'} diff --git a/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx b/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx index ba86998ca..4dbad030a 100644 --- a/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx +++ b/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx @@ -171,7 +171,7 @@ export const DepositAddressModalRenderer: FC = ({ }) queryQuote(relayClient?.baseApiUrl, quoteParameters, { headers: { - 'relay-sdk-version': relayClient?.version ?? 'unknown', + 'relay:sdk-version': relayClient?.version ?? 'unknown', 'relay-kit-ui-version': relayClient?.uiVersion ?? 'unknown' } }) diff --git a/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx b/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx index b8945bda2..e1dc37873 100644 --- a/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx +++ b/packages/ui/src/components/common/TransactionModal/TransactionModal.tsx @@ -235,7 +235,7 @@ const InnerTransactionModal: FC = ({ trigger={null} open={open} onOpenChange={onOpenChange} - className="relay-overflow-hidden relay-p-4 !relay-max-w-[412px] max-[520px]:!relay-max-w-[unset]" + className="relay:overflow-hidden relay:p-4 relay:!max-w-[412px] max-[520px]:!relay-max-w-[unset]" showCloseButton={true} onPointerDownOutside={(e) => { const dynamicModalElements = Array.from( @@ -252,9 +252,9 @@ const InnerTransactionModal: FC = ({ > - + Transaction Details {progressStep === TransactionProgressStep.Confirmation || diff --git a/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx index 7e61b2e18..8305efd18 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/ApprovalPlusSwapStep.tsx @@ -46,22 +46,22 @@ export const ApprovalPlusSwapStep: FC = ({ align="center" justify="between" direction="column" - className="relay-shrink-0 bp500:relay-flex-row" + className="relay:shrink-0 relay:bp500:flex-row" > {fromAmountFormatted} {fromToken?.symbol} @@ -74,24 +74,24 @@ export const ApprovalPlusSwapStep: FC = ({ {toAmountFormatted} {toToken?.symbol} @@ -104,7 +104,7 @@ export const ApprovalPlusSwapStep: FC = ({ {steps?.map((step, index) => { const isCurrentStep = @@ -132,36 +132,36 @@ export const ApprovalPlusSwapStep: FC = ({ - + {step.id === 'approve' ? ( ) : ( )} - + {stepTitle} {isApproveStep && !hasTxHash && ( @@ -180,7 +180,7 @@ export const ApprovalPlusSwapStep: FC = ({ key={txHash} href={txUrl} target="_blank" - className="relay-text-[12px]" + className="relay:text-[12px]" > View Tx: {truncateAddress(txHash, '...', 6, 4)} @@ -192,12 +192,12 @@ export const ApprovalPlusSwapStep: FC = ({ {isCurrentStep && hasTxHash ? ( ) : step?.items?.every( (item) => item.status === 'complete' ) ? ( - + ) : null} @@ -205,8 +205,8 @@ export const ApprovalPlusSwapStep: FC = ({ {index !== (steps?.length || 0) - 1 && ( - - + + )} diff --git a/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx index 3960a54ab..cba453c80 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/DepositAddressValidatingStep.tsx @@ -23,15 +23,15 @@ export const DepositAddressValidatingStep: FC< <> - + Funds received. Your transaction is now in progress. Feel free to leave at any time, you can track your progress within the{' '} diff --git a/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx index 14b0d6d02..4ffa16adf 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/ErrorStep.tsx @@ -108,27 +108,27 @@ export const ErrorStep: FC = ({ direction="column" align="center" justify="between" - className="relay-w-full" + className="relay:w-full" > -
+
{isRefund ? ( - - + + ) : null} {!isRefund && isSolverStatusTimeout ? ( - + ) : null} {!isRefund && !isSolverStatusTimeout ? ( - - + + ) : null}
@@ -138,9 +138,9 @@ export const ErrorStep: FC = ({ !transaction?.data?.failReason ? ( - + It looks like an unknown issue occurred during the transaction. {fromToken && fromAmountFormatted && fromChain ? ` We've refunded ${fromAmountFormatted} ${fromToken.symbol} on ${fromChain.displayName}.` @@ -148,11 +148,11 @@ export const ErrorStep: FC = ({ {/* Transaction Pills */} - + {fromToken && fromChain ? ( = ({ tokenSymbol={fromToken.symbol} size="base" /> - + {fromAmountFormatted} {fromToken.symbol} @@ -168,14 +168,14 @@ export const ErrorStep: FC = ({ ? )} - - + + {toToken && toChain ? ( = ({ tokenSymbol={toToken.symbol} size="base" /> - + {toAmountFormatted} {toToken.symbol} @@ -193,7 +193,7 @@ export const ErrorStep: FC = ({ ) : ( - + {refundDetails ? ` We've refunded ${refundDetails.amountFormatted} ${refundDetails.currency?.symbol} on ${refundChain?.displayName}.` @@ -211,7 +211,7 @@ export const ErrorStep: FC = ({ <> = ({ /> - + @@ -244,7 +244,7 @@ export const ErrorStep: FC = ({ onClick={() => { onOpenChange(false) }} - className="relay-justify-center relay-w-full" + className="relay:justify-center relay:w-full" > Done @@ -256,7 +256,7 @@ export const ErrorStep: FC = ({ onClick={() => { onOpenChange(false) }} - className="relay-justify-center relay-w-full" + className="relay:justify-center relay:w-full" > Done diff --git a/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx index 447258d9d..d963a1cde 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/SwapConfirmationStep.tsx @@ -81,23 +81,23 @@ export const SwapConfirmationStep: FC = ({ align="center" justify="between" direction="column" - className="relay-shrink-0 bp500:relay-flex-row" + className="relay:shrink-0 relay:bp500:flex-row" > - + {fromChain?.displayName} - + {fromAmountFormatted} {fromToken?.symbol} @@ -105,25 +105,25 @@ export const SwapConfirmationStep: FC = ({ - + {toChain?.displayName} - + {toAmountFormatted} {toToken?.symbol} @@ -133,7 +133,7 @@ export const SwapConfirmationStep: FC = ({ Additional Gas @@ -145,15 +145,15 @@ export const SwapConfirmationStep: FC = ({ ) : null} {formattedSteps.map((step, index) => ( {index !== formattedSteps.length - 1 && ( - - + + )} @@ -193,17 +193,17 @@ export const StepRow: FC = ({ const relayClient = useRelayClient() const chains = relayClient?.chains return ( - - + + = ({ )} - + {action} {subText && ( - + {(() => { // Handle "Success: txhash" case with split colors and link if (subText.startsWith('Success:')) { @@ -244,7 +244,7 @@ export const StepRow: FC = ({ : undefined return ( - + = ({ href={txUrl} target="_blank" rel="noopener noreferrer" - className="relay-no-underline" + className="relay:no-underline" > = ({ : undefined return ( - + = ({ href={txUrl} target="_blank" rel="noopener noreferrer" - className="relay-no-underline" + className="relay:no-underline" > = ({ ) })()} {showSubTextSpinner && ( - + )} )} diff --git a/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx index eb0a7845a..2bb0f4e92 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/SwapSuccessStep.tsx @@ -167,14 +167,14 @@ export const SwapSuccessStep: FC = ({ return isDelayedTx ? ( <> -
+
= ({ - +
{isBitcoinOrigin || isBitcoinDestination ? `Bitcoin confirmation takes ${estimatedMinutes} minutes. Track progress on the transaction page.` : `Processing bridge, this will take ~${estimatedMinutes} ${estimatedMinutes === 1 ? 'min' : 'mins'}.`} - + {fromChain ? ( - + = ({ size="sm" chainRadius={2.5} /> - + {_fromAmountFormatted} {_fromToken?.symbol} ) : ( ? )} - - + + {toChain ? ( - + = ({ size="sm" chainRadius={2.5} /> - + {_toAmountFormatted} {_toToken?.symbol} @@ -249,7 +249,7 @@ export const SwapSuccessStep: FC = ({ You can close this modal while it finalizes on the blockchain. The transaction will continue in the background. @@ -270,7 +270,7 @@ export const SwapSuccessStep: FC = ({ View Refund Tx: {truncatedHash} @@ -281,7 +281,7 @@ export const SwapSuccessStep: FC = ({ Fetching refund transaction... @@ -300,7 +300,7 @@ export const SwapSuccessStep: FC = ({ View Tx: {truncatedHash} @@ -314,7 +314,7 @@ export const SwapSuccessStep: FC = ({ {!delayedTxUrl ? ( = ({ ) : null} - + {delayedTxUrl ? ( - + @@ -352,17 +352,17 @@ export const SwapSuccessStep: FC = ({ ) : ( <> -
+
@@ -370,7 +370,7 @@ export const SwapSuccessStep: FC = ({ {fillTime && fillTime !== '-' ? ( <> @@ -383,15 +383,15 @@ export const SwapSuccessStep: FC = ({ {_fromToken ? ( - + Sent - + = ({ /> {isLoadingTransaction ? ( ) : ( @@ -421,7 +421,7 @@ export const SwapSuccessStep: FC = ({ {truncateAddress(txHash, '...', 6, 4)} @@ -434,12 +434,12 @@ export const SwapSuccessStep: FC = ({ )} {_toToken ? ( - + Received - + = ({ /> {isLoadingTransaction ? ( ) : ( @@ -468,7 +468,7 @@ export const SwapSuccessStep: FC = ({ {truncateAddress(txHash, '...', 6, 4)} @@ -478,7 +478,7 @@ export const SwapSuccessStep: FC = ({ {/* Additional Gas - positioned below transaction hash with 8px spacing */} {formattedGasTopUpAmount && gasTopUpAmountCurrency ? ( - + = ({ - + {requestId ? ( { e.stopPropagation() @@ -514,7 +514,7 @@ export const SwapSuccessStep: FC = ({ @@ -525,7 +525,7 @@ export const SwapSuccessStep: FC = ({ onClick={() => { onOpenChange(false) }} - className="relay-justify-center relay-w-full" + className="relay:justify-center relay:w-full" > Done diff --git a/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx b/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx index 1a04be64f..c06e6fa7b 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/TransactionsByChain.tsx @@ -100,21 +100,21 @@ export const TransactionsByChain: FC = ({ // Return single container with all transactions return ( - + {transactions.map((transaction, idx) => ( - - + + {transaction.label} {transaction.isRefund ? ( {' '} Refunded @@ -176,7 +176,7 @@ export const TransactionsByChain: FC = ({ transaction.txHashes.length === 0 && isSolverStatusTimeout ? ( - + ) : null} diff --git a/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx b/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx index fa2b3d029..f183a4853 100644 --- a/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx +++ b/packages/ui/src/components/common/TransactionModal/steps/WaitingForDepositStep.tsx @@ -60,7 +60,7 @@ export const WaitingForDepositStep: FC = ({ <> Transfer funds manually from your {fromChain?.displayName} wallet to @@ -73,29 +73,29 @@ export const WaitingForDepositStep: FC = ({ Learn More - - + + Network {fromChain?.displayName} - + Amount to transfer {fromAmountFormatted ? ( <> {' '} - + {fromAmountFormatted} = ({ src={fromToken?.logoURI} width={20} height={20} - className="relay-rounded-full" + className="relay:rounded-full" /> {fromToken?.symbol} ) : ( - + )}{' '} @@ -117,22 +117,22 @@ export const WaitingForDepositStep: FC = ({ {isFetchingQuote ? ( - + ) : ( <> - + {truncateAddress(depositAddress, '...', 28, 4)} {qrcodeUrl ? ( - - + + @@ -196,7 +196,7 @@ export const WaitingForDepositStep: FC = ({ chainId={fromChain?.id} height={14} width={14} - className="relay-absolute relay-bottom-0 relay-right-0 relay-border relay-border-solid relay-border-white" + className="relay:absolute relay:bottom-0 relay:right-0 relay:border relay:border-solid relay:border-white" borderRadius={4} />
@@ -213,10 +213,10 @@ export const WaitingForDepositStep: FC = ({ diff --git a/packages/ui/src/components/common/UnverifiedTokenModal.tsx b/packages/ui/src/components/common/UnverifiedTokenModal.tsx index eed33baf3..137f6f091 100644 --- a/packages/ui/src/components/common/UnverifiedTokenModal.tsx +++ b/packages/ui/src/components/common/UnverifiedTokenModal.tsx @@ -52,48 +52,46 @@ export const UnverifiedTokenModal: FC = ({ onCloseButtonClicked={() => { onDecline?.(data?.token, data?.context) }} - className="relay-overflow-hidden relay-z-[10000001]" + className="relay:overflow-hidden relay:z-[10000001]" overlayZIndex={10000001} > Unverified Token - + {isValidTokenLogo ? ( {data?.token?.name} ) : null} - + - + This token isn't traded on leading U.S. centralized exchanges or frequently swapped on major DEXes. Always conduct your own research before trading. {data?.token?.address} @@ -103,14 +101,14 @@ export const UnverifiedTokenModal: FC = ({ - + - + @@ -146,7 +144,7 @@ export const UnverifiedTokenModal: FC = ({ onAcceptToken(data?.token, data?.context) }} color="warning" - className="relay-flex-1 relay-justify-center relay-px-4 relay-whitespace-nowrap" + className="relay:flex-1 relay:justify-center relay:px-4 relay:whitespace-nowrap" > I Understand diff --git a/packages/ui/src/components/primitives/AccessibleList.tsx b/packages/ui/src/components/primitives/AccessibleList.tsx index 90886f854..a039e383c 100644 --- a/packages/ui/src/components/primitives/AccessibleList.tsx +++ b/packages/ui/src/components/primitives/AccessibleList.tsx @@ -18,7 +18,7 @@ export const AccessibleList: FC = ({ type="single" loop={false} onValueChange={onSelect} - className={cn('relay-flex relay-flex-col', className)} + className={cn('relay:flex relay:flex-col', className)} > {children} @@ -40,7 +40,7 @@ export const AccessibleListItem = forwardRef< = ({ return iconUrl ? (
= ({ return chainId ? (
= ({ /> ) : tokenSymbol ? (
= ({ width={chainSize} height={chainSize} borderRadius={chainRadius} - className="relay-absolute relay-right-0 relay-bottom-0 relay-overflow-hidden relay-border relay-border-solid relay-border-[var(--relay-colors-modal-background)] relay-bg-[var(--relay-colors-modal-background)]" + className="relay:absolute relay:right-0 relay:bottom-0 relay:overflow-hidden relay:border relay:border-solid relay:border-[var(--relay-colors-modal-background)] relay:bg-[var(--relay-colors-modal-background)]" />
) : null diff --git a/packages/ui/src/components/primitives/Collapsible.tsx b/packages/ui/src/components/primitives/Collapsible.tsx index 4ca9250b5..25e223c6d 100644 --- a/packages/ui/src/components/primitives/Collapsible.tsx +++ b/packages/ui/src/components/primitives/Collapsible.tsx @@ -17,9 +17,9 @@ const CollapsibleContent = forwardRef< ref={forwardedRef} {...props} className={cn( - 'relay-overflow-hidden', - 'data-[state=open]:relay-animate-collapsible-down', - 'data-[state=closed]:relay-animate-collapsible-up', + 'relay:overflow-hidden', + 'relay:data-[state=open]:animate-collapsible-down', + 'relay:data-[state=closed]:animate-collapsible-up', className )} > @@ -37,7 +37,7 @@ const CollapsibleRoot = forwardRef< {children} @@ -54,7 +54,7 @@ const CollapsibleTrigger = forwardRef< ref={forwardedRef} {...props} className={cn( - 'relay-w-full relay-flex relay-items-center relay-justify-between relay-cursor-pointer', + 'relay:w-full relay:flex relay:items-center relay:justify-between relay:cursor-pointer', className )} > diff --git a/packages/ui/src/components/primitives/Dialog.tsx b/packages/ui/src/components/primitives/Dialog.tsx index fdeb43b69..cd370da58 100644 --- a/packages/ui/src/components/primitives/Dialog.tsx +++ b/packages/ui/src/components/primitives/Dialog.tsx @@ -21,8 +21,8 @@ const Overlay = forwardRef< ref={forwardedRef} {...props} className={cn( - 'relay-fixed relay-inset-0 relay-bg-black/50 relay-backdrop-blur-sm', - 'relay-animate-overlay-fade-in data-[state=closed]:relay-animate-overlay-fade-out', + 'relay:fixed relay:inset-0 relay:bg-black/50 relay:backdrop-blur-sm', + 'relay:animate-overlay-fade-in relay:data-[state=closed]:animate-overlay-fade-out', className, 'relay-kit-reset' )} @@ -33,13 +33,13 @@ const Overlay = forwardRef< }) const contentBase = [ - 'relay-bg-[var(--relay-colors-modal-background)]', - 'relay-rounded-[var(--relay-radii-modal-border-radius)]', - 'relay-border-modal', - 'relay-shadow-xl', - 'relay-fixed', - 'relay-max-h-[85vh] relay-overflow-y-auto', - 'focus:relay-outline-none' + 'relay:bg-[var(--relay-colors-modal-background)]', + 'relay:rounded-[var(--relay-radii-modal-border-radius)]', + 'relay:border-modal', + 'relay:shadow-xl', + 'relay:fixed', + 'relay:max-h-[85vh] relay:overflow-y-auto', + 'relay:focus:outline-none' ].join(' ') const Content = forwardRef< @@ -51,7 +51,7 @@ const Content = forwardRef< {children} @@ -68,23 +68,23 @@ const AnimatedContent = forwardRef< const isMobileFullScreen = isMobile && disableAnimation const mobileSlideUpClasses = [ - 'relay-bottom-0 relay-top-auto relay-left-0 relay-w-full', - 'relay-animate-dialog-slide-up', - 'data-[state=closed]:relay-animate-dialog-slide-down', - 'max-[520px]:relay-rounded-b-none' + 'relay:bottom-0 relay:top-auto relay:left-0 relay:w-full', + 'relay:animate-dialog-slide-up', + 'relay:data-[state=closed]:animate-dialog-slide-down', + 'relay:max-[520px]:rounded-b-none' ].join(' ') const mobileFullScreenClasses = [ - 'relay-top-0 relay-left-0 relay-w-full relay-h-full', - 'relay-max-h-full relay-rounded-none' + 'relay:top-0 relay:left-0 relay:w-full relay:h-full', + 'relay:max-h-full relay:rounded-none' ].join(' ') const desktopClasses = [ - 'relay-left-1/2 relay-top-1/2', - 'relay-min-w-[90vw] relay-max-w-[100vw]', - 'sm:relay-min-w-[400px] sm:relay-max-w-[532px]', - 'relay-animate-scale-in', - 'data-[state=closed]:relay-animate-scale-out' + 'relay:left-1/2 relay:top-1/2', + 'relay:min-w-[90vw] relay:max-w-[100vw]', + 'relay:sm:min-w-[400px] relay:sm:max-w-[532px]', + 'relay:animate-scale-in', + 'relay:data-[state=closed]:animate-scale-out' ].join(' ') return ( diff --git a/packages/ui/src/components/primitives/Dropdown.tsx b/packages/ui/src/components/primitives/Dropdown.tsx index c63755b39..d78cd7f8c 100644 --- a/packages/ui/src/components/primitives/Dropdown.tsx +++ b/packages/ui/src/components/primitives/Dropdown.tsx @@ -19,11 +19,11 @@ const DropdownMenuContent = forwardRef< {...props} ref={forwardedRef} className={cn( - 'relay-mx-4 relay-p-0 relay-rounded-[8px] relay-z-[10000002]', - 'relay-bg-[var(--relay-colors-modal-background)]', - 'relay-shadow-lg', - 'relay-border-dropdown', - 'relay-animate-content-fade-in', + 'relay:mx-4 relay:p-0 relay:rounded-[8px] relay:z-[10000002]', + 'relay:bg-[var(--relay-colors-modal-background)]', + 'relay:shadow-lg', + 'relay:border-dropdown', + 'relay:animate-content-fade-in', className )} > @@ -43,13 +43,13 @@ const DropdownMenuItem = forwardRef< {...props} ref={forwardedRef} className={cn( - 'relay-flex relay-items-center relay-text-[16px]', - 'relay-text-[color:var(--relay-colors-text-default)]', - 'relay-bg-[var(--relay-colors-modal-background)]', - 'relay-p-2 relay-outline-none relay-cursor-pointer', - 'relay-transition-[background-color] relay-duration-150 relay-ease-linear', - 'hover:relay-bg-[var(--relay-colors-gray3)]', - 'focus:relay-bg-[var(--relay-colors-gray3)]', + 'relay:flex relay:items-center relay:text-[16px]', + 'relay:text-[color:var(--relay-colors-text-default)]', + 'relay:bg-[var(--relay-colors-modal-background)]', + 'relay:p-2 relay:outline-none relay:cursor-pointer', + 'relay:transition-[background-color] relay:duration-150 relay:ease-linear', + 'relay:hover:bg-[var(--relay-colors-gray3)]', + 'relay:focus:bg-[var(--relay-colors-gray3)]', className )} > diff --git a/packages/ui/src/components/primitives/Flex.tsx b/packages/ui/src/components/primitives/Flex.tsx index 1a08b52a7..b4ff46832 100644 --- a/packages/ui/src/components/primitives/Flex.tsx +++ b/packages/ui/src/components/primitives/Flex.tsx @@ -7,32 +7,32 @@ import type { } from 'react' import { cn } from '../../utils/cn.js' -export const flexVariants = cva('relay-flex', { +export const flexVariants = cva('relay:flex', { variants: { align: { - start: 'relay-items-start', - center: 'relay-items-center', - end: 'relay-items-end', - stretch: 'relay-items-stretch', - baseline: 'relay-items-baseline', - normal: 'relay-items-[normal]' + start: 'relay:items-start', + center: 'relay:items-center', + end: 'relay:items-end', + stretch: 'relay:items-stretch', + baseline: 'relay:items-baseline', + normal: 'relay:items-[normal]' }, justify: { - start: 'relay-justify-start', - center: 'relay-justify-center', - end: 'relay-justify-end', - between: 'relay-justify-between' + start: 'relay:justify-start', + center: 'relay:justify-center', + end: 'relay:justify-end', + between: 'relay:justify-between' }, direction: { - row: 'relay-flex-row', - column: 'relay-flex-col', - rowReverse: 'relay-flex-row-reverse', - columnReverse: 'relay-flex-col-reverse' + row: 'relay:flex-row', + column: 'relay:flex-col', + rowReverse: 'relay:flex-row-reverse', + columnReverse: 'relay:flex-col-reverse' }, wrap: { - noWrap: 'relay-flex-nowrap', - wrap: 'relay-flex-wrap', - wrapReverse: 'relay-flex-wrap-reverse' + noWrap: 'relay:flex-nowrap', + wrap: 'relay:flex-wrap', + wrapReverse: 'relay:flex-wrap-reverse' } } }) diff --git a/packages/ui/src/components/primitives/Input.tsx b/packages/ui/src/components/primitives/Input.tsx index 6a886c7d0..4e1400279 100644 --- a/packages/ui/src/components/primitives/Input.tsx +++ b/packages/ui/src/components/primitives/Input.tsx @@ -5,22 +5,22 @@ import { cn } from '../../utils/cn.js' const inputVariants = cva( [ - 'relay-w-full relay-px-4 relay-py-3 relay-rounded-input relay-font-body relay-text-[16px]', - 'relay-text-[color:var(--relay-colors-input-color)]', - 'relay-bg-[var(--relay-colors-input-background)]', - 'placeholder:relay-text-[color:var(--relay-colors-gray9)] relay-placeholder-ellipsis', - 'relay-transition-shadow relay-duration-200', - 'focus:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)] focus:relay-outline-none', - 'disabled:relay-cursor-not-allowed', - '[&::-webkit-outer-spin-button]:relay-appearance-none [&::-webkit-inner-spin-button]:relay-appearance-none' + 'relay:w-full relay:px-4 relay:py-3 relay:rounded-input relay:font-body relay:text-[16px]', + 'relay:text-[color:var(--relay-colors-input-color)]', + 'relay:bg-[var(--relay-colors-input-background)]', + 'relay:placeholder:text-[color:var(--relay-colors-gray9)] relay:placeholder-ellipsis', + 'relay:transition-shadow relay:duration-200', + 'relay:focus:shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)] relay:focus:outline-none', + 'relay:disabled:cursor-not-allowed', + 'relay:[&::-webkit-outer-spin-button]:appearance-none relay:[&::-webkit-inner-spin-button]:appearance-none' ].join(' '), { variants: { size: { - large: 'relay-text-[32px] relay-leading-[42px]' + large: 'relay:text-[32px] relay:leading-[42px]' }, ellipsify: { - true: 'relay-text-ellipsis relay-overflow-hidden relay-whitespace-nowrap' + true: 'relay:text-ellipsis relay:overflow-hidden relay:whitespace-nowrap' } } } @@ -55,15 +55,15 @@ const Input = forwardRef< const { size, ellipsify, style, ...inputProps } = props return ( -
+
{icon && ( -
+
diff --git a/packages/ui/src/components/primitives/Pill.tsx b/packages/ui/src/components/primitives/Pill.tsx index 97e0b680d..be39e6a2f 100644 --- a/packages/ui/src/components/primitives/Pill.tsx +++ b/packages/ui/src/components/primitives/Pill.tsx @@ -3,25 +3,25 @@ import type { FC, HTMLAttributes } from 'react' import { cn } from '../../utils/cn.js' export const pillVariants = cva( - 'relay-flex relay-px-3 relay-py-1 relay-gap-1 relay-transition-colors relay-duration-150', + 'relay:flex relay:px-3 relay:py-1 relay:gap-1 relay:transition-colors relay:duration-150', { variants: { color: { - default: 'relay-bg-[var(--relay-colors-subtle-background-color)]', - red: 'relay-bg-[var(--relay-colors-red3)] relay-text-[color:var(--relay-colors-red11)]', - gray: 'relay-bg-[var(--relay-colors-gray2)] relay-text-[color:var(--relay-colors-gray8)]', - green: 'relay-bg-[var(--relay-colors-green3)] relay-text-[color:var(--relay-colors-green12)]', - amber: 'relay-bg-[var(--relay-colors-amber2)] relay-text-[color:var(--relay-colors-amber9)]', - transparent: 'relay-bg-transparent relay-text-[color:var(--relay-colors-gray12)]', - primary: 'relay-bg-[var(--relay-colors-primary3)] relay-text-[color:var(--relay-colors-primary12)]' + default: 'relay:bg-[var(--relay-colors-subtle-background-color)]', + red: 'relay:bg-[var(--relay-colors-red3)] relay:text-[color:var(--relay-colors-red11)]', + gray: 'relay:bg-[var(--relay-colors-gray2)] relay:text-[color:var(--relay-colors-gray8)]', + green: 'relay:bg-[var(--relay-colors-green3)] relay:text-[color:var(--relay-colors-green12)]', + amber: 'relay:bg-[var(--relay-colors-amber2)] relay:text-[color:var(--relay-colors-amber9)]', + transparent: 'relay:bg-transparent relay:text-[color:var(--relay-colors-gray12)]', + primary: 'relay:bg-[var(--relay-colors-primary3)] relay:text-[color:var(--relay-colors-primary12)]' }, radius: { - pill: 'relay-rounded-[25px]', - rounded: 'relay-rounded-[12px]', - squared: 'relay-rounded-[8px]' + pill: 'relay:rounded-[25px]', + rounded: 'relay:rounded-[12px]', + squared: 'relay:rounded-[8px]' }, bordered: { - true: 'relay-border relay-border-solid relay-border-[var(--relay-colors-gray-6)]' + true: 'relay:border relay:border-solid relay:border-[var(--relay-colors-gray-6)]' } }, defaultVariants: { diff --git a/packages/ui/src/components/primitives/Skeleton.tsx b/packages/ui/src/components/primitives/Skeleton.tsx index 7c24d924e..677e1af38 100644 --- a/packages/ui/src/components/primitives/Skeleton.tsx +++ b/packages/ui/src/components/primitives/Skeleton.tsx @@ -9,7 +9,7 @@ const Skeleton: FC = ({ className }) => { return (
diff --git a/packages/ui/src/components/primitives/SlippageButton.tsx b/packages/ui/src/components/primitives/SlippageButton.tsx index 8e8ef73a8..caccfc644 100644 --- a/packages/ui/src/components/primitives/SlippageButton.tsx +++ b/packages/ui/src/components/primitives/SlippageButton.tsx @@ -31,7 +31,7 @@ export const SlippageButton: FC = ({ aria-label="Slippage Settings" size="none" color="ghost" - className="relay-flex relay-rounded-[8px] relay-items-center relay-gap-[4px] relay-justify-center relay-p-1 hover:relay-bg-[var(--relay-colors-gray2)] relay-bg-[var(--relay-colors-gray3)] relay-px-[6px] relay-py-[4px]" + className="relay:flex relay:rounded-[8px] relay:items-center relay:gap-[4px] relay:justify-center relay:p-1 relay:hover:bg-[var(--relay-colors-gray2)] relay:bg-[var(--relay-colors-gray3)] relay:px-[6px] relay:py-[4px]" onClick={() => { onOpenSlippageConfig?.() }} diff --git a/packages/ui/src/components/primitives/Switch.tsx b/packages/ui/src/components/primitives/Switch.tsx index 913857d0e..751f3f053 100644 --- a/packages/ui/src/components/primitives/Switch.tsx +++ b/packages/ui/src/components/primitives/Switch.tsx @@ -15,11 +15,11 @@ export const StyledSwitch = forwardRef< ref={forwardedRef} {...props} className={cn( - 'relay-cursor-pointer relay-w-[38px] relay-h-[20px]', - 'relay-bg-[var(--relay-colors-gray7)] relay-rounded-full', - 'relay-relative relay-flex relay-items-center relay-px-[10px]', - 'relay-transition-all relay-duration-200', - "data-[state='checked']:relay-bg-[var(--relay-colors-primary-button-background)]", + 'relay:cursor-pointer relay:w-[38px] relay:h-[20px]', + 'relay:bg-[var(--relay-colors-gray7)] relay:rounded-full', + 'relay:relative relay:flex relay:items-center relay:px-[10px]', + 'relay:transition-all relay:duration-200', + "relay:data-[state='checked']:bg-[var(--relay-colors-primary-button-background)]", className )} > @@ -37,13 +37,13 @@ export const StyledThumb = forwardRef< ref={forwardedRef} {...props} className={cn( - 'relay-block relay-w-[17.5px] relay-h-[17.5px]', - 'relay-bg-[var(--relay-colors-gray1)] relay-rounded-full', - 'relay-z-[1] relay-border relay-border-solid relay-border-[var(--relay-colors-gray-8)] relay-shadow-sm', - 'relay-transition-transform relay-duration-100', - 'relay-translate-x-0 relay-will-change-transform', - 'relay-absolute relay-left-[2px]', - "data-[state='checked']:relay-translate-x-[17px]", + 'relay:block relay:w-[17.5px] relay:h-[17.5px]', + 'relay:bg-[var(--relay-colors-gray1)] relay:rounded-full', + 'relay:z-[1] relay:border relay:border-solid relay:border-[var(--relay-colors-gray-8)] relay:shadow-sm', + 'relay:transition-transform relay:duration-100', + 'relay:translate-x-0 relay:will-change-transform', + 'relay:absolute relay:left-[2px]', + "relay:data-[state='checked']:translate-x-[17px]", className )} /> diff --git a/packages/ui/src/components/primitives/Tabs.tsx b/packages/ui/src/components/primitives/Tabs.tsx index 4bf74a004..c0a9baf58 100644 --- a/packages/ui/src/components/primitives/Tabs.tsx +++ b/packages/ui/src/components/primitives/Tabs.tsx @@ -17,8 +17,8 @@ const TabsList = forwardRef< {...props} ref={forwardedRef} className={cn( - 'relay-flex relay-items-center relay-rounded-[8px] relay-p-1', - 'relay-bg-[var(--relay-colors-gray2)] relay-border-none', + 'relay:flex relay:items-center relay:rounded-[8px] relay:p-1', + 'relay:bg-[var(--relay-colors-gray2)] relay:border-none', className )} > @@ -38,12 +38,12 @@ const TabsTrigger = forwardRef< {...props} ref={forwardedRef} className={cn( - 'relay-w-full relay-font-medium relay-text-[14px] relay-cursor-pointer', - 'relay-py-[2px] relay-text-[color:var(--relay-colors-gray12)]', - 'relay-rounded-[8px] relay-bg-transparent', - 'relay-border relay-border-solid relay-border-transparent', - 'data-[state=active]:relay-bg-[var(--relay-colors-subtle-background-color)]', - 'data-[state=active]:relay-border-[var(--relay-colors-gray-5)]', + 'relay:w-full relay:font-medium relay:text-[14px] relay:cursor-pointer', + 'relay:py-[2px] relay:text-[color:var(--relay-colors-gray12)]', + 'relay:rounded-[8px] relay:bg-transparent', + 'relay:border relay:border-solid relay:border-transparent', + 'relay:data-[state=active]:bg-[var(--relay-colors-subtle-background-color)]', + 'relay:data-[state=active]:border-[var(--relay-colors-gray-5)]', className )} > diff --git a/packages/ui/src/components/primitives/Text.tsx b/packages/ui/src/components/primitives/Text.tsx index 71ad8f3fd..ecec1b694 100644 --- a/packages/ui/src/components/primitives/Text.tsx +++ b/packages/ui/src/components/primitives/Text.tsx @@ -3,46 +3,46 @@ import type { FC, PropsWithChildren } from 'react' import { cn } from '../../utils/cn.js' const textVariants = cva( - 'relay-font-body', + 'relay:font-body', { variants: { style: { - h1: 'relay-font-[800] relay-text-[64px] relay-font-heading', - h2: 'relay-font-bold relay-text-[48px]', - h3: 'relay-font-bold relay-text-[32px]', - h4: 'relay-font-bold relay-text-[24px]', - h5: 'relay-font-bold relay-text-[20px]', - h6: 'relay-font-bold relay-text-[16px]', - subtitle1: 'relay-font-medium relay-text-[16px]', - subtitle2: 'relay-font-medium relay-text-[14px]', - subtitle3: 'relay-font-medium relay-text-[12px]', - body1: 'relay-font-normal relay-text-[16px]', - body2: 'relay-font-normal relay-text-[14px]', - body3: 'relay-font-normal relay-text-[12px]', + h1: 'relay:font-[800] relay:text-[64px] relay:font-heading', + h2: 'relay:font-bold relay:text-[48px]', + h3: 'relay:font-bold relay:text-[32px]', + h4: 'relay:font-bold relay:text-[24px]', + h5: 'relay:font-bold relay:text-[20px]', + h6: 'relay:font-bold relay:text-[16px]', + subtitle1: 'relay:font-medium relay:text-[16px]', + subtitle2: 'relay:font-medium relay:text-[14px]', + subtitle3: 'relay:font-medium relay:text-[12px]', + body1: 'relay:font-normal relay:text-[16px]', + body2: 'relay:font-normal relay:text-[14px]', + body3: 'relay:font-normal relay:text-[12px]', tiny: - 'relay-font-medium relay-text-[10px] relay-text-[color:var(--relay-colors-gray11)]' + 'relay:font-medium relay:text-[10px] relay:text-[color:var(--relay-colors-gray11)]' }, color: { - default: 'relay-text-[color:var(--relay-colors-text-default)]', - subtle: 'relay-text-[color:var(--relay-colors-text-subtle)]', + default: 'relay:text-[color:var(--relay-colors-text-default)]', + subtle: 'relay:text-[color:var(--relay-colors-text-subtle)]', subtleSecondary: - 'relay-text-[color:var(--relay-colors-text-subtle-secondary)]', - error: 'relay-text-[color:var(--relay-colors-text-error)]', - red: 'relay-text-[color:var(--relay-colors-red11)]', - blue: 'relay-text-[color:var(--relay-colors-blue12)]', - success: 'relay-text-[color:var(--relay-colors-text-success)]', - warning: 'relay-text-[color:var(--relay-colors-amber12)]', - warningSecondary: 'relay-text-[color:var(--relay-colors-amber11)]', - primary: 'relay-text-[color:var(--relay-colors-primary11)]', - primary12: 'relay-text-[color:var(--relay-colors-primary12)]', - green: 'relay-text-[color:var(--relay-colors-green11)]', - slate: 'relay-text-[color:var(--relay-colors-slate10)]' + 'relay:text-[color:var(--relay-colors-text-subtle-secondary)]', + error: 'relay:text-[color:var(--relay-colors-text-error)]', + red: 'relay:text-[color:var(--relay-colors-red11)]', + blue: 'relay:text-[color:var(--relay-colors-blue12)]', + success: 'relay:text-[color:var(--relay-colors-text-success)]', + warning: 'relay:text-[color:var(--relay-colors-amber12)]', + warningSecondary: 'relay:text-[color:var(--relay-colors-amber11)]', + primary: 'relay:text-[color:var(--relay-colors-primary11)]', + primary12: 'relay:text-[color:var(--relay-colors-primary12)]', + green: 'relay:text-[color:var(--relay-colors-green11)]', + slate: 'relay:text-[color:var(--relay-colors-slate10)]' }, italic: { - true: 'relay-italic' + true: 'relay:italic' }, ellipsify: { - true: 'relay-text-ellipsis relay-overflow-hidden relay-whitespace-nowrap' + true: 'relay:text-ellipsis relay:overflow-hidden relay:whitespace-nowrap' } }, defaultVariants: { diff --git a/packages/ui/src/components/primitives/Tooltip.tsx b/packages/ui/src/components/primitives/Tooltip.tsx index 3be2a3fdc..89d2413fa 100644 --- a/packages/ui/src/components/primitives/Tooltip.tsx +++ b/packages/ui/src/components/primitives/Tooltip.tsx @@ -30,9 +30,9 @@ const Tooltip = ({ {...props} >
-
+
{content}
@@ -56,13 +56,13 @@ const Tooltip = ({ side="bottom" align="center" style={{ zIndex: 10000003 }} - className="relay-animate-content-fade-in" + className="relay:animate-content-fade-in" {...props} >
-
+
{content}
diff --git a/packages/ui/src/components/widgets/FeeBreakdown.tsx b/packages/ui/src/components/widgets/FeeBreakdown.tsx index 9f8991551..c33dad997 100644 --- a/packages/ui/src/components/widgets/FeeBreakdown.tsx +++ b/packages/ui/src/components/widgets/FeeBreakdown.tsx @@ -103,7 +103,7 @@ const FeeBreakdown: FC = ({ value: ( = ({ { title: 'Network cost', value: ( - + {originGasFeeFormatted} @@ -140,13 +140,13 @@ const FeeBreakdown: FC = ({
{feeBreakdown?.totalFees?.priceImpactPercentage} @@ -155,7 +155,7 @@ const FeeBreakdown: FC = ({ icon={faInfoCircle} width={14} height={14} - className="relay-inline-block relay-ml-[4px]" + className="relay:inline-block relay:ml-[4px]" />
@@ -170,10 +170,10 @@ const FeeBreakdown: FC = ({ return ( -
- +
+
) @@ -184,16 +184,16 @@ const FeeBreakdown: FC = ({ return ( - + Max Slippage - + {isAutoSlippage ? ( @@ -157,7 +157,7 @@ export const OnrampProcessingStepUI: FC = ({ It might take a few minutes for the MoonPay transaction to @@ -168,11 +168,11 @@ export const OnrampProcessingStepUI: FC = ({ ) : null}
= ({ tokenlogoURI={toToken?.logoURI} />
- + = ({ View Tx: {truncateAddress(fillTxHash)} @@ -210,7 +210,7 @@ export const OnrampProcessingStepUI: FC = ({ {processingStep === OnrampProcessingStep.Relaying ? ( ) : null} @@ -221,7 +221,7 @@ export const OnrampProcessingStepUI: FC = ({ transaction page @@ -235,7 +235,7 @@ export const OnrampProcessingStepUI: FC = ({ Learn more diff --git a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx index b9959d636..40880b6c5 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/modals/steps/OnrampSuccessStep.tsx @@ -38,53 +38,53 @@ export const OnrampSuccessStep: FC = ({ return ( - + Transaction Details -
+
- - + +
- + Successfully purchased {isLoadingTransaction ? ( - + ) : ( {toAmountFormatted} {toToken.symbol} )} - + View MoonPay transaction{' '} - + {fillTxUrl ? ( @@ -93,11 +93,11 @@ export const OnrampSuccessStep: FC = ({ ) : null} - + {fillTxHash ? ( { e.stopPropagation() @@ -106,7 +106,7 @@ export const OnrampSuccessStep: FC = ({ @@ -117,7 +117,7 @@ export const OnrampSuccessStep: FC = ({ onClick={() => { onOpenChange(false) }} - className="relay-justify-center relay-w-full" + className="relay:justify-center relay:w-full" > Done diff --git a/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx b/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx index 6eee61137..b700b6777 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/widget/FiatCurrencyModal.tsx @@ -89,17 +89,17 @@ const FiatCurrencyModal: FC = ({ @@ -113,11 +113,11 @@ const FiatCurrencyModal: FC = ({ } setOpen(open) }} - className="relay-overflow-hidden relay-p-4 relay-max-w-[100vw] !relay-max-h-[450px] relay-h-[450px] sm:!relay-max-w-[400px]" + className="relay:overflow-hidden relay:p-4 relay:max-w-[100vw] relay:!max-h-[450px] relay:h-[450px] sm:!relay-max-w-[400px]" > Select a currency @@ -133,16 +133,16 @@ const FiatCurrencyModal: FC = ({ setOpen(false) } }} - className="relay-flex relay-flex-col relay-w-full relay-gap-1 relay-overflow-y-auto relay-h-[calc(100%-24px)] relay-scroll-pt-[40px]" + className="relay:flex relay:flex-col relay:w-full relay:gap-1 relay:overflow-y-auto relay:h-[calc(100%-24px)] relay:scroll-pt-[40px]" > + = ({ /> } - containerClassName="relay-w-full" + containerClassName="relay:w-full" style={{ scrollSnapAlign: 'start' }} - className="relay-w-full relay-h-[40px]" + className="relay:w-full relay:h-[40px]" value={currencySearchInput} onChange={(e) => { setCurrencySearchInput((e.target as HTMLInputElement).value) @@ -172,29 +172,29 @@ const FiatCurrencyModal: FC = ({ color="ghost" size="none" className={cn( - 'relay-p-2 relay-flex relay-items-center relay-gap-2 relay-relative', - 'relay-transition-[backdrop-filter] relay-duration-[250ms] relay-ease-linear', - 'focus-visible:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', - '[&[data-state=on]]:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', - 'active:relay-shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', - !active && 'hover:relay-bg-[rgba(128,128,128,0.1)]', - i + 1 < (fiatCurrencies?.length ?? 0) ? 'relay-mb-1' : '' + 'relay:p-2 relay:flex relay:items-center relay:gap-2 relay:relative', + 'relay:transition-[backdrop-filter] relay:duration-[250ms] relay:ease-linear', + 'relay:focus-visible:shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', + 'relay:[&[data-state=on]]:shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', + 'relay:active:shadow-[inset_0_0_0_2px_var(--relay-colors-focus-color)]', + !active && 'relay:hover:bg-[rgba(128,128,128,0.1)]', + i + 1 < (fiatCurrencies?.length ?? 0) ? 'relay:mb-1' : '' )} style={{ scrollSnapAlign: 'start' }} > {active ? (
) : null} - + {`${currency.name} {currency.name} @@ -209,7 +209,7 @@ const FiatCurrencyModal: FC = ({ })} {/* Empty State */} {filteredCurrencies.length === 0 ? ( - + No results found. ) : null} diff --git a/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx b/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx index efc61981d..3ea621d70 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/widget/index.tsx @@ -199,20 +199,20 @@ const OnrampWidget: FC = ({ return (
Enter an amount @@ -318,7 +318,7 @@ const OnrampWidget: FC = ({ } input.style.caretColor = 'initial' }} - className="relay-text-center relay-whitespace-pre" + className="relay:text-center relay:whitespace-pre" inputStyle={{ fontWeight: '700', fontSize: 48, @@ -330,10 +330,10 @@ const OnrampWidget: FC = ({ : undefined, whiteSpace: 'pre' }} - containerClassName="relay-mb-2 relay-w-full" + containerClassName="relay:mb-2 relay:w-full" /> {notEnoughFiat ? ( - + Minimum amount is{' '} {displayCurrency && minAmountCurrency ? `${minAmountCurrency} ${token.symbol}` @@ -341,7 +341,7 @@ const OnrampWidget: FC = ({ ) : undefined}