Skip to content

Commit

Permalink
Merge pull request #17 from toddkao/add-web-component
Browse files Browse the repository at this point in the history
Add web component version of widget
  • Loading branch information
toddkao authored Jul 4, 2024
2 parents 0cdd584 + 7519b0a commit 392e8e7
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/clever-donuts-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@skip-go/widget': minor
---

Add web-component
8 changes: 1 addition & 7 deletions examples/nextjs/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import '../styles/globals.css';
import type { AppProps } from 'next/app';

import { SwapWidgetProvider } from '@skip-go/widget';

function MyApp({ Component, pageProps }: AppProps) {
return (
<SwapWidgetProvider>
<Component {...pageProps} />
</SwapWidgetProvider>
);
return <Component {...pageProps} />;
}

export default MyApp;
59 changes: 47 additions & 12 deletions examples/nextjs/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,61 @@
import { NextPage } from 'next';
import { SwapWidget } from '@skip-go/widget';
import '@skip-go/widget/style.css';
import React from 'react';
import dynamic from 'next/dynamic';
import {
SwapWidget,
SwapWidgetProvider,
initializeSwapWidget,
} from '@skip-go/widget';

const NoSsrBase = (props: any) => <>{props.children}</>;

const NoSSR = dynamic(() => Promise.resolve(NoSsrBase), {
ssr: false,
});

const Home: NextPage = () => {
initializeSwapWidget();

return (
<div>
<div style={{ display: 'flex', gap: 50 }}>
<div
style={{
width: '450px',
height: '820px',
}}
>
<SwapWidgetProvider>
<SwapWidget
colors={{
primary: '#FF4FFF',
}}
defaultRoute={{
srcChainID: 'osmosis-1',
srcAssetDenom:
'ibc/1480b8fd20ad5fcae81ea87584d269547dd4d436843c1d20f15e00eb64743ef4',
}}
/>
</SwapWidgetProvider>
</div>
<div
style={{
width: '450px',
height: '820px',
}}
>
<SwapWidget
colors={{
primary: '#FF4FFF',
}}
defaultRoute={{
srcChainID: 'osmosis-1',
srcAssetDenom:
'ibc/1480b8fd20ad5fcae81ea87584d269547dd4d436843c1d20f15e00eb64743ef4',
}}
/>
<NoSSR>
<skip-widget
colors={JSON.stringify({
primary: '#FF4FFF',
})}
default-route={JSON.stringify({
srcChainID: 'osmosis-1',
srcAssetDenom:
'ibc/1480b8fd20ad5fcae81ea87584d269547dd4d436843c1d20f15e00eb64743ef4',
})}
></skip-widget>
</NoSSR>
</div>
</div>
);
Expand Down
44 changes: 44 additions & 0 deletions packages/widget/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,47 @@ The `SwapWidgetProvider` component accepts the following prop:
- `apiURL` (Optional): 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.

By following these steps, you can easily integrate the Skip Go Widget into your React application and customize it to meet your specific needs.

### Web Component

The web component is created with the `@r2wc/react-to-web-component` library.

In order to register the web-component, you must call the `initializeSwapWidget` function:

```tsx
import { initializeSwapWidget } from '@skip-go/widget';

initializeSwapWidget();
```

voila! you can now use the `swap-widget` web-component

The props for the web component are the same as `SwapWidgetProps` and `SwapWidgetProviderProps` except that
they are sent to the web-component as attributes in kebab-case as strings or stringified objects ie.

```tsx
interface SwapWidgetProps {
colors
defaultRoute
routeConfig
```
becomes
```tsx
<swap-widget
class-name="classname"
colors='{"primary":"#FF4FFF"}'
default-route={JSON.stringify({
srcChainID: 'osmosis-1',
srcAssetDenom:
'ibc/1480b8fd20ad5fcae81ea87584d269547dd4d436843c1d20f15e00eb64743ef4',
})}
></swap-widget>
```
the web-component exposes the `SwapWidgetProviderProps` as attributes on swap-widget as well
```tsx
<swap-widget toaster-props="" endpoint-options="" api-url=""></swap-widget>
```
1 change: 1 addition & 0 deletions packages/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"@heroicons/react": "^2.1.3",
"@injectivelabs/utils": "1.14.6",
"@interchain-ui/react": "^1.23.13",
"@r2wc/react-to-web-component": "^2.0.3",
"@radix-ui/colors": "^3.0.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-collapsible": "^1.0.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/widget/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import packageJson from './package.json';
export default [
{
input: ['./src/index.ts'],
external: ['react', 'react-dom'],
external: ['react', 'react-dom', '@r2wc/react-to-web-component'],
output: {
file: packageJson.exports['.'].import,
format: 'esm',
Expand Down
1 change: 1 addition & 0 deletions packages/widget/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import './styles/global.css';
export { SwapWidgetProvider } from './provider';
export { SwapWidget, SwapWidgetProps } from './ui';
export { initializeSwapWidget } from './ui/WebComponent';
export { useAssets } from './provider/assets';
export { useChains, useChainByID } from './hooks/use-chains';
77 changes: 77 additions & 0 deletions packages/widget/src/ui/WebComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { SwapWidget, SwapWidgetProps } from './index';
import { SwapWidgetProvider, SwapWidgetProviderProps } from '../provider';

type WebComponentProps = SwapWidgetProps & SwapWidgetProviderProps;

function isJsonString(str: string) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}

const camelize = (inputString: string) =>
inputString.replace(/-./g, (x) => x[1].toUpperCase());

const WidgetWithProvider = (props: WebComponentProps) => {
// @ts-ignore
const parsedProps = Array.from(props.container.attributes).map(
({ name, value }: any) => {
return { key: name, value };
}
);

const realProps = parsedProps.reduce((accumulator, initialValue) => {
const { key, value } = initialValue;

accumulator[camelize(key)] = isJsonString(value)
? JSON.parse(value)
: value;
return accumulator;
}, {});

const { toasterProps, endpointOptions, apiURL, ...swapWidgetProps } =
realProps as WebComponentProps;
return (
<SwapWidgetProvider
toasterProps={toasterProps}
endpointOptions={endpointOptions}
apiURL={apiURL}
>
<SwapWidget {...swapWidgetProps} />
</SwapWidgetProvider>
);
};

const WEB_COMPONENT_NAME = 'skip-widget';

let initialized = false;

export const initializeSwapWidget = () => {
if (!initialized && typeof window !== 'undefined') {
import('@r2wc/react-to-web-component').then(
({ default: ReactToWebComponent }) => {
const WebComponent = ReactToWebComponent(WidgetWithProvider);

if (!customElements.get(WEB_COMPONENT_NAME)) {
customElements.define(WEB_COMPONENT_NAME, WebComponent);
}
initialized = true;
}
);
}
};

type Stringify<T> = {
[K in keyof T]?: string;
};

declare global {
namespace JSX {
interface IntrinsicElements {
[WEB_COMPONENT_NAME]: Stringify<WebComponentProps>;
}
}
}
20 changes: 20 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5563,6 +5563,25 @@ __metadata:
languageName: node
linkType: hard

"@r2wc/core@npm:^1.0.0":
version: 1.1.0
resolution: "@r2wc/core@npm:1.1.0"
checksum: b46c67f15707be832d2f32737dce9b1c1c1f2cf895b79d564fe7971bf174e0afe940eec028ef53c80e0784bd6b6954bdb91f69542b94f39367c572be3c82db81
languageName: node
linkType: hard

"@r2wc/react-to-web-component@npm:^2.0.3":
version: 2.0.3
resolution: "@r2wc/react-to-web-component@npm:2.0.3"
dependencies:
"@r2wc/core": ^1.0.0
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
checksum: 762bbd07d0f0d1476e4874a8f7533321e92af8a1033c7e1ec5cf9a4085bcc361fccf67e66e347baf8228b77dc33f59e4f8b63197044a3b529ac5ff0387db1cd8
languageName: node
linkType: hard

"@radix-ui/colors@npm:^3.0.0":
version: 3.0.0
resolution: "@radix-ui/colors@npm:3.0.0"
Expand Down Expand Up @@ -7867,6 +7886,7 @@ __metadata:
"@heroicons/react": ^2.1.3
"@injectivelabs/utils": 1.14.6
"@interchain-ui/react": ^1.23.13
"@r2wc/react-to-web-component": ^2.0.3
"@radix-ui/colors": ^3.0.0
"@radix-ui/react-accordion": ^1.1.2
"@radix-ui/react-collapsible": ^1.0.3
Expand Down

0 comments on commit 392e8e7

Please sign in to comment.