-
+
+
-
+ disabled={!destinationChain}
+ onClick={async () => {
+ if (!destinationChain || !invertButtonRef.current) return;
+ invertButtonRef.current.setAttribute('data-swap', 'true');
+ await onInvertDirection();
+ }}
+ data-testid="swap-button"
+ ref={invertButtonRef}
+ >
+
+
-
{numberOfTransactions} Signature Required
-
- )}
- {!!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 && (
-
- )}
- {sourceChain && isWalletConnected && (
-
-
-
- )}
-
-
-
-
-
-
-
-
+ )}
+ {routeLoading && (
+
+
Finding best route...
+
+
+ )}
+ {route && !routeLoading && numberOfTransactions > 1 && (
+
+
+
+
+
+
{numberOfTransactions} Signature Required
+
+ )}
+ {!!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"