From e10995d748ab001ae16e85b5394ff2623f789a52 Mon Sep 17 00:00:00 2001 From: Todd Kao Date: Thu, 18 Jul 2024 17:20:57 -0400 Subject: [PATCH] [major] Add themeing (#21) --- .changeset/curly-crabs-switch.md | 5 + examples/nextjs/pages/index.tsx | 138 ++++-- examples/nextjs/pages/provider.tsx | 3 - examples/nextjs/pages/vue.tsx | 6 +- examples/nextjs/pages/web-component.tsx | 3 - packages/widget/README.md | 10 +- packages/widget/package.json | 8 +- .../src/hooks/use-fix-radix-ui-wheel-event.ts | 24 - packages/widget/src/hooks/use-swap-widget.ts | 2 +- packages/widget/src/provider/index.tsx | 9 +- packages/widget/src/store/swap-widget.ts | 15 - packages/widget/src/styles/cssReset.css | 6 - packages/widget/src/ui/AssetInput.tsx | 23 +- .../src/ui/AssetSelect/AssetSelectContent.tsx | 42 +- packages/widget/src/ui/AssetSelect/index.tsx | 10 +- .../widget/src/ui/Button/HistoryButton.tsx | 7 +- .../widget/src/ui/Button/SettingsButton.tsx | 8 +- packages/widget/src/ui/Button/ShareButton.tsx | 7 +- .../src/ui/ChainSelect/ChainSelectContent.tsx | 27 +- .../src/ui/ChainSelect/ChainSelectTrigger.tsx | 13 +- .../widget/src/ui/ConnectedWalletButton.tsx | 7 +- packages/widget/src/ui/CraftedBySkip.tsx | 7 +- .../widget/src/ui/Dialog/DialogContent.tsx | 8 +- .../src/ui/HistoryDialog/DescriptionList.tsx | 5 +- .../src/ui/HistoryDialog/HistoryList.tsx | 11 +- .../widget/src/ui/HistoryDialog/index.tsx | 44 +- packages/widget/src/ui/Icon/HistoryIcon.tsx | 13 +- packages/widget/src/ui/Icon/ShareIcon.tsx | 11 - packages/widget/src/ui/Icon/SkipLogo.tsx | 9 +- packages/widget/src/ui/JsonDialog.tsx | 7 +- .../widget/src/ui/PreviewRoute/ChainStep.tsx | 91 ++-- .../src/ui/PreviewRoute/SetAddressDialog.tsx | 78 ++-- packages/widget/src/ui/PreviewRoute/index.tsx | 69 +-- packages/widget/src/ui/PriceImpactWarning.tsx | 12 +- .../src/ui/SettingsDialog/GasSetting.tsx | 4 +- .../src/ui/SettingsDialog/SlippageSetting.tsx | 12 +- .../widget/src/ui/SettingsDialog/index.tsx | 30 +- packages/widget/src/ui/SimpleTooltip.tsx | 38 +- .../widget/src/ui/StyledComponents/Buttons.ts | 17 + .../widget/src/ui/StyledComponents/Input.ts | 9 + .../widget/src/ui/StyledComponents/Theme.ts | 15 + packages/widget/src/ui/SwapDetails.tsx | 17 +- packages/widget/src/ui/TransactionDialog.tsx | 10 +- .../widget/src/ui/WalletModal/WalletModal.tsx | 22 +- packages/widget/src/ui/Widget.tsx | 424 +++++++++--------- .../widget/src/ui/WithStyledShadowDom.tsx | 70 +++ packages/widget/src/ui/index.tsx | 81 ++-- packages/widget/src/ui/theme.ts | 23 + yarn.lock | 123 +++-- 49 files changed, 938 insertions(+), 695 deletions(-) create mode 100644 .changeset/curly-crabs-switch.md delete mode 100644 packages/widget/src/hooks/use-fix-radix-ui-wheel-event.ts create mode 100644 packages/widget/src/ui/StyledComponents/Buttons.ts create mode 100644 packages/widget/src/ui/StyledComponents/Input.ts create mode 100644 packages/widget/src/ui/StyledComponents/Theme.ts create mode 100644 packages/widget/src/ui/WithStyledShadowDom.tsx create mode 100644 packages/widget/src/ui/theme.ts diff --git a/.changeset/curly-crabs-switch.md b/.changeset/curly-crabs-switch.md new file mode 100644 index 000000000..f808c1c03 --- /dev/null +++ b/.changeset/curly-crabs-switch.md @@ -0,0 +1,5 @@ +--- +'@skip-go/widget': major +--- + +Add theme prop to enable custom styling diff --git a/examples/nextjs/pages/index.tsx b/examples/nextjs/pages/index.tsx index f163c17ee..210dbd81e 100644 --- a/examples/nextjs/pages/index.tsx +++ b/examples/nextjs/pages/index.tsx @@ -1,46 +1,124 @@ import { NextPage } from 'next'; -import React from 'react'; +import React, { useState } from 'react'; import { SwapWidget } from '@skip-go/widget'; +import crypto from 'crypto'; +import { PartialTheme } from '@skip-go/widget/build/ui/theme'; + +function hashString(inputString: string) { + const hash = crypto.createHash('sha256'); + hash.update(inputString); + return hash.digest('hex'); +} + +function isJsonString(str: string) { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; +} + +const defaultProps = { + defaultRoute: { + srcChainID: 'osmosis-1', + srcAssetDenom: + 'ibc/1480b8fd20ad5fcae81ea87584d269547dd4d436843c1d20f15e00eb64743ef4', + }, +}; + +const darkTheme = { + backgroundColor: '#191A1C', + textColor: '#E6EAE9', + borderColor: '#363B3F', + brandColor: '#FF4FFF', + highlightColor: '#1F2022', +}; + +const defaultHash = hashString(JSON.stringify(defaultProps)); const Home: NextPage = () => { + const [props, setProps] = useState(defaultProps); + const [propsHash, setPropsHash] = useState(defaultHash); + + const handleOnChange = (event: any) => { + if (isJsonString(event.target.innerText)) { + const newHash = hashString(event.target.innerText); + if (propsHash !== newHash) { + console.log('update props'); + setProps({ ...JSON.parse(event.target.innerText) }); + setPropsHash(newHash); + } + } + }; + + const handleUpdateTheme = (theme: PartialTheme) => { + const newProps = { ...props, theme: theme } as any; + const newHash = hashString(JSON.stringify(newProps)); + setProps(newProps); + setPropsHash(newHash); + }; + return ( -
+
- +
+
+
+ + + +
+ +
+        >
+          {JSON.stringify(props, null, 2)}
+        
-
); }; diff --git a/examples/nextjs/pages/provider.tsx b/examples/nextjs/pages/provider.tsx index ee5662fad..1acd29b23 100644 --- a/examples/nextjs/pages/provider.tsx +++ b/examples/nextjs/pages/provider.tsx @@ -19,9 +19,6 @@ const Home: NextPage = () => { > { const { createApp } = require('vue/dist/vue.esm-bundler.js'); const app = createApp({ setup() { - const colors = JSON.stringify({ - primary: '#FF4FFF', - }); const defaultRoute = JSON.stringify({ srcChainID: 'osmosis-1', srcAssetDenom: @@ -19,13 +16,12 @@ const VuePage: NextPage = () => { }); return { - colors, defaultRoute, }; }, template: `
- +
`, }); diff --git a/examples/nextjs/pages/web-component.tsx b/examples/nextjs/pages/web-component.tsx index a80fa9283..ce05dd85b 100644 --- a/examples/nextjs/pages/web-component.tsx +++ b/examples/nextjs/pages/web-component.tsx @@ -13,9 +13,6 @@ const WebComponent: NextPage = () => { }} > Promise; }; apiURL?: string; // Custom API URL to override Skip API endpoint. Defaults to Skip proxied endpoints. Please reach out to us first if you want to be whitelisted. + theme?: { + backgroundColor: string; // background color + textColor: string; // text color + borderColor: string; // border color + brandColor: string; // color used for confirmation buttons + highlightColor: string; // color used when hovering over buttons, and in select chain/asset dropdown + }; } ```` diff --git a/packages/widget/package.json b/packages/widget/package.json index c2df8118a..802ed0a15 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -37,7 +37,9 @@ }, "peerDependencies": { "react": "17.x || 18.x", - "react-dom": "17.x || 18.x" + "react-dom": "17.x || 18.x", + "styled-components": "^6.0.0", + "stylis": "^4.0.0" }, "devDependencies": { "@rollup/plugin-url": "^8.0.2", @@ -45,11 +47,12 @@ "@types/qrcode": "^1.4.2", "@types/react": "^18.0.6", "@types/react-dom": "^18.0.2", - "@types/styled-components": "^5.1.25", "autoprefixer": "^10.4.19", "postcss": "^8.4.38", "postcss-prefixwrap": "^1.49.0", "rollup-plugin-copy": "^3.5.0", + "styled-components": "^6.0.0", + "stylis": "^4.0.0", "tailwindcss": "^3.4.3", "tailwindcss-animate": "^1.0.7", "typed-query-selector": "^2.11.2", @@ -101,7 +104,6 @@ "match-sorter": "^6.3.4", "react-hot-toast": "^2.4.1", "react-icons": "^5.2.1", - "react-shadow-scope": "^1.0.5", "tailwind-merge": "^2.3.0", "viem": "2.x", "wagmi": "2.x", diff --git a/packages/widget/src/hooks/use-fix-radix-ui-wheel-event.ts b/packages/widget/src/hooks/use-fix-radix-ui-wheel-event.ts deleted file mode 100644 index 7c886c44a..000000000 --- a/packages/widget/src/hooks/use-fix-radix-ui-wheel-event.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect } from 'react'; - -export const useFixRadixUiWheelEvent = () => { - useEffect(() => { - const element = document.querySelector('react-shadow-scope')?.shadowRoot; - if (!element) return; - - const handleWheel = (e: Event) => { - e.stopPropagation(); - }; - - const handleTouchMove = (e: Event) => { - e.stopPropagation(); - }; - - element.addEventListener('wheel', handleWheel, true); - element.addEventListener('touchmove', handleTouchMove, true); - - return () => { - element.removeEventListener('wheel', handleWheel, true); - element.removeEventListener('touchmove', handleTouchMove, true); - }; - }, []); -}; diff --git a/packages/widget/src/hooks/use-swap-widget.ts b/packages/widget/src/hooks/use-swap-widget.ts index b8f09f91d..382b31593 100644 --- a/packages/widget/src/hooks/use-swap-widget.ts +++ b/packages/widget/src/hooks/use-swap-widget.ts @@ -28,7 +28,7 @@ import { Chain, useChains } from './use-chains'; import { useBalancesByChain } from './use-balances-by-chain'; import { useRoute } from './use-route'; import { getChainFeeAssets, getChainGasPrice } from '../utils/chain'; -import { useSkipClient, useSkipConfig } from './use-skip-client'; +import { useSkipClient } from './use-skip-client'; import { getAmountWei, parseAmountWei } from '../utils/number'; import { gracefullyConnect } from '../utils/wallet'; import { useSwapWidgetUIStore } from '../store/swap-widget'; diff --git a/packages/widget/src/provider/index.tsx b/packages/widget/src/provider/index.tsx index 3e78c7c8e..18dd7d9a0 100644 --- a/packages/widget/src/provider/index.tsx +++ b/packages/widget/src/provider/index.tsx @@ -13,6 +13,7 @@ import { endpointOptions as defaultEndpointOptions, apiURL as defaultApiURL, } from '../constants/defaults'; +import { useTheme } from 'styled-components'; interface WalletProviderProps { children: React.ReactNode; @@ -24,7 +25,6 @@ export interface WidgetConfig { } export interface SwapWidgetProviderProps extends SkipAPIProviderProps { children: React.ReactNode; - toasterProps?: ToasterProps; } export interface SkipAPIProviderProps { children: React.ReactNode; @@ -58,18 +58,11 @@ export const SkipAPIProvider: React.FC = ({ export const SwapWidgetProvider: React.FC = ({ children, - toasterProps, ...skipApiProviderProps }) => { return ( {children} - ); }; diff --git a/packages/widget/src/store/swap-widget.ts b/packages/widget/src/store/swap-widget.ts index 237463dad..33d5d4d19 100644 --- a/packages/widget/src/store/swap-widget.ts +++ b/packages/widget/src/store/swap-widget.ts @@ -10,9 +10,6 @@ import { } from 'zustand/middleware'; interface SwapWidgetStore { - colors: { - primary?: string; - }; onlyTestnet?: boolean; defaultRoute?: DefaultRouteConfig; routeConfig?: RouteConfig; @@ -45,9 +42,6 @@ interface SwapWidgetStore { }; } export const swapWidgetDefaultValues: SwapWidgetStore = { - colors: { - primary: '#FF486E', - }, onlyTestnet: false, defaultRoute: undefined, routeConfig: { @@ -78,12 +72,6 @@ export const useSwapWidgetUIStore = create( ); export interface ConfigureSwapWidgetArgs { - colors?: { - /** - * @default '#FF486E' - */ - primary?: string; - }; settings?: { /** * gas amount for validation @@ -128,9 +116,6 @@ export interface ConfigureSwapWidgetArgs { export const configureSwapWidget = (args: ConfigureSwapWidgetArgs) => { useSwapWidgetUIStore.setState((prev) => ({ - colors: { - primary: args.colors?.primary || prev.colors.primary, - }, onlyTestnet: args.onlyTestnet || prev.onlyTestnet, defaultRoute: args.defaultRoute || prev.defaultRoute, routeConfig: { diff --git a/packages/widget/src/styles/cssReset.css b/packages/widget/src/styles/cssReset.css index df7efbd96..f294a648f 100644 --- a/packages/widget/src/styles/cssReset.css +++ b/packages/widget/src/styles/cssReset.css @@ -19,9 +19,3 @@ background-color: unset; word-spacing: unset; } - -div, -p, -span { - color: black; -} diff --git a/packages/widget/src/ui/AssetInput.tsx b/packages/widget/src/ui/AssetInput.tsx index 68e2e116b..79185a6b7 100644 --- a/packages/widget/src/ui/AssetInput.tsx +++ b/packages/widget/src/ui/AssetInput.tsx @@ -20,6 +20,11 @@ import { } from '../utils/number'; import { formatPercent, formatUSD } from '../utils/intl'; import { useSwapWidgetUIStore } from '../store/swap-widget'; +import { + StyledBorderDiv, + StyledBrandDiv, + StyledThemedDiv, +} from './StyledComponents/Theme'; interface Props { amount: string; @@ -97,9 +102,9 @@ function AssetInput({ }, [selectedAssetBalance]); return ( -
)} - )} - +
)}
@@ -266,7 +269,7 @@ function AssetInput({ {isError} )} - + ); } diff --git a/packages/widget/src/ui/AssetSelect/AssetSelectContent.tsx b/packages/widget/src/ui/AssetSelect/AssetSelectContent.tsx index 94e05c091..2a0596d76 100644 --- a/packages/widget/src/ui/AssetSelect/AssetSelectContent.tsx +++ b/packages/widget/src/ui/AssetSelect/AssetSelectContent.tsx @@ -7,6 +7,9 @@ import { formatUnits } from 'viem'; import { SpinnerIcon } from '../Icon/SpinnerIcon'; import { cn } from '../../utils/ui'; +import { styled } from 'styled-components'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; +import { StyledSearchInput } from '../StyledComponents/Input'; interface Props { assets?: Asset[]; @@ -72,18 +75,18 @@ function AssetSelectContent({ return (
- +

Select Token

{isBalancesLoading && ( )}
- - {filteredAssets.map((asset) => ( -
- + ))} - + ); } export default AssetSelectContent; + +export const StyledScrollAreaRoot = styled(ScrollArea.Root)` + &::before { + background-image: linear-gradient( + to bottom, + ${(props) => props.theme.backgroundColor}, + hsla(0, 0%, 100%, 0) + ); + } + &::after { + background-image: linear-gradient( + to top, + ${(props) => props.theme.backgroundColor}, + hsla(0, 0%, 100%, 0) + ); + } +`; diff --git a/packages/widget/src/ui/AssetSelect/index.tsx b/packages/widget/src/ui/AssetSelect/index.tsx index b6f2a0eef..3718778b4 100644 --- a/packages/widget/src/ui/AssetSelect/index.tsx +++ b/packages/widget/src/ui/AssetSelect/index.tsx @@ -7,6 +7,8 @@ import { cn } from '../../utils/ui'; import { Dialog } from '../Dialog/Dialog'; import { DialogTrigger } from '../Dialog/DialogTrigger'; import { DialogContent } from '../Dialog/DialogContent'; +import { styled } from 'styled-components'; +import { StyledHighlightButton } from '../StyledComponents/Buttons'; interface Props { asset?: Asset; @@ -29,11 +31,11 @@ function AssetSelect({ return ( - + ) => { return ( - + ); }; diff --git a/packages/widget/src/ui/Button/SettingsButton.tsx b/packages/widget/src/ui/Button/SettingsButton.tsx index cdd39ae3c..29e1478a2 100644 --- a/packages/widget/src/ui/Button/SettingsButton.tsx +++ b/packages/widget/src/ui/Button/SettingsButton.tsx @@ -1,9 +1,9 @@ -import { Cog6ToothIcon } from '@heroicons/react/20/solid'; import { ComponentProps } from 'react'; import { SimpleTooltip } from '../SimpleTooltip'; import { cn } from '../../utils/ui'; import { disclosure } from '../../store/disclosures'; import { GearIcon } from '../Icon/GearIcon'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; export const SettingsButton = ({ className, @@ -11,9 +11,9 @@ export const SettingsButton = ({ }: ComponentProps<'button'>) => { return ( - + ); }; diff --git a/packages/widget/src/ui/Button/ShareButton.tsx b/packages/widget/src/ui/Button/ShareButton.tsx index d6781446a..d89af73bb 100644 --- a/packages/widget/src/ui/Button/ShareButton.tsx +++ b/packages/widget/src/ui/Button/ShareButton.tsx @@ -2,11 +2,12 @@ import toast from 'react-hot-toast'; import { SimpleTooltip } from '../SimpleTooltip'; import { cn } from '../../utils/ui'; import { ShareIcon } from '../Icon/ShareIcon'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; export const ShareButton = ({ shareableLink }: { shareableLink: string }) => { return ( - + ); }; diff --git a/packages/widget/src/ui/ChainSelect/ChainSelectContent.tsx b/packages/widget/src/ui/ChainSelect/ChainSelectContent.tsx index af2d35bcd..333bf56dd 100644 --- a/packages/widget/src/ui/ChainSelect/ChainSelectContent.tsx +++ b/packages/widget/src/ui/ChainSelect/ChainSelectContent.tsx @@ -4,6 +4,9 @@ import { matchSorter } from 'match-sorter'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Chain } from '../../hooks/use-chains'; import { cn } from '../../utils/ui'; +import { StyledScrollAreaRoot } from '../AssetSelect/AssetSelectContent'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; +import { StyledSearchInput } from '../StyledComponents/Input'; interface Props { chains: Chain[]; @@ -32,15 +35,15 @@ function ChainSelectContent({ chains, onChange, onClose }: Props) { return (
- +

Select Network

-
) : ( - {filteredChains.map((chain) => ( - + ))} - + )} ); diff --git a/packages/widget/src/ui/ChainSelect/ChainSelectTrigger.tsx b/packages/widget/src/ui/ChainSelect/ChainSelectTrigger.tsx index ac8c82723..e0b53a888 100644 --- a/packages/widget/src/ui/ChainSelect/ChainSelectTrigger.tsx +++ b/packages/widget/src/ui/ChainSelect/ChainSelectTrigger.tsx @@ -2,6 +2,7 @@ import { ChevronDownIcon } from '@heroicons/react/20/solid'; import { forwardRef } from 'react'; import { Chain } from '../../hooks/use-chains'; import { cn } from '../../utils/ui'; +import { StyledHighlightButton } from '../StyledComponents/Buttons'; interface Props { chain?: Chain; @@ -10,11 +11,11 @@ interface Props { const ChainSelectTrigger = forwardRef( function ChainSelectTrigger({ chain, ...props }, ref) { return ( - +
+ +
+ ); } // diff --git a/packages/widget/src/ui/ConnectedWalletButton.tsx b/packages/widget/src/ui/ConnectedWalletButton.tsx index 29a800396..228c4dd56 100644 --- a/packages/widget/src/ui/ConnectedWalletButton.tsx +++ b/packages/widget/src/ui/ConnectedWalletButton.tsx @@ -1,5 +1,6 @@ import { ComponentProps, forwardRef } from 'react'; import { cn } from '../utils/ui'; +import { StyledThemedButton } from './StyledComponents/Buttons'; type Props = ComponentProps<'button'> & { address: string; @@ -11,10 +12,10 @@ export const ConnectedWalletButton = forwardRef( function Component(props, ref) { const { address, walletLogo, walletName, className, ...rest } = props; return ( - + ); } // diff --git a/packages/widget/src/ui/CraftedBySkip.tsx b/packages/widget/src/ui/CraftedBySkip.tsx index 6ee6c15ae..8449313fc 100644 --- a/packages/widget/src/ui/CraftedBySkip.tsx +++ b/packages/widget/src/ui/CraftedBySkip.tsx @@ -1,3 +1,4 @@ +import { styled } from 'styled-components'; import SkipLogo from './Icon/SkipLogo'; export const CraftedBySkip = () => { @@ -5,8 +6,12 @@ export const CraftedBySkip = () => {

Crafted by

- +
); }; + +const StyledSkipLogo = styled(SkipLogo)` + fill: ${(props) => props.theme.textColor}; +`; diff --git a/packages/widget/src/ui/Dialog/DialogContent.tsx b/packages/widget/src/ui/Dialog/DialogContent.tsx index 51ed1013b..fcf0302a7 100644 --- a/packages/widget/src/ui/Dialog/DialogContent.tsx +++ b/packages/widget/src/ui/Dialog/DialogContent.tsx @@ -4,6 +4,7 @@ import { PropsWithChildren, useContext } from 'react'; import { DialogContext } from './context'; import { cn } from '../../utils/ui'; +import { StyledThemedDiv } from '../StyledComponents/Theme'; interface Props extends PropsWithChildren { onInteractOutside?: DialogContentProps['onInteractOutside']; @@ -17,17 +18,18 @@ export function DialogContent({ children, onInteractOutside }: Props) { return ( <> - {children} - + ); } diff --git a/packages/widget/src/ui/HistoryDialog/DescriptionList.tsx b/packages/widget/src/ui/HistoryDialog/DescriptionList.tsx index a538649c2..dd1b560d7 100644 --- a/packages/widget/src/ui/HistoryDialog/DescriptionList.tsx +++ b/packages/widget/src/ui/HistoryDialog/DescriptionList.tsx @@ -13,10 +13,7 @@ export const Row = ({ className, ...props }: ComponentProps<'div'>) => { export const Dt = ({ className, ...props }: ComponentProps<'dt'>) => { return ( -
+
); }; diff --git a/packages/widget/src/ui/HistoryDialog/HistoryList.tsx b/packages/widget/src/ui/HistoryDialog/HistoryList.tsx index 55af1fe10..c1445da46 100644 --- a/packages/widget/src/ui/HistoryDialog/HistoryList.tsx +++ b/packages/widget/src/ui/HistoryDialog/HistoryList.tsx @@ -27,8 +27,7 @@ import { cn } from '../../utils/ui'; import { ChainSymbol } from '../ChainSymbol'; import { AssetValue } from '../AssetValue'; import { disclosure } from '../../store/disclosures'; -import { useSwapWidgetUIStore } from '../../store/swap-widget'; -import { css } from '@emotion/css'; +import { StyledHighlightButton } from '../StyledComponents/Buttons'; type RootProps = Omit; @@ -117,7 +116,7 @@ export const Item = forwardRef(function Item( @@ -246,9 +245,9 @@ export const Item = forwardRef(function Item(
- +
diff --git a/packages/widget/src/ui/HistoryDialog/index.tsx b/packages/widget/src/ui/HistoryDialog/index.tsx index a96e7aa02..ce9f5d7fe 100644 --- a/packages/widget/src/ui/HistoryDialog/index.tsx +++ b/packages/widget/src/ui/HistoryDialog/index.tsx @@ -1,5 +1,4 @@ import { ArrowLeftIcon } from '@heroicons/react/20/solid'; -import * as Dialog from '@radix-ui/react-dialog'; import * as ScrollArea from '@radix-ui/react-scroll-area'; import { useMemo } from 'react'; @@ -10,6 +9,10 @@ import { useDisclosureKey } from '../../store/disclosures'; import { useTxHistory } from '../../store/tx-history'; import { useAssets } from '../../provider/assets'; import { cn } from '../../utils/ui'; +import { Dialog } from '../Dialog/Dialog'; +import { DialogContent } from '../Dialog/DialogContent'; +import { StyledScrollAreaRoot } from '../AssetSelect/AssetSelectContent'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; export const HistoryDialog = () => { const [isOpen, { close }] = useDisclosureKey('historyDialog'); @@ -24,34 +27,22 @@ export const HistoryDialog = () => { useSyncState(); return ( - - - + close()}> +
- +

Transaction History

- @@ -61,7 +52,12 @@ export const HistoryDialog = () => { )} {entries?.map(([id, data]) => ( - + ))} {!isReady && (
@@ -77,9 +73,9 @@ export const HistoryDialog = () => { - +
- - + +
); }; diff --git a/packages/widget/src/ui/Icon/HistoryIcon.tsx b/packages/widget/src/ui/Icon/HistoryIcon.tsx index c5308742d..2c02294f0 100644 --- a/packages/widget/src/ui/Icon/HistoryIcon.tsx +++ b/packages/widget/src/ui/Icon/HistoryIcon.tsx @@ -3,18 +3,9 @@ import { ComponentProps } from 'react'; export const HistoryIcon = (props: ComponentProps<'svg'>) => { return ( - - - - - + + ); diff --git a/packages/widget/src/ui/Icon/ShareIcon.tsx b/packages/widget/src/ui/Icon/ShareIcon.tsx index 01e40b053..694236b81 100644 --- a/packages/widget/src/ui/Icon/ShareIcon.tsx +++ b/packages/widget/src/ui/Icon/ShareIcon.tsx @@ -9,18 +9,7 @@ export const ShareIcon = (props: React.SVGProps) => { viewBox="0 0 119.79 119.79" {...props} > - - - - - - - - - + ); diff --git a/packages/widget/src/ui/JsonDialog.tsx b/packages/widget/src/ui/JsonDialog.tsx index ca9a50445..f5d9f97a4 100644 --- a/packages/widget/src/ui/JsonDialog.tsx +++ b/packages/widget/src/ui/JsonDialog.tsx @@ -7,6 +7,7 @@ import * as Dialog from '@radix-ui/react-dialog'; import { useMemo, useState } from 'react'; import { useJsonDisclosure } from '../store/disclosures'; import { cn } from '../utils/ui'; +import { StyledThemedButton } from './StyledComponents/Buttons'; export const JsonDialog = () => { const [state, { close }] = useJsonDisclosure(); @@ -48,12 +49,12 @@ export const JsonDialog = () => {
- +

{state.title || 'JSON Viewer'}

diff --git a/packages/widget/src/ui/PreviewRoute/ChainStep.tsx b/packages/widget/src/ui/PreviewRoute/ChainStep.tsx index 96479d968..b8c095991 100644 --- a/packages/widget/src/ui/PreviewRoute/ChainStep.tsx +++ b/packages/widget/src/ui/PreviewRoute/ChainStep.tsx @@ -27,9 +27,10 @@ import { } from '../../utils/ledger-warning'; import { cn } from '../../utils/ui'; import { ExpandArrow } from '../Icon/ExpandArrow'; -import { useSwapWidgetUIStore } from '../../store/swap-widget'; -import { css } from '@emotion/css'; import { useAssets } from '../../provider/assets'; +import { StyledThemedDiv } from '../StyledComponents/Theme'; +import { useTheme, styled } from 'styled-components'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; export const ChainStep = ({ chainID, @@ -75,6 +76,7 @@ export const ChainStep = ({ chainAddresses: ChainAddresses; setChainAddresses: (v: SetChainAddressesParam) => void; }) => { + const theme = useTheme(); const { data: chain } = useChainByID(chainID); const totalChains = chainIDsWithAction.length; @@ -216,7 +218,7 @@ export const ChainStep = ({ >
-
-
+ {chainID} -
+ {signRequired && (
)} -
+ {!isDestination && (
{transferAction && isExpanded && bridge && ( @@ -258,18 +260,18 @@ export const ChainStep = ({ label={`Bridged with ${bridge?.name}`} type="default" > - {chainID} + + {chainID} + )} {!isExpanded && ( @@ -290,22 +292,22 @@ export const ChainStep = ({
)} {!isExpanded && ( - + )} -
@@ -395,19 +397,16 @@ export const ChainStep = ({
)} {stepState?.explorerLink && ( - {stepState.explorerLink.shorthand} - + )}
setIsAddressDialogOpen(index)}> - )} @@ -573,3 +568,27 @@ const AssetSwap = (props: {
); }; + +const StyledAdaptiveLink = styled(AdaptiveLink)` + color: ${(props) => props.theme.brandColor}; +`; + +const StyledPencilSquareIcon = styled(PencilSquareIcon)<{ isFocused: boolean }>` + ${(props) => props.isFocused && props.theme.brandColor}; +`; + +const StyledBridgeLogoContainer = styled.div` + border-radius: 50%; + background-color: ${(props) => props.theme.borderColor}; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + position: absolute; + right: 1rem; +`; + +const StyledChainLogoContainer = styled.div` + background-color: ${(props) => props.theme.borderColor}; +`; diff --git a/packages/widget/src/ui/PreviewRoute/SetAddressDialog.tsx b/packages/widget/src/ui/PreviewRoute/SetAddressDialog.tsx index 4c94baa3d..2b0dfa891 100644 --- a/packages/widget/src/ui/PreviewRoute/SetAddressDialog.tsx +++ b/packages/widget/src/ui/PreviewRoute/SetAddressDialog.tsx @@ -15,8 +15,10 @@ import { TrackWalletCtx } from '../../store/track-wallet'; import { Dialog } from '../Dialog/Dialog'; import { DialogContent } from '../Dialog/DialogContent'; import { cn } from '../../utils/ui'; -import { useSwapWidgetUIStore } from '../../store/swap-widget'; -import { css } from '@emotion/css'; +import { StyledScrollAreaRoot } from '../AssetSelect/AssetSelectContent'; +import { styled } from 'styled-components'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; +import { StyledBorderDiv, StyledBrandDiv } from '../StyledComponents/Theme'; export const SetAddressDialog = ({ open, @@ -111,14 +113,14 @@ export const SetAddressDialog = ({
- +

Set {isDestination ? 'Destination' : 'Recovery'} Address

@@ -131,11 +133,10 @@ export const SetAddressDialog = ({ />
- @@ -152,19 +153,9 @@ export const SetAddressDialog = ({ 'data-[unsupported=true]:before:absolute data-[unsupported=true]:before:inset-0 data-[unsupported=true]:before:cursor-not-allowed' )} > - + {chainType === 'svm' && wallet.isAvailable !== true && (
@@ -219,50 +210,40 @@ export const SetAddressDialog = ({ {!signRequired && (
{isEditing ? ( -
- + setAddress(e.target.value)} /> - - +
) : ( - + )}
)} @@ -284,9 +265,18 @@ export const SetAddressDialog = ({ - +
); }; + +const StyledApproveButton = styled(StyledBrandDiv)` + border-color: ${(props) => props.theme.brandColor}; +`; + +const StyledCancelButton = styled.button` + color: ${(props) => props.theme.brandColor}; + border-color: ${(props) => props.theme.brandColor}; +`; diff --git a/packages/widget/src/ui/PreviewRoute/index.tsx b/packages/widget/src/ui/PreviewRoute/index.tsx index be1200294..bede3637c 100644 --- a/packages/widget/src/ui/PreviewRoute/index.tsx +++ b/packages/widget/src/ui/PreviewRoute/index.tsx @@ -34,9 +34,14 @@ import { txHistory } from '../../store/tx-history'; import { isUserRejectedRequestError } from '../../utils/error'; import { cn } from '../../utils/ui'; import { trackWallet, TrackWalletCtx } from '../../store/track-wallet'; -import { useSwapWidgetUIStore } from '../../store/swap-widget'; -import { css } from '@emotion/css'; import { CraftedBySkip } from '../CraftedBySkip'; +import { styled } from 'styled-components'; +import { + StyledBorderDiv, + StyledBrandDiv, + StyledThemedDiv, +} from '../StyledComponents/Theme'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; export interface Wallet { walletName: string; @@ -267,14 +272,17 @@ export const PreviewRoute = ({ ({ createdAt, id }) => (

Transaction Failed!

-
+            
               {err instanceof Error
                 ? `${err.name}: ${err.message}`
                 : String(err)}
               

{new Date(createdAt).toISOString()} -
+ + ); } return ( - + ); }; return ( -
+
- +

Transaction Preview

{isExpanded && ( - + )}
-
+ {chainIDsWithAction.map( ({ chainID, transferAction, swapAction }, index) => ( ) )} -
+
{statusData?.isSuccess && submitMutation.isSuccess ? (
@@ -568,16 +569,14 @@ export const PreviewRoute = ({ )} {submitMutation.isPending || submitMutation.isSuccess ? ( - + ) : ( )}
-
+
); }; const HREF_COMMON_FINALITY_TIMES = `https://docs.axelar.dev/learn/txduration#common-finality-time-for-interchain-transactions`; + +const StyledBrandTextButton = styled.button` + color: ${(props) => props.theme.brandColor}; +`; diff --git a/packages/widget/src/ui/PriceImpactWarning.tsx b/packages/widget/src/ui/PriceImpactWarning.tsx index 1a5737d82..f7416d7b3 100644 --- a/packages/widget/src/ui/PriceImpactWarning.tsx +++ b/packages/widget/src/ui/PriceImpactWarning.tsx @@ -1,7 +1,7 @@ import { ExclamationTriangleIcon } from '@heroicons/react/20/solid'; import { useDisclosureKey } from '../store/disclosures'; import { cn } from '../utils/ui'; -import { useSwapWidgetUIStore } from '../store/swap-widget'; +import { StyledBrandDiv } from './StyledComponents/Theme'; interface Props { onGoBack: () => void; @@ -33,26 +33,24 @@ export const PriceImpactWarning = ({
- +
); diff --git a/packages/widget/src/ui/SettingsDialog/GasSetting.tsx b/packages/widget/src/ui/SettingsDialog/GasSetting.tsx index 226bd9077..86577719b 100644 --- a/packages/widget/src/ui/SettingsDialog/GasSetting.tsx +++ b/packages/widget/src/ui/SettingsDialog/GasSetting.tsx @@ -4,6 +4,7 @@ import { formatNumberWithCommas, formatNumberWithoutCommas, } from '../../utils/number'; +import { StyledThemedDiv } from '../StyledComponents/Theme'; export const GasSetting = () => { const currentValue = useSettingsStore((state) => state.customGasAmount); @@ -14,7 +15,8 @@ export const GasSetting = () => {
- {
- {
{OPTION_VALUES.map((value, i) => ( - + ))}
diff --git a/packages/widget/src/ui/SettingsDialog/index.tsx b/packages/widget/src/ui/SettingsDialog/index.tsx index 885291ce8..c77addb4d 100644 --- a/packages/widget/src/ui/SettingsDialog/index.tsx +++ b/packages/widget/src/ui/SettingsDialog/index.tsx @@ -1,35 +1,27 @@ import { ArrowLeftIcon } from '@heroicons/react/16/solid'; -import * as Dialog from '@radix-ui/react-dialog'; - import { AdaptiveLink } from '../AdaptiveLink'; import { GasSetting } from './GasSetting'; import { SaveIndicator } from './SaveIndicator'; import { SlippageSetting } from './SlippageSetting'; import { useDisclosureKey } from '../../store/disclosures'; -import { cn } from '../../utils/ui'; + +import { Dialog } from '../Dialog/Dialog'; +import { DialogContent } from '../Dialog/DialogContent'; +import { StyledThemedButton } from '../StyledComponents/Buttons'; export const SettingsDialog = () => { const [isOpen, { close }] = useDisclosureKey('settingsDialog'); return ( - - - - {' '} + close()}> +
- +

Swap Settings

@@ -59,7 +51,7 @@ export const SettingsDialog = () => { wallets state, transaction history, and settings.

*/}
- - + +
); }; diff --git a/packages/widget/src/ui/SimpleTooltip.tsx b/packages/widget/src/ui/SimpleTooltip.tsx index fe44014e3..2abc58404 100644 --- a/packages/widget/src/ui/SimpleTooltip.tsx +++ b/packages/widget/src/ui/SimpleTooltip.tsx @@ -2,6 +2,7 @@ import * as Tooltip from '@radix-ui/react-tooltip'; import { ComponentPropsWithoutRef, ReactNode } from 'react'; import { cn } from '../utils/ui'; import { useSwapWidgetUIStore } from '../store/swap-widget'; +import { styled } from 'styled-components'; type Props = Tooltip.TooltipProps & { type?: 'default' | 'warning' | 'brand'; @@ -26,41 +27,36 @@ export const SimpleTooltip = (props: Props) => { return ( {children} - {label} - + ); }; + +const StyledTooltipContent = styled(Tooltip.Content)<{ + type: 'default' | 'warning' | 'brand'; +}>` + background-color: ${(props) => props.theme.backgroundColor}; + + fill: ${(props) => props.theme.backgroundColor}; + color: ${(props) => + props.type === 'warning' ? props.theme.brandColor : props.theme.textColor}; +`; diff --git a/packages/widget/src/ui/StyledComponents/Buttons.ts b/packages/widget/src/ui/StyledComponents/Buttons.ts new file mode 100644 index 000000000..e847155c8 --- /dev/null +++ b/packages/widget/src/ui/StyledComponents/Buttons.ts @@ -0,0 +1,17 @@ +import { styled } from 'styled-components'; + +export const StyledThemedButton = styled.button` + background-color: ${(props) => props.theme.backgroundColor}; + color: ${(props) => props.theme.textColor}; + fill: ${(props) => props.theme.textColor}; + border-color: ${(props) => props.theme.borderColor}; + &:hover { + background-color: ${(props) => props.theme.highlightColor}; + } +`; + +export const StyledHighlightButton = styled.button` + background-color: ${(props) => props.theme.highlightColor}; + border-color: ${(props) => props.theme.borderColor}; + color: ${(props) => props.theme.textColor}; +`; diff --git a/packages/widget/src/ui/StyledComponents/Input.ts b/packages/widget/src/ui/StyledComponents/Input.ts new file mode 100644 index 000000000..696c70b6e --- /dev/null +++ b/packages/widget/src/ui/StyledComponents/Input.ts @@ -0,0 +1,9 @@ +import { styled } from 'styled-components'; + +export const StyledSearchInput = styled.input` + background-color: ${(props) => props.theme.highlightColor}; + border-color: ${(props) => props.theme.borderColor}; + &::placeholder { + color: ${(props) => props.theme.textColor}; + } +`; diff --git a/packages/widget/src/ui/StyledComponents/Theme.ts b/packages/widget/src/ui/StyledComponents/Theme.ts new file mode 100644 index 000000000..52fff08a4 --- /dev/null +++ b/packages/widget/src/ui/StyledComponents/Theme.ts @@ -0,0 +1,15 @@ +import { styled } from 'styled-components'; + +export const StyledThemedDiv = styled.div` + background-color: ${(props) => props.theme.backgroundColor}; + color: ${(props) => props.theme.textColor}; + border-color: ${(props) => props.theme.borderColor}; +`; + +export const StyledBrandDiv = styled.div` + background-color: ${(props) => props.theme.brandColor}; +`; + +export const StyledBorderDiv = styled.div` + border-color: ${(props) => props.theme.borderColor}; +`; diff --git a/packages/widget/src/ui/SwapDetails.tsx b/packages/widget/src/ui/SwapDetails.tsx index e4b710082..99172658f 100644 --- a/packages/widget/src/ui/SwapDetails.tsx +++ b/packages/widget/src/ui/SwapDetails.tsx @@ -11,6 +11,8 @@ import { SimpleTooltip } from './SimpleTooltip'; import { disclosure } from '../store/disclosures'; import { SwapWidgetStore } from '../hooks/use-swap-widget'; import { ConversionRate } from './ConversionRate'; +import { StyledThemedButton } from './StyledComponents/Buttons'; +import { StyledBorderDiv } from './StyledComponents/Theme'; type Props = SwapWidgetStore & { amountOut: string; @@ -126,12 +128,11 @@ export const SwapDetails = ({ } return ( - setDetailsOpen(open)} @@ -216,16 +217,16 @@ export const SwapDetails = ({
Slippage
- + {slippage}%
@@ -281,6 +282,6 @@ export const SwapDetails = ({ )} -
+ ); }; diff --git a/packages/widget/src/ui/TransactionDialog.tsx b/packages/widget/src/ui/TransactionDialog.tsx index 9f3a59771..e2bf1a793 100644 --- a/packages/widget/src/ui/TransactionDialog.tsx +++ b/packages/widget/src/ui/TransactionDialog.tsx @@ -6,7 +6,7 @@ import { PreviewRoute } from './PreviewRoute'; import { useDisclosureKey } from '../store/disclosures'; import { PriceImpactWarning } from './PriceImpactWarning'; import { cn } from '../utils/ui'; -import { useSwapWidgetUIStore } from '../store/swap-widget'; +import { StyledBrandDiv } from './StyledComponents/Theme'; export type ActionType = 'NONE' | 'TRANSFER' | 'SWAP'; @@ -67,20 +67,18 @@ function TransactionDialog({ return (
- + {isOpen && route && (
- +

Connect Wallet

{totalWallets < 1 && ( @@ -63,11 +65,10 @@ export function WalletModal({ chainType, onClose, wallets }: Props) {

)} - @@ -83,10 +84,9 @@ export function WalletModal({ chainType, onClose, wallets }: Props) { 'data-[unsupported=true]:before:absolute data-[unsupported=true]:before:inset-0 data-[unsupported=true]:before:cursor-not-allowed' )} > - + {wallet.isWalletConnected && (
); } diff --git a/packages/widget/src/ui/Widget.tsx b/packages/widget/src/ui/Widget.tsx index 6d68f4305..0144fa020 100644 --- a/packages/widget/src/ui/Widget.tsx +++ b/packages/widget/src/ui/Widget.tsx @@ -23,16 +23,24 @@ import { useChains } from '../hooks/use-chains'; import TransactionDialog from './TransactionDialog'; import { SpinnerIcon } from './Icon/SpinnerIcon'; import { useSwapWidgetUIStore } from '../store/swap-widget'; -import { css } from '@emotion/css'; import { CraftedBySkip } from './CraftedBySkip'; -import { useFixRadixUiWheelEvent } from '../hooks/use-fix-radix-ui-wheel-event'; +import { StyledBrandDiv, StyledThemedDiv } from './StyledComponents/Theme'; +import { styled, useTheme } from 'styled-components'; +import { Toaster, ToasterProps } from 'react-hot-toast'; + +export type SwapWidgetUIProps = Pick< + React.HTMLAttributes, + 'className' | 'style' +> & { + toasterProps?: ToasterProps; +}; export const SwapWidgetUI = ({ className, style, -}: Pick, 'className' | 'style'>) => { - useFixRadixUiWheelEvent(); - + toasterProps, +}: SwapWidgetUIProps) => { + const theme = useTheme(); useEffect(() => void disclosure.rehydrate(), []); const { openWalletModal } = useWalletModal(); @@ -116,214 +124,220 @@ export const SwapWidgetUI = ({ const accountStateKey = `${srcAccount?.isWalletConnected ? 'src' : 'no-src'}`; return ( - - -
-
-

From

-
- - - -
- {srcAccount?.address && srcAccount?.wallet ? ( - - - sourceChain?.chainID && openWalletModal(sourceChain.chainID) - } - walletName={srcAccount.wallet?.walletPrettyName} - walletLogo={ - srcAccount.wallet.walletInfo - ? typeof srcAccount.wallet.walletInfo.logo === 'string' - ? srcAccount.wallet.walletInfo.logo - : srcAccount.wallet.walletInfo.logo?.major || - srcAccount.wallet.walletInfo.logo?.minor - : '' - } - className="animate-slide-left-and-fade" - key={srcAccount.address} - /> - - ) : null} -
-
- -
-
-
- + + + + +
+

From

+
+ + + +
+ {srcAccount?.address && srcAccount?.wallet ? ( + + + sourceChain?.chainID && + openWalletModal(sourceChain.chainID) + } + walletName={srcAccount.wallet?.walletPrettyName} + walletLogo={ + srcAccount.wallet.walletInfo + ? typeof srcAccount.wallet.walletInfo.logo === 'string' + ? srcAccount.wallet.walletInfo.logo + : srcAccount.wallet.walletInfo.logo?.major || + srcAccount.wallet.walletInfo.logo?.minor + : '' + } + className="animate-slide-left-and-fade" + key={srcAccount.address} + /> + + ) : null}
-

To

-
-
- -
- {route && ( - - )} - {routeLoading && ( -
-

Finding best route...

- +
+
- )} - {route && !routeLoading && numberOfTransactions > 1 && ( -
-
-
+
+
-

{numberOfTransactions} Signature Required

-
- )} - {!!routeError && ( -
-

{routeError}

+

To

- )} - {destinationChain?.chainID === 'dydx-mainnet-1' && ( -
-

- This transaction will let you transfer and stake tokens on dydx, - it will not allow you to trade. Follow the{' '} - - dydx frontend - {' '} - directions to set up a trading account -

+
+
- )} - {!isWalletConnected && ( - - )} - {sourceChain && isWalletConnected && ( -
- -
- )} - -
- - - - - - + )} + {routeLoading && ( +
+

Finding best route...

+ +
+ )} + {route && !routeLoading && numberOfTransactions > 1 && ( +
+
+ + +
+

{numberOfTransactions} Signature Required

+
+ )} + {!!routeError && ( +
+

{routeError}

+
+ )} + {destinationChain?.chainID === 'dydx-mainnet-1' && ( +
+

+ This transaction will let you transfer and stake tokens on + dydx, it will not allow you to trade. Follow the{' '} + + dydx frontend + {' '} + directions to set up a trading account +

+
+ )} + {!isWalletConnected && ( + { + if (sourceChain && !srcAccount?.isWalletConnected) { + openWalletModal(sourceChain.chainID); + return; + } + if (!destinationChain) { + promptDestAsset(); + return; + } + }} + > +
+ {!srcAccount?.isWalletConnected && 'Connect Wallet'} +
+
+ )} + {sourceChain && isWalletConnected && ( +
+ +
+ )} + + + + + + + + + + ); }; + +const StyledAppWrapper = styled.div` + div, + p, + span { + color: ${(props) => props.theme.textColor}; + } +`; diff --git a/packages/widget/src/ui/WithStyledShadowDom.tsx b/packages/widget/src/ui/WithStyledShadowDom.tsx new file mode 100644 index 000000000..072e98a83 --- /dev/null +++ b/packages/widget/src/ui/WithStyledShadowDom.tsx @@ -0,0 +1,70 @@ +import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; +import { createRoot } from 'react-dom/client'; +import { StyleSheetManager } from 'styled-components'; +import shadowDomStyles from '../styles/shadowDomStyles.css'; +import toastStyles from '../styles/toastStyles.css'; +import cssReset from '../styles/cssReset.css'; +import { useInjectFontsToDocumentHead } from '../hooks/use-inject-fonts-to-document-head'; + +export const WithStyledShadowDom = ({ children }: { children: ReactNode }) => { + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + useInjectFontsToDocumentHead(); + + const onRefLoaded = useCallback((element: HTMLDivElement) => { + if (element !== null) { + const shadowRoot = element.attachShadow({ mode: 'open' }); + + const styleContainer = document.createElement('div'); + const appContainer = document.createElement('div'); + const hostStyle = document.createElement('style'); + hostStyle.textContent = ` + ${cssReset} + ${toastStyles} + ${shadowDomStyles} + `; + + shadowRoot.appendChild(hostStyle); + shadowRoot.appendChild(styleContainer); + shadowRoot.appendChild(appContainer); + + const Root = ({ children }: { children?: ReactNode }) => { + useEffect(() => { + const stopPropagation = (e: Event) => e.stopPropagation(); + + element?.shadowRoot?.addEventListener('wheel', stopPropagation, true); + element?.shadowRoot?.addEventListener( + 'touchmove', + stopPropagation, + true + ); + return () => { + element?.shadowRoot?.removeEventListener( + 'wheel', + stopPropagation, + true + ); + element?.shadowRoot?.removeEventListener( + 'touchmove', + stopPropagation, + true + ); + }; + }, []); + return ( + + {children} + + ); + }; + + createRoot(appContainer).render({children}); + } + }, []); + + return isClient ?
: null; +}; diff --git a/packages/widget/src/ui/index.tsx b/packages/widget/src/ui/index.tsx index f02bc7d99..796cb1810 100644 --- a/packages/widget/src/ui/index.tsx +++ b/packages/widget/src/ui/index.tsx @@ -1,21 +1,18 @@ -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; import { SwapWidgetProvider, SwapWidgetProviderProps } from '../provider'; import { configureSwapWidget, ConfigureSwapWidgetArgs, } from '../store/swap-widget'; -import { SwapWidgetUI } from './Widget'; -import shadowDomStyles from '../styles/shadowDomStyles.css'; -import toastStyles from '../styles/toastStyles.css'; -import cssReset from '../styles/cssReset.css'; -import { Scope } from 'react-shadow-scope'; -import { useInjectFontsToDocumentHead } from '../hooks/use-inject-fonts-to-document-head'; +import { SwapWidgetUI, SwapWidgetUIProps } from './Widget'; +import { defaultTheme, PartialTheme } from './theme'; +import { WithStyledShadowDom } from './WithStyledShadowDom'; +import { ThemeProvider } from 'styled-components'; -export type SwapWidgetWithoutProvidersProps = Pick< - React.HTMLAttributes, - 'className' | 'style' -> & - ConfigureSwapWidgetArgs; +export type SwapWidgetWithoutProvidersProps = SwapWidgetUIProps & + ConfigureSwapWidgetArgs & { + theme?: PartialTheme; + }; export type SwapWidgetProps = SwapWidgetWithoutProvidersProps & Partial; @@ -23,68 +20,80 @@ export type SwapWidgetProps = SwapWidgetWithoutProvidersProps & export const SwapWidgetWithoutProviders: React.FC< SwapWidgetWithoutProvidersProps > = ({ - colors, settings, onlyTestnet, defaultRoute, routeConfig, - className, - style, + theme, filter, + ...swapWidgetUIProps }) => { - useInjectFontsToDocumentHead(); useEffect(() => { configureSwapWidget({ - colors, onlyTestnet, settings, defaultRoute, routeConfig, filter, }); - }, [colors, onlyTestnet, settings, defaultRoute, routeConfig]); + }, [onlyTestnet, settings, defaultRoute, routeConfig]); + + const mergedThemes = useMemo(() => { + return { + ...defaultTheme, + ...theme, + }; + }, [defaultTheme, theme]); return ( - - - + + + + + ); }; export const SwapWidget: React.FC = ({ - colors, settings, onlyTestnet, defaultRoute, routeConfig, + theme, className, style, filter, + toasterProps, ...swapWidgetProviderProps }) => { - useInjectFontsToDocumentHead(); useEffect(() => { configureSwapWidget({ - colors, onlyTestnet, settings, defaultRoute, routeConfig, filter, }); - }, [colors, onlyTestnet, settings, defaultRoute, routeConfig]); + }, [onlyTestnet, settings, defaultRoute, routeConfig]); + + const mergedThemes = useMemo(() => { + return { + ...defaultTheme, + ...theme, + }; + }, [defaultTheme, theme]); return ( - - - - - + + + + + + + ); }; diff --git a/packages/widget/src/ui/theme.ts b/packages/widget/src/ui/theme.ts new file mode 100644 index 000000000..8ee222a74 --- /dev/null +++ b/packages/widget/src/ui/theme.ts @@ -0,0 +1,23 @@ +import 'styled-components'; + +export const defaultTheme = { + backgroundColor: 'white', + textColor: 'black', + borderColor: 'rgb(229 229 229)', + brandColor: 'rgb(255, 72, 110)', + highlightColor: 'rgb(245, 245, 245)', +}; + +export type PartialTheme = Partial | undefined; + +export type Theme = { + backgroundColor: string; + textColor: string; + borderColor: string; + brandColor: string; + highlightColor: string; +}; + +declare module 'styled-components' { + export interface DefaultTheme extends Theme {} +} diff --git a/yarn.lock b/yarn.lock index 14e702921..a4e2d7f54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2689,6 +2689,15 @@ __metadata: languageName: node linkType: hard +"@emotion/is-prop-valid@npm:1.2.2": + version: 1.2.2 + resolution: "@emotion/is-prop-valid@npm:1.2.2" + dependencies: + "@emotion/memoize": ^0.8.1 + checksum: 61f6b128ea62b9f76b47955057d5d86fcbe2a6989d2cd1e583daac592901a950475a37d049b9f7a7c6aa8758a33b408735db759fdedfd1f629df0f85ab60ea25 + languageName: node + linkType: hard + "@emotion/memoize@npm:^0.8.1": version: 0.8.1 resolution: "@emotion/memoize@npm:0.8.1" @@ -2716,7 +2725,7 @@ __metadata: languageName: node linkType: hard -"@emotion/unitless@npm:^0.8.1": +"@emotion/unitless@npm:0.8.1, @emotion/unitless@npm:^0.8.1": version: 0.8.1 resolution: "@emotion/unitless@npm:0.8.1" checksum: 385e21d184d27853bb350999471f00e1429fa4e83182f46cd2c164985999d9b46d558dc8b9cc89975cb337831ce50c31ac2f33b15502e85c299892e67e7b4a88 @@ -7938,7 +7947,6 @@ __metadata: "@types/qrcode": ^1.4.2 "@types/react": ^18.0.6 "@types/react-dom": ^18.0.2 - "@types/styled-components": ^5.1.25 autoprefixer: ^10.4.19 clsx: ^2.1.1 match-sorter: ^6.3.4 @@ -7946,8 +7954,9 @@ __metadata: postcss-prefixwrap: ^1.49.0 react-hot-toast: ^2.4.1 react-icons: ^5.2.1 - react-shadow-scope: ^1.0.5 rollup-plugin-copy: ^3.5.0 + styled-components: ^6.0.0 + stylis: ^4.0.0 tailwind-merge: ^2.3.0 tailwindcss: ^3.4.3 tailwindcss-animate: ^1.0.7 @@ -7959,6 +7968,8 @@ __metadata: peerDependencies: react: 17.x || 18.x react-dom: 17.x || 18.x + styled-components: ^6.0.0 + stylis: ^4.0.0 languageName: unknown linkType: soft @@ -9656,16 +9667,6 @@ __metadata: languageName: node linkType: hard -"@types/hoist-non-react-statics@npm:*": - version: 3.3.5 - resolution: "@types/hoist-non-react-statics@npm:3.3.5" - dependencies: - "@types/react": "*" - hoist-non-react-statics: ^3.3.0 - checksum: b645b062a20cce6ab1245ada8274051d8e2e0b2ee5c6bd58215281d0ec6dae2f26631af4e2e7c8abe238cdcee73fcaededc429eef569e70908f82d0cc0ea31d7 - languageName: node - linkType: hard - "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" @@ -9924,14 +9925,10 @@ __metadata: languageName: node linkType: hard -"@types/styled-components@npm:^5.1.25": - version: 5.1.34 - resolution: "@types/styled-components@npm:5.1.34" - dependencies: - "@types/hoist-non-react-statics": "*" - "@types/react": "*" - csstype: ^3.0.2 - checksum: 7868066a15afe42d8b353953fc196a7f11a9918b1c980f51d21bb9b49e309c7fe2d730be2d3dc48e92ab4de20f44a3c9dfe6cc98b6ea4dd02655e6f8128171b6 +"@types/stylis@npm:4.2.5": + version: 4.2.5 + resolution: "@types/stylis@npm:4.2.5" + checksum: 24f91719db5569979e9e2f197e050ef82e1fd72474e8dc45bca38d48ee56481eae0f0d4a7ac172540d7774b45a2a78d901a4c6d07bba77a33dbccff464ea3edf languageName: node linkType: hard @@ -12372,6 +12369,13 @@ __metadata: languageName: node linkType: hard +"camelize@npm:^1.0.0": + version: 1.0.1 + resolution: "camelize@npm:1.0.1" + checksum: 91d8611d09af725e422a23993890d22b2b72b4cabf7239651856950c76b4bf53fe0d0da7c5e4db05180e898e4e647220e78c9fbc976113bd96d603d1fcbfcb99 + languageName: node + linkType: hard + "caniuse-api@npm:^3.0.0": version: 3.0.0 resolution: "caniuse-api@npm:3.0.0" @@ -13175,6 +13179,13 @@ __metadata: languageName: node linkType: hard +"css-color-keywords@npm:^1.0.0": + version: 1.0.0 + resolution: "css-color-keywords@npm:1.0.0" + checksum: 8f125e3ad477bd03c77b533044bd9e8a6f7c0da52d49bbc0bbe38327b3829d6ba04d368ca49dd9ff3b667d2fc8f1698d891c198bbf8feade1a5501bf5a296408 + languageName: node + linkType: hard + "css-declaration-sorter@npm:^6.3.1": version: 6.4.1 resolution: "css-declaration-sorter@npm:6.4.1" @@ -13197,6 +13208,17 @@ __metadata: languageName: node linkType: hard +"css-to-react-native@npm:3.2.0": + version: 3.2.0 + resolution: "css-to-react-native@npm:3.2.0" + dependencies: + camelize: ^1.0.0 + css-color-keywords: ^1.0.0 + postcss-value-parser: ^4.0.2 + checksum: 263be65e805aef02c3f20c064665c998a8c35293e1505dbe6e3054fb186b01a9897ac6cf121f9840e5a9dfe3fb3994f6fcd0af84a865f1df78ba5bf89e77adce + languageName: node + linkType: hard + "css-tree@npm:^1.1.2, css-tree@npm:^1.1.3": version: 1.1.3 resolution: "css-tree@npm:1.1.3" @@ -13316,7 +13338,7 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.0.2, csstype@npm:^3.0.7, csstype@npm:^3.1.3": +"csstype@npm:3.1.3, csstype@npm:^3.0.2, csstype@npm:^3.0.7, csstype@npm:^3.1.3": version: 3.1.3 resolution: "csstype@npm:3.1.3" checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 @@ -15933,7 +15955,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -20461,7 +20483,7 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f @@ -20479,7 +20501,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.23, postcss@npm:^8.4.38": +"postcss@npm:8.4.38, postcss@npm:^8.4.23, postcss@npm:^8.4.38": version: 8.4.38 resolution: "postcss@npm:8.4.38" dependencies: @@ -21130,16 +21152,6 @@ __metadata: languageName: node linkType: hard -"react-shadow-scope@npm:^1.0.5": - version: 1.0.5 - resolution: "react-shadow-scope@npm:1.0.5" - peerDependencies: - react: ^16.8.3 || ^18 - react-dom: ^16.14.0 || ^18 - checksum: 6ae39c58ac8ea4f18473da47ccb445a116f96fea162c44637dc5f69bd55858d423fee88436ba31319170b439f35b541f156729ff973de809e84e1eaa84c04cc9 - languageName: node - linkType: hard - "react-stately@npm:^3.31.1": version: 3.31.1 resolution: "react-stately@npm:3.31.1" @@ -22170,6 +22182,13 @@ __metadata: languageName: node linkType: hard +"shallowequal@npm:1.1.0": + version: 1.1.0 + resolution: "shallowequal@npm:1.1.0" + checksum: f4c1de0837f106d2dbbfd5d0720a5d059d1c66b42b580965c8f06bb1db684be8783538b684092648c981294bf817869f743a066538771dbecb293df78f765e00 + languageName: node + linkType: hard + "shebang-command@npm:^1.2.0": version: 1.2.0 resolution: "shebang-command@npm:1.2.0" @@ -22909,6 +22928,26 @@ __metadata: languageName: node linkType: hard +"styled-components@npm:^6.0.0": + version: 6.1.11 + resolution: "styled-components@npm:6.1.11" + dependencies: + "@emotion/is-prop-valid": 1.2.2 + "@emotion/unitless": 0.8.1 + "@types/stylis": 4.2.5 + css-to-react-native: 3.2.0 + csstype: 3.1.3 + postcss: 8.4.38 + shallowequal: 1.1.0 + stylis: 4.3.2 + tslib: 2.6.2 + peerDependencies: + react: ">= 16.8.0" + react-dom: ">= 16.8.0" + checksum: 18fb43fe49b61c7b5d3b6c6bd6fd315c7f83310916b52e7b788286064f6586d3211d40528d9413b4f812c6ff806ae25976f7e400f9b125a8f7ea653b39f155c8 + languageName: node + linkType: hard + "styled-jsx@npm:5.1.1": version: 5.1.1 resolution: "styled-jsx@npm:5.1.1" @@ -22944,6 +22983,13 @@ __metadata: languageName: node linkType: hard +"stylis@npm:4.3.2, stylis@npm:^4.0.0": + version: 4.3.2 + resolution: "stylis@npm:4.3.2" + checksum: 0faa8a97ff38369f47354376cd9f0def9bf12846da54c28c5987f64aaf67dcb6f00dce88a8632013bfb823b2c4d1d62a44f4ac20363a3505a7ab4e21b70179fc + languageName: node + linkType: hard + "sucrase@npm:^3.20.3, sucrase@npm:^3.32.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -23472,6 +23518,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.6.2": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.2": version: 2.6.3 resolution: "tslib@npm:2.6.3"