diff --git a/apps/gallery/stories/composites/wui-balance.stories.ts b/apps/gallery/stories/composites/wui-balance.stories.ts new file mode 100644 index 0000000000..502f332363 --- /dev/null +++ b/apps/gallery/stories/composites/wui-balance.stories.ts @@ -0,0 +1,18 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-balance' +import type { WuiBalance } from '@web3modal/ui/src/composites/wui-balance' +import { html } from 'lit' + +type Component = Meta + +export default { + title: 'Composites/wui-balance', + args: { + dollars: '4,798', + pennies: '75' + } +} as Component + +export const Default: Component = { + render: args => html`` +} diff --git a/apps/gallery/stories/composites/wui-banner.stories.ts b/apps/gallery/stories/composites/wui-banner.stories.ts new file mode 100644 index 0000000000..fad470cc85 --- /dev/null +++ b/apps/gallery/stories/composites/wui-banner.stories.ts @@ -0,0 +1,29 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-banner' +import type { WuiBanner } from '@web3modal/ui/src/composites/wui-banner' +import { html } from 'lit' +import { iconOptions } from '../../utils/PresetUtils' +import '../../components/gallery-container' + +type Component = Meta + +export default { + title: 'Composites/wui-banner', + args: { + text: 'You can only receive assets on these networks', + icon: 'warningCircle' + }, + argTypes: { + icon: { + options: iconOptions, + control: { type: 'select' } + } + } +} as Component + +export const Default: Component = { + render: args => + html` ` +} diff --git a/apps/gallery/stories/composites/wui-chip-button.stories.ts b/apps/gallery/stories/composites/wui-chip-button.stories.ts new file mode 100644 index 0000000000..9f9a6104b2 --- /dev/null +++ b/apps/gallery/stories/composites/wui-chip-button.stories.ts @@ -0,0 +1,42 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-chip-button' +import type { WuiChipButton } from '@web3modal/ui/src/composites/wui-chip-button' +import { html } from 'lit' +import { chipOptions, iconOptions, walletImagesOptions } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-chip-button', + args: { + variant: 'fill', + disabled: false, + icon: 'externalLink', + imageSrc: walletImagesOptions[3]?.src, + text: 'dianeyes.walletconnect.eth' + }, + argTypes: { + variant: { + options: chipOptions, + control: { type: 'select' } + }, + icon: { + options: iconOptions, + control: { type: 'select' } + }, + disabled: { + control: { type: 'boolean' } + } + } +} as Component + +export const Default: Component = { + render: args => + html`` +} diff --git a/apps/gallery/stories/composites/wui-compatible-network.stories.ts b/apps/gallery/stories/composites/wui-compatible-network.stories.ts new file mode 100644 index 0000000000..3d8272385b --- /dev/null +++ b/apps/gallery/stories/composites/wui-compatible-network.stories.ts @@ -0,0 +1,22 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-compatible-network' +import type { WuiCompatibleNetwork } from '@web3modal/ui/src/composites/wui-compatible-network' +import { html } from 'lit' +import { networkImages } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-compatible-network', + args: { + text: 'Only receive assets on these networks"', + networkImages + } +} as Component + +export const Default: Component = { + render: args => html` + + + ` +} diff --git a/apps/gallery/stories/composites/wui-list-network.stories.ts b/apps/gallery/stories/composites/wui-list-network.stories.ts index ee83ead6e4..88abdab377 100644 --- a/apps/gallery/stories/composites/wui-list-network.stories.ts +++ b/apps/gallery/stories/composites/wui-list-network.stories.ts @@ -12,7 +12,11 @@ export default { args: { walletImages: walletImagesOptions, imageSrc: networkImageSrc, - name: 'Ethereum' + name: 'Ethereum', + transparent: false + }, + transparent: { + control: { type: 'boolean' } } } as Component @@ -23,6 +27,7 @@ export const Default: Component = { .imageSrc=${args.imageSrc} ?disabled=${args.disabled} name=${args.name} + ?transparent=${args.transparent} > ` } diff --git a/apps/gallery/stories/composites/wui-network-image.stories.ts b/apps/gallery/stories/composites/wui-network-image.stories.ts index 22f33721f6..0fd792b3e5 100644 --- a/apps/gallery/stories/composites/wui-network-image.stories.ts +++ b/apps/gallery/stories/composites/wui-network-image.stories.ts @@ -16,7 +16,7 @@ export default { }, argTypes: { size: { - options: ['sm', 'md', 'lg'], + options: ['xs', 'sm', 'md', 'lg'], control: { type: 'select' } } } diff --git a/apps/gallery/stories/composites/wui-profile-button.stories.ts b/apps/gallery/stories/composites/wui-profile-button.stories.ts new file mode 100644 index 0000000000..f5d423f6f8 --- /dev/null +++ b/apps/gallery/stories/composites/wui-profile-button.stories.ts @@ -0,0 +1,32 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-profile-button' +import type { WuiProfileButton } from '@web3modal/ui/src/composites/wui-profile-button' +import { html } from 'lit' +import { address, avatarImageSrc, networkImageSrc } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-profile-button', + args: { + networkSrc: networkImageSrc, + avatarSrc: avatarImageSrc, + address, + isProfileName: false + }, + argTypes: { + isProfileName: { + control: { type: 'boolean' } + } + } +} as Component + +export const Default: Component = { + render: args => + html`` +} diff --git a/apps/gallery/stories/composites/wui-promo.stories.ts b/apps/gallery/stories/composites/wui-promo.stories.ts new file mode 100644 index 0000000000..0ae2b47c44 --- /dev/null +++ b/apps/gallery/stories/composites/wui-promo.stories.ts @@ -0,0 +1,17 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-promo' +import type { WuiPromo } from '@web3modal/ui/src/composites/wui-promo' +import { html } from 'lit' + +type Component = Meta + +export default { + title: 'Composites/wui-promo', + args: { + text: 'Activate your account' + } +} as Component + +export const Default: Component = { + render: args => html`` +} diff --git a/apps/gallery/stories/composites/wui-tooltip-select.stories.ts b/apps/gallery/stories/composites/wui-tooltip-select.stories.ts new file mode 100644 index 0000000000..ddcd25bbf3 --- /dev/null +++ b/apps/gallery/stories/composites/wui-tooltip-select.stories.ts @@ -0,0 +1,30 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-tooltip-select' +import type { WuiTooltipSelect } from '@web3modal/ui/src/composites/wui-tooltip-select' +import { html } from 'lit' +import '../../components/gallery-container' +import { iconOptions } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-tooltip-select', + args: { + icon: 'card', + text: 'Buy' + }, + argTypes: { + icon: { + options: iconOptions, + control: { type: 'select' } + } + } +} as Component + +export const Default: Component = { + render: args => html` + + + ` +} diff --git a/apps/gallery/stories/composites/wui-tooltip.stories.ts b/apps/gallery/stories/composites/wui-tooltip.stories.ts index ee0f691597..39b12947e1 100644 --- a/apps/gallery/stories/composites/wui-tooltip.stories.ts +++ b/apps/gallery/stories/composites/wui-tooltip.stories.ts @@ -11,18 +11,27 @@ export default { title: 'Composites/wui-tooltip', args: { message: 'Tooltip', - placement: 'top' + placement: 'top', + variant: 'fill' }, argTypes: { placement: { options: placementOptions, control: { type: 'select' } + }, + variant: { + options: ['fill', 'shade'], + control: { type: 'select' } } } } as Component export const Default: Component = { render: args => - html`` + html`` } diff --git a/apps/gallery/utils/PresetUtils.ts b/apps/gallery/utils/PresetUtils.ts index 9184e67b5c..f9393e98aa 100644 --- a/apps/gallery/utils/PresetUtils.ts +++ b/apps/gallery/utils/PresetUtils.ts @@ -133,6 +133,14 @@ export const walletImageSrc = export const networkImageSrc = 'https://explorer-api.walletconnect.com/w3m/v1/getAssetImage/692ed6ba-e569-459a-556a-776476829e00?projectId=c1781fc385454899a2b1385a2b83df3b' +export const networkImages = [ + 'https://explorer-api.walletconnect.com/w3m/v1/getAssetImage/692ed6ba-e569-459a-556a-776476829e00?projectId=c1781fc385454899a2b1385a2b83df3b', + 'https://explorer-api.walletconnect.com/w3m/v1/getAssetImage/30c46e53-e989-45fb-4549-be3bd4eb3b00?projectId=c1781fc385454899a2b1385a2b83df3b', + 'https://explorer-api.walletconnect.com/w3m/v1/getAssetImage/93564157-2e8e-4ce7-81df-b264dbee9b00?projectId=c1781fc385454899a2b1385a2b83df3b', + 'https://explorer-api.walletconnect.com/w3m/v1/getAssetImage/ab9c186a-c52f-464b-2906-ca59d760a400?projectId=c1781fc385454899a2b1385a2b83df3b', + 'https://explorer-api.walletconnect.com/w3m/v1/getAssetImage/41d04d42-da3b-4453-8506-668cc0727900?projectId=c1781fc385454899a2b1385a2b83df3b' +] + export const avatarImageSrc = 'https://i.seadn.io/gcs/files/007a5af0d93d561f87c8d026ddd5179e.png?auto=format&dpr=1&w=1000' @@ -179,6 +187,7 @@ export const iconOptions: IconType[] = [ 'compass', 'copy', 'cursor', + 'cursorTransparent', 'desktop', 'disconnect', 'discord', diff --git a/apps/laboratory/src/pages/library/ethers-wallet.tsx b/apps/laboratory/src/pages/library/ethers-wallet.tsx new file mode 100644 index 0000000000..17166d878a --- /dev/null +++ b/apps/laboratory/src/pages/library/ethers-wallet.tsx @@ -0,0 +1,35 @@ +import { createWeb3Modal, defaultConfig } from '@web3modal/ethers/react' +import { ThemeStore } from '../../utils/StoreUtil' +import { EthersConstants } from '../../utils/EthersConstants' +import { ConstantsUtil } from '../../utils/ConstantsUtil' +import { EthersTests } from '../../components/Ethers/EthersTests' +import { Web3ModalButtons } from '../../components/Web3ModalButtons' + +const modal = createWeb3Modal({ + ethersConfig: defaultConfig({ + metadata: ConstantsUtil.Metadata, + defaultChainId: 1, + rpcUrl: 'https://cloudflare-eth.com', + enableEmail: true + }), + chains: EthersConstants.chains, + projectId: ConstantsUtil.ProjectId, + enableAnalytics: true, + metadata: ConstantsUtil.Metadata, + termsConditionsUrl: 'https://walletconnect.com/terms', + privacyPolicyUrl: 'https://walletconnect.com/privacy', + enableOnramp: true, + customWallets: ConstantsUtil.CustomWallets, + enableWalletFeatures: true +}) + +ThemeStore.setModal(modal) + +export default function Ethers() { + return ( + <> + + + + ) +} diff --git a/apps/laboratory/src/pages/library/wagmi-wallet.tsx b/apps/laboratory/src/pages/library/wagmi-wallet.tsx new file mode 100644 index 0000000000..6c9b8336b4 --- /dev/null +++ b/apps/laboratory/src/pages/library/wagmi-wallet.tsx @@ -0,0 +1,51 @@ +import { createWeb3Modal } from '@web3modal/wagmi/react' +import { defaultWagmiConfig } from '@web3modal/wagmi/react/config' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { useEffect, useState } from 'react' +import { WagmiProvider } from 'wagmi' +import { Web3ModalButtons } from '../../components/Web3ModalButtons' +import { WagmiTests } from '../../components/Wagmi/WagmiTests' +import { ThemeStore } from '../../utils/StoreUtil' +import { WagmiConstantsUtil } from '../../utils/WagmiConstants' +import { ConstantsUtil } from '../../utils/ConstantsUtil' + +const queryClient = new QueryClient() + +export const wagmiConfig = defaultWagmiConfig({ + chains: WagmiConstantsUtil.chains, + projectId: ConstantsUtil.ProjectId, + metadata: ConstantsUtil.Metadata, + enableEmail: true, + ssr: true +}) + +const modal = createWeb3Modal({ + wagmiConfig, + projectId: ConstantsUtil.ProjectId, + enableAnalytics: true, + metadata: ConstantsUtil.Metadata, + termsConditionsUrl: 'https://walletconnect.com/terms', + privacyPolicyUrl: 'https://walletconnect.com/privacy', + enableOnramp: true, + customWallets: ConstantsUtil.CustomWallets, + enableWalletFeatures: true +}) + +ThemeStore.setModal(modal) + +export default function Wagmi() { + const [ready, setReady] = useState(false) + + useEffect(() => { + setReady(true) + }, []) + + return ready ? ( + + + + + + + ) : null +} diff --git a/apps/laboratory/src/utils/DataUtil.ts b/apps/laboratory/src/utils/DataUtil.ts index e98d371de4..f47555dbc3 100644 --- a/apps/laboratory/src/utils/DataUtil.ts +++ b/apps/laboratory/src/utils/DataUtil.ts @@ -33,6 +33,11 @@ export const wagmiSdkOptions = [ title: 'Email', link: '/library/wagmi-email/', description: 'Configuration usign wagmi and implementing email login' + }, + { + title: 'Wallet', + link: '/library/wagmi-wallet/', + description: 'Configuration usign wagmi and implementing email wallet' } ] @@ -51,5 +56,10 @@ export const ethersSdkOptions = [ title: 'Email', link: '/library/ethers-email/', description: 'Configuration usign ethers and implementing email login' + }, + { + title: 'Wallet', + link: '/library/ethers-wallet/', + description: 'Configuration usign ethers and implementing email wallet' } ] diff --git a/packages/core/src/controllers/OptionsController.ts b/packages/core/src/controllers/OptionsController.ts index 4b9b17a9b4..a79df96217 100644 --- a/packages/core/src/controllers/OptionsController.ts +++ b/packages/core/src/controllers/OptionsController.ts @@ -19,6 +19,7 @@ export interface OptionsControllerState { enableAnalytics?: boolean metadata?: Metadata enableOnramp?: boolean + enableWalletFeatures?: boolean } type StateKey = keyof OptionsControllerState @@ -92,5 +93,9 @@ export const OptionsController = { setOnrampEnabled(enableOnramp: OptionsControllerState['enableOnramp']) { state.enableOnramp = enableOnramp + }, + + setWalletFeaturesEnabled(enableWalletFeatures: OptionsControllerState['enableWalletFeatures']) { + state.enableWalletFeatures = enableWalletFeatures } } diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index e09fb2914a..819efb1038 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -10,6 +10,7 @@ export interface RouterControllerState { | 'AllWallets' | 'ApproveTransaction' | 'BuyInProgress' + | 'WalletCompatibleNetworks' | 'Connect' | 'ConnectingExternal' | 'ConnectingWalletConnect' @@ -23,6 +24,7 @@ export interface RouterControllerState { | 'OnRampFiatSelect' | 'OnRampProviders' | 'OnRampTokenSelect' + | 'WalletReceive' | 'SwitchNetwork' | 'Transactions' | 'UnsupportedChain' diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index fba2aa8681..d048612cfa 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -33,6 +33,8 @@ export * from './src/views/w3m-update-email-wallet-view/index.js' export * from './src/views/w3m-update-email-primary-otp-view/index.js' export * from './src/views/w3m-update-email-secondary-otp-view/index.js' export * from './src/views/w3m-unsupported-chain-view/index.js' +export * from './src/views/w3m-wallet-receive-view/index.js' +export * from './src/views/w3m-wallet-compatible-networks-view/index.js' export * from './src/partials/w3m-all-wallets-list/index.js' export * from './src/partials/w3m-all-wallets-search/index.js' @@ -51,6 +53,8 @@ export * from './src/partials/w3m-mobile-download-links/index.js' export * from './src/partials/w3m-onramp-providers-footer/index.js' export * from './src/partials/w3m-snackbar/index.js' export * from './src/partials/w3m-email-login-widget/index.js' +export * from './src/partials/w3m-account-default-widget/index.js' +export * from './src/partials/w3m-account-wallet-features-widget/index.js' export { Web3ModalScaffold } from './src/client.js' export type { LibraryOptions, ScaffoldOptions } from './src/client.js' diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 283c1ca1ea..ee3910ca08 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -47,6 +47,7 @@ export interface LibraryOptions { enableAnalytics?: OptionsControllerState['enableAnalytics'] metadata?: OptionsControllerState['metadata'] enableOnramp?: OptionsControllerState['enableOnramp'] + enableWalletFeatures?: OptionsControllerState['enableWalletFeatures'] allowUnsupportedChain?: NetworkControllerState['allowUnsupportedChain'] _sdkVersion: OptionsControllerState['sdkVersion'] } @@ -238,6 +239,10 @@ export class Web3ModalScaffold { OptionsController.setOnrampEnabled(Boolean(options.enableOnramp)) } + if (options.enableWalletFeatures) { + OptionsController.setWalletFeaturesEnabled(Boolean(options.enableWalletFeatures)) + } + if (options.allowUnsupportedChain) { NetworkController.setAllowUnsupportedChain(options.allowUnsupportedChain) } diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index 637289cc9a..c009d71279 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -111,6 +111,10 @@ export class W3mRouter extends LitElement { return html`` case 'BuyInProgress': return html`` + case 'WalletReceive': + return html`` + case 'WalletCompatibleNetworks': + return html`` default: return html`` } diff --git a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts new file mode 100644 index 0000000000..ca920b6815 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts @@ -0,0 +1,309 @@ +import { + AccountController, + CoreHelperUtil, + ModalController, + NetworkController, + RouterController, + AssetUtil, + StorageUtil, + ConnectorController, + EventsController, + ConnectionController, + SnackController, + ConstantsUtil, + OptionsController +} from '@web3modal/core' +import { UiHelperUtil, customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import { state } from 'lit/decorators.js' +import { ifDefined } from 'lit/directives/if-defined.js' +import styles from './styles.js' + +@customElement('w3m-account-default-widget') +export class W3mAccountDefaultWidget extends LitElement { + public static override styles = styles + + // -- Members ------------------------------------------- // + private unsubscribe: (() => void)[] = [] + + // -- State & Properties -------------------------------- // + @state() private address = AccountController.state.address + + @state() private profileImage = AccountController.state.profileImage + + @state() private profileName = AccountController.state.profileName + + @state() private network = NetworkController.state.caipNetwork + + @state() private disconnecting = false + + @state() private balance = AccountController.state.balance + + @state() private balanceSymbol = AccountController.state.balanceSymbol + + public constructor() { + super() + this.unsubscribe.push( + ...[ + AccountController.subscribe(val => { + if (val.address) { + this.address = val.address + this.profileImage = val.profileImage + this.profileName = val.profileName + this.balance = val.balance + this.balanceSymbol = val.balanceSymbol + } else { + SnackController.showError('Account not found') + } + }) + ], + NetworkController.subscribeKey('caipNetwork', val => { + if (val?.id) { + this.network = val + } + }) + ) + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe()) + } + + // -- Render -------------------------------------------- // + public override render() { + if (!this.address) { + throw new Error('w3m-account-view: No account provided') + } + + const networkImage = AssetUtil.getNetworkImage(this.network) + + return html` + + + + + ${this.profileName + ? UiHelperUtil.getTruncateString({ + string: this.profileName, + charsStart: 20, + charsEnd: 0, + truncate: 'end' + }) + : UiHelperUtil.getTruncateString({ + string: this.address ? this.address : '', + charsStart: 4, + charsEnd: 4, + truncate: 'middle' + })} + + + + ${CoreHelperUtil.formatBalance(this.balance, this.balanceSymbol)} + + ${this.explorerBtnTemplate()} + + + + ${this.emailCardTemplate()} ${this.emailBtnTemplate()} + + + + ${this.network?.name ?? 'Unknown'} + + + ${this.onrampTemplate()} + + Activity + + + Disconnect + + ` + } + + // -- Private ------------------------------------------- // + private onrampTemplate() { + const { enableOnramp } = OptionsController.state + + if (!enableOnramp) { + return null + } + + return html` + + Buy crypto + + ` + } + + private emailCardTemplate() { + const type = StorageUtil.getConnectedConnector() + const emailConnector = ConnectorController.getEmailConnector() + const { origin } = location + if (!emailConnector || type !== 'EMAIL' || origin.includes(ConstantsUtil.SECURE_SITE)) { + return null + } + + return html` + + ` + } + + private handleClickPay() { + RouterController.push('OnRampProviders') + } + + private explorerBtnTemplate() { + const { addressExplorerUrl } = AccountController.state + + if (!addressExplorerUrl) { + return null + } + + return html` + + + Block Explorer + + + ` + } + + private emailBtnTemplate() { + const type = StorageUtil.getConnectedConnector() + const emailConnector = ConnectorController.getEmailConnector() + if (!emailConnector || type !== 'EMAIL') { + return null + } + const email = emailConnector.provider.getEmail() ?? '' + + return html` + this.onGoToUpdateEmail(email)} + > + ${email} + + ` + } + + private isAllowedNetworkSwitch() { + const { requestedCaipNetworks } = NetworkController.state + const isMultiNetwork = requestedCaipNetworks ? requestedCaipNetworks.length > 1 : false + const isValidNetwork = requestedCaipNetworks?.find(({ id }) => id === this.network?.id) + + return isMultiNetwork || !isValidNetwork + } + + private onCopyAddress() { + try { + if (this.address) { + CoreHelperUtil.copyToClopboard(this.address) + SnackController.showSuccess('Address copied') + } + } catch { + SnackController.showError('Failed to copy') + } + } + + private onNetworks() { + if (this.isAllowedNetworkSwitch()) { + EventsController.sendEvent({ type: 'track', event: 'CLICK_NETWORKS' }) + RouterController.push('Networks') + } + } + + private onTransactions() { + EventsController.sendEvent({ type: 'track', event: 'CLICK_TRANSACTIONS' }) + RouterController.push('Transactions') + } + + private async onDisconnect() { + try { + this.disconnecting = true + await ConnectionController.disconnect() + EventsController.sendEvent({ type: 'track', event: 'DISCONNECT_SUCCESS' }) + ModalController.close() + } catch { + EventsController.sendEvent({ type: 'track', event: 'DISCONNECT_ERROR' }) + SnackController.showError('Failed to disconnect') + } finally { + this.disconnecting = false + } + } + + private onExplorer() { + const { addressExplorerUrl } = AccountController.state + if (addressExplorerUrl) { + CoreHelperUtil.openHref(addressExplorerUrl, '_blank') + } + } + + private onGoToUpgradeView() { + EventsController.sendEvent({ type: 'track', event: 'EMAIL_UPGRADE_FROM_MODAL' }) + RouterController.push('UpgradeEmailWallet') + } + + private onGoToUpdateEmail(email: string) { + RouterController.push('UpdateEmailWallet', { email }) + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-account-default-widget': W3mAccountDefaultWidget + } +} diff --git a/packages/scaffold/src/views/w3m-account-view/styles.ts b/packages/scaffold/src/partials/w3m-account-default-widget/styles.ts similarity index 100% rename from packages/scaffold/src/views/w3m-account-view/styles.ts rename to packages/scaffold/src/partials/w3m-account-default-widget/styles.ts diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts new file mode 100644 index 0000000000..761119a258 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts @@ -0,0 +1,126 @@ +import { + AccountController, + ModalController, + NetworkController, + AssetUtil, + RouterController +} from '@web3modal/core' +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import { state } from 'lit/decorators.js' +import { ifDefined } from 'lit/directives/if-defined.js' +import styles from './styles.js' +import { ConstantsUtil } from '../../utils/ConstantsUtil.js' + +@customElement('w3m-account-wallet-features-widget') +export class W3mAccountWalletFeaturesWidget extends LitElement { + public static override styles = styles + + // -- Members ------------------------------------------- // + private unsubscribe: (() => void)[] = [] + + // -- State & Properties -------------------------------- // + @state() private address = AccountController.state.address + + @state() private profileImage = AccountController.state.profileImage + + @state() private profileName = AccountController.state.profileName + + @state() private network = NetworkController.state.caipNetwork + + public constructor() { + super() + this.unsubscribe.push( + ...[ + AccountController.subscribe(val => { + if (val.address) { + this.address = val.address + this.profileImage = val.profileImage + this.profileName = val.profileName + } else { + ModalController.close() + } + }) + ], + NetworkController.subscribeKey('caipNetwork', val => { + if (val?.id) { + this.network = val + } + }) + ) + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe()) + } + + // -- Render -------------------------------------------- // + public override render() { + if (!this.address) { + throw new Error('w3m-account-view: No account provided') + } + + const networkImage = AssetUtil.getNetworkImage(this.network) + + return html` + ${this.activateAccountTemplate()} + + + + + + + + + + + ` + } + + // -- Private ------------------------------------------- // + + private activateAccountTemplate() { + // eslint-disable-next-line no-warning-comments + // Todo: Check if SA is deployed + + return html` ` + } + + private onProfileButtonClick() { + RouterController.push('AccountSettings') + } + + private onBuyClick() { + RouterController.push('OnRampProviders') + } + + private onReceiveClick() { + RouterController.push('WalletReceive') + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-account-wallet-features-widget': W3mAccountWalletFeaturesWidget + } +} diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts new file mode 100644 index 0000000000..264076216f --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts @@ -0,0 +1,24 @@ +import { css } from 'lit' + +export default css` + wui-flex { + width: 100%; + } + + wui-promo { + position: absolute; + top: -32px; + } + + wui-profile-button { + margin-top: var(--wui-spacing-2l); + } + + wui-tooltip-select { + width: 100%; + } + + wui-tabs { + width: 100%; + } +` diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index 948f9464b6..97206b5fc7 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -49,7 +49,9 @@ function headings() { WhatIsABuy: 'What is Buy?', BuyInProgress: 'Buy', OnRampTokenSelect: 'Select Token', - OnRampFiatSelect: 'Select Currency' + OnRampFiatSelect: 'Select Currency', + WalletReceive: 'Receive', + WalletCompatibleNetworks: 'Compatible Networks' } } diff --git a/packages/scaffold/src/utils/ConstantsUtil.ts b/packages/scaffold/src/utils/ConstantsUtil.ts new file mode 100644 index 0000000000..62a6e66b03 --- /dev/null +++ b/packages/scaffold/src/utils/ConstantsUtil.ts @@ -0,0 +1,3 @@ +export const ConstantsUtil = { + ACCOUNT_TABS: [{ label: 'Tokens' }, { label: 'NFTs' }, { label: 'Activity' }] +} diff --git a/packages/scaffold/src/views/w3m-account-settings-view/index.ts b/packages/scaffold/src/views/w3m-account-settings-view/index.ts index 61cc2faff9..9ac0bf7cf5 100644 --- a/packages/scaffold/src/views/w3m-account-settings-view/index.ts +++ b/packages/scaffold/src/views/w3m-account-settings-view/index.ts @@ -7,7 +7,9 @@ import { ModalController, NetworkController, RouterController, - SnackController + SnackController, + StorageUtil, + ConnectorController } from '@web3modal/core' import { UiHelperUtil, customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' @@ -31,10 +33,6 @@ export class W3mAccountSettingsView extends LitElement { @state() private profileName = AccountController.state.profileName - @state() private balance = AccountController.state.balance - - @state() private balanceSymbol = AccountController.state.balanceSymbol - @state() private network = NetworkController.state.caipNetwork @state() private disconnecting = false @@ -48,8 +46,6 @@ export class W3mAccountSettingsView extends LitElement { this.address = val.address this.profileImage = val.profileImage this.profileName = val.profileName - this.balance = val.balance - this.balanceSymbol = val.balanceSymbol } else { ModalController.close() } @@ -111,17 +107,12 @@ export class W3mAccountSettingsView extends LitElement { @click=${this.onCopyAddress} > - - - ${CoreHelperUtil.formatBalance(this.balance, this.balanceSymbol)} - - ${this.explorerBtnTemplate()} - - + + ${this.emailBtnTemplate()} - - - Activity - - - Block Explorer - - - ` - } - private isAllowedNetworkSwitch() { const { requestedCaipNetworks } = NetworkController.state const isMultiNetwork = requestedCaipNetworks ? requestedCaipNetworks.length > 1 : false @@ -201,6 +161,32 @@ export class W3mAccountSettingsView extends LitElement { } } + private emailBtnTemplate() { + const type = StorageUtil.getConnectedConnector() + const emailConnector = ConnectorController.getEmailConnector() + if (!emailConnector || type !== 'EMAIL') { + return null + } + const email = emailConnector.provider.getEmail() ?? '' + + return html` + this.onGoToUpdateEmail(email)} + > + ${email} + + ` + } + + private onGoToUpdateEmail(email: string) { + RouterController.push('UpdateEmailWallet', { email }) + } + private onNetworks() { if (this.isAllowedNetworkSwitch()) { RouterController.push('Networks') @@ -220,13 +206,6 @@ export class W3mAccountSettingsView extends LitElement { this.disconnecting = false } } - - private onExplorer() { - const { addressExplorerUrl } = AccountController.state - if (addressExplorerUrl) { - CoreHelperUtil.openHref(addressExplorerUrl, '_blank') - } - } } declare global { diff --git a/packages/scaffold/src/views/w3m-account-view/index.ts b/packages/scaffold/src/views/w3m-account-view/index.ts index c8500ff45b..9911b7fafc 100644 --- a/packages/scaffold/src/views/w3m-account-view/index.ts +++ b/packages/scaffold/src/views/w3m-account-view/index.ts @@ -1,306 +1,27 @@ -import { - AccountController, - CoreHelperUtil, - ModalController, - NetworkController, - RouterController, - AssetUtil, - StorageUtil, - ConnectorController, - EventsController, - ConnectionController, - SnackController, - ConstantsUtil, - OptionsController -} from '@web3modal/core' -import { UiHelperUtil, customElement } from '@web3modal/ui' +import { OptionsController, StorageUtil } from '@web3modal/core' +import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' -import { state } from 'lit/decorators.js' -import { ifDefined } from 'lit/directives/if-defined.js' -import styles from './styles.js' @customElement('w3m-account-view') export class W3mAccountView extends LitElement { - public static override styles = styles - - // -- Members -------------------------------------------- // - private unsubscribe: (() => void)[] = [] - - // -- State & Properties --------------------------------- // - @state() private address = AccountController.state.address - - @state() private profileImage = AccountController.state.profileImage - - @state() private profileName = AccountController.state.profileName - - @state() private network = NetworkController.state.caipNetwork - - @state() private disconnecting = false - - @state() private balance = AccountController.state.balance - - @state() private balanceSymbol = AccountController.state.balanceSymbol - - public constructor() { - super() - this.unsubscribe.push( - ...[ - AccountController.subscribe(val => { - if (val.address) { - this.address = val.address - this.profileImage = val.profileImage - this.profileName = val.profileName - this.balance = val.balance - this.balanceSymbol = val.balanceSymbol - } else { - ModalController.close() - } - }) - ], - NetworkController.subscribeKey('caipNetwork', val => { - if (val?.id) { - this.network = val - } - }) - ) - } - - public override disconnectedCallback() { - this.unsubscribe.forEach(unsubscribe => unsubscribe()) - } - // -- Render -------------------------------------------- // public override render() { - if (!this.address) { - throw new Error('w3m-account-view: No account provided') - } - - const networkImage = AssetUtil.getNetworkImage(this.network) - - return html` - - - - - - ${this.profileName - ? UiHelperUtil.getTruncateString({ - string: this.profileName, - charsStart: 20, - charsEnd: 0, - truncate: 'end' - }) - : UiHelperUtil.getTruncateString({ - string: this.address, - charsStart: 4, - charsEnd: 4, - truncate: 'middle' - })} - - - - ${CoreHelperUtil.formatBalance(this.balance, this.balanceSymbol)} - - ${this.explorerBtnTemplate()} - - - - ${this.emailCardTemplate()} ${this.emailBtnTemplate()} - - - - ${this.network?.name ?? 'Unknown'} - - - ${this.onrampTemplate()} - - Activity - - - Disconnect - - - ` - } - - // -- Private ------------------------------------------- // - private onrampTemplate() { - const { enableOnramp } = OptionsController.state - - if (!enableOnramp) { - return null - } - - return html` - - Buy crypto - - ` - } - - private emailCardTemplate() { const type = StorageUtil.getConnectedConnector() - const emailConnector = ConnectorController.getEmailConnector() - const { origin } = location - if (!emailConnector || type !== 'EMAIL' || origin.includes(ConstantsUtil.SECURE_SITE)) { - return null - } return html` - + ${OptionsController.state.enableWalletFeatures && type === 'EMAIL' + ? this.walletFeaturesTemplate() + : this.defaultTemplate()} ` } - private handleClickPay() { - RouterController.push('OnRampProviders') - } - - private explorerBtnTemplate() { - const { addressExplorerUrl } = AccountController.state - - if (!addressExplorerUrl) { - return null - } - - return html` - - - Block Explorer - - - ` - } - - private emailBtnTemplate() { - const type = StorageUtil.getConnectedConnector() - const emailConnector = ConnectorController.getEmailConnector() - if (!emailConnector || type !== 'EMAIL') { - return null - } - const email = emailConnector.provider.getEmail() ?? '' - - return html` - this.onGoToUpdateEmail(email)} - > - ${email} - - ` - } - - private isAllowedNetworkSwitch() { - const { requestedCaipNetworks } = NetworkController.state - const isMultiNetwork = requestedCaipNetworks ? requestedCaipNetworks.length > 1 : false - const isValidNetwork = requestedCaipNetworks?.find(({ id }) => id === this.network?.id) - - return isMultiNetwork || !isValidNetwork - } - - private onCopyAddress() { - try { - if (this.address) { - CoreHelperUtil.copyToClopboard(this.address) - SnackController.showSuccess('Address copied') - } - } catch { - SnackController.showError('Failed to copy') - } - } - - private onNetworks() { - if (this.isAllowedNetworkSwitch()) { - EventsController.sendEvent({ type: 'track', event: 'CLICK_NETWORKS' }) - RouterController.push('Networks') - } - } - - private onTransactions() { - EventsController.sendEvent({ type: 'track', event: 'CLICK_TRANSACTIONS' }) - RouterController.push('Transactions') - } - - private async onDisconnect() { - try { - this.disconnecting = true - await ConnectionController.disconnect() - EventsController.sendEvent({ type: 'track', event: 'DISCONNECT_SUCCESS' }) - ModalController.close() - } catch { - EventsController.sendEvent({ type: 'track', event: 'DISCONNECT_ERROR' }) - SnackController.showError('Failed to disconnect') - } finally { - this.disconnecting = false - } - } - - private onExplorer() { - const { addressExplorerUrl } = AccountController.state - if (addressExplorerUrl) { - CoreHelperUtil.openHref(addressExplorerUrl, '_blank') - } - } - - private onGoToUpgradeView() { - EventsController.sendEvent({ type: 'track', event: 'EMAIL_UPGRADE_FROM_MODAL' }) - RouterController.push('UpgradeEmailWallet') + // -- Private ------------------------------------------- // + private walletFeaturesTemplate() { + return html`` } - private onGoToUpdateEmail(email: string) { - RouterController.push('UpdateEmailWallet', { email }) + private defaultTemplate() { + return html`` } } diff --git a/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/index.ts b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/index.ts new file mode 100644 index 0000000000..4e301c22de --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/index.ts @@ -0,0 +1,52 @@ +import { AssetUtil, CoreHelperUtil, NetworkController } from '@web3modal/core' +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import { ifDefined } from 'lit/directives/if-defined.js' +import styles from './styles.js' + +@customElement('w3m-wallet-compatible-networks-view') +export class W3mWalletCompatibleNetworksView extends LitElement { + public static override styles = styles + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ${this.networkTemplate()} + ` + } + + // -- Private ------------------------------------------- // + networkTemplate() { + const { approvedCaipNetworkIds, requestedCaipNetworks } = NetworkController.state + + const sortedNetworks = CoreHelperUtil.sortRequestedNetworks( + approvedCaipNetworkIds, + requestedCaipNetworks + ) + + return sortedNetworks.map( + network => html` + + + ` + ) + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-wallet-compatible-networks-view': W3mWalletCompatibleNetworksView + } +} diff --git a/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/styles.ts new file mode 100644 index 0000000000..61afa36915 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/styles.ts @@ -0,0 +1,13 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex { + max-height: clamp(360px, 540px, 80vh); + overflow: scroll; + scrollbar-width: none; + } + + :host > wui-flex::-webkit-scrollbar { + display: none; + } +` diff --git a/packages/scaffold/src/views/w3m-wallet-receive-view/index.ts b/packages/scaffold/src/views/w3m-wallet-receive-view/index.ts new file mode 100644 index 0000000000..28ee333c76 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-receive-view/index.ts @@ -0,0 +1,133 @@ +import { + AccountController, + AssetUtil, + CoreHelperUtil, + NetworkController, + RouterController, + SnackController, + ThemeController +} from '@web3modal/core' +import { UiHelperUtil, customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import styles from './styles.js' +import { state } from 'lit/decorators.js' + +@customElement('w3m-wallet-receive-view') +export class W3mWalletReceiveView extends LitElement { + public static override styles = styles + + // -- Members ------------------------------------------- // + private unsubscribe: (() => void)[] = [] + + // -- State & Properties -------------------------------- // + @state() private address = AccountController.state.address + + @state() private profileName = AccountController.state.profileName + + @state() private network = NetworkController.state.caipNetwork + + public constructor() { + super() + this.unsubscribe.push( + ...[ + AccountController.subscribe(val => { + if (val.address) { + this.address = val.address + this.profileName = val.profileName + } else { + SnackController.showError('Account not found') + } + }) + ], + NetworkController.subscribeKey('caipNetwork', val => { + if (val?.id) { + this.network = val + } + }) + ) + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe()) + } + + // -- Render -------------------------------------------- // + public override render() { + if (!this.address) { + throw new Error('w3m-wallet-receive-view: No account provided') + } + + const networkImage = AssetUtil.getNetworkImage(this.network) + + return html` + + + + + Copy your address or scan this QR code + + + ${this.networkTemplate()} + ` + } + + // -- Private ------------------------------------------- // + networkTemplate() { + const networks = NetworkController.getRequestedCaipNetworks() + const slicedNetworks = networks?.filter(network => network?.imageId)?.slice(0, 5) + const imagesArray = slicedNetworks.map(AssetUtil.getNetworkImage).filter(Boolean) as string[] + + return html`` + } + + onReceiveClick() { + RouterController.push('WalletCompatibleNetworks') + } + + onCopyClick() { + try { + if (this.address) { + CoreHelperUtil.copyToClopboard(this.address) + SnackController.showSuccess('Address copied') + } + } catch { + SnackController.showError('Failed to copy') + } + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-wallet-receive-view': W3mWalletReceiveView + } +} diff --git a/packages/scaffold/src/views/w3m-wallet-receive-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-receive-view/styles.ts new file mode 100644 index 0000000000..3576735e3b --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-receive-view/styles.ts @@ -0,0 +1,7 @@ +import { css } from 'lit' + +export default css` + wui-compatible-network { + margin-top: var(--wui-spacing-l); + } +` diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 641cc83cab..e0bb70cd56 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -49,6 +49,13 @@ export * from './src/composites/wui-list-network/index.js' export * from './src/composites/wui-list-wallet-transaction/index.js' export * from './src/composites/wui-onramp-activity-item/index.js' export * from './src/composites/wui-onramp-provider-item/index.js' +export * from './src/composites/wui-promo/index.js' +export * from './src/composites/wui-tooltip-select/index.js' +export * from './src/composites/wui-balance/index.js' +export * from './src/composites/wui-profile-button/index.js' +export * from './src/composites/wui-chip-button/index.js' +export * from './src/composites/wui-compatible-network/index.js' +export * from './src/composites/wui-banner/index.js' export * from './src/layout/wui-flex/index.js' export * from './src/layout/wui-grid/index.js' diff --git a/packages/ui/src/assets/svg/cursor-transparent.ts b/packages/ui/src/assets/svg/cursor-transparent.ts new file mode 100644 index 0000000000..cdce92a152 --- /dev/null +++ b/packages/ui/src/assets/svg/cursor-transparent.ts @@ -0,0 +1,14 @@ +import { svg } from 'lit' + +export const cursorTransparentSvg = svg` + + + + ` diff --git a/packages/ui/src/components/wui-icon/index.ts b/packages/ui/src/components/wui-icon/index.ts index ce9d973f95..f168dc62db 100644 --- a/packages/ui/src/components/wui-icon/index.ts +++ b/packages/ui/src/components/wui-icon/index.ts @@ -69,6 +69,7 @@ import { recycleHorizontalSvg } from '../../assets/svg/recycle-horizontal.js' import { bankSvg } from '../../assets/svg/bank.js' import { cardSvg } from '../../assets/svg/card.js' import { plusSvg } from '../../assets/svg/plus.js' +import { cursorTransparentSvg } from '../../assets/svg/cursor-transparent.js' const svgOptions: Record> = { add: addSvg, @@ -95,6 +96,7 @@ const svgOptions: Record> = { coinPlaceholder: coinPlaceholderSvg, copy: copySvg, cursor: cursorSvg, + cursorTransparent: cursorTransparentSvg, desktop: desktopSvg, disconnect: disconnectSvg, discord: discordSvg, diff --git a/packages/ui/src/composites/wui-balance/index.ts b/packages/ui/src/composites/wui-balance/index.ts new file mode 100644 index 0000000000..28ed53604f --- /dev/null +++ b/packages/ui/src/composites/wui-balance/index.ts @@ -0,0 +1,26 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' +import { resetStyles } from '../../utils/ThemeUtil.js' + +@customElement('wui-balance') +export class WuiBalance extends LitElement { + public static override styles = [resetStyles, styles] + + // -- State & Properties -------------------------------- // + @property() dollars = '0' + + @property() pennies = '00' + + // -- Render -------------------------------------------- // + public override render() { + return html`$${this.dollars}.${this.pennies}` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-balance': WuiBalance + } +} diff --git a/packages/ui/src/composites/wui-balance/styles.ts b/packages/ui/src/composites/wui-balance/styles.ts new file mode 100644 index 0000000000..a6583e115c --- /dev/null +++ b/packages/ui/src/composites/wui-balance/styles.ts @@ -0,0 +1,16 @@ +import { css } from 'lit' + +export default css` + span { + font-weight: 500; + font-size: 40px; + color: var(--wui-color-fg-100); + line-height: 130%; /* 52px */ + letter-spacing: -1.6px; + text-align: center; + } + + .pennies { + color: var(--wui-color-fg-200); + } +` diff --git a/packages/ui/src/composites/wui-banner/index.ts b/packages/ui/src/composites/wui-banner/index.ts new file mode 100644 index 0000000000..73ae897746 --- /dev/null +++ b/packages/ui/src/composites/wui-banner/index.ts @@ -0,0 +1,41 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-text/index.js' +import '../wui-icon-box/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import type { IconType } from '../../utils/TypeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' + +@customElement('wui-banner') +export class WuiBanner extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + + @property() public icon: IconType = 'externalLink' + + @property() public text = '' + + // -- Render -------------------------------------------- // + public override render() { + return html` + + + ${this.text} + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-banner': WuiBanner + } +} diff --git a/packages/ui/src/composites/wui-banner/styles.ts b/packages/ui/src/composites/wui-banner/styles.ts new file mode 100644 index 0000000000..4e715490b4 --- /dev/null +++ b/packages/ui/src/composites/wui-banner/styles.ts @@ -0,0 +1,11 @@ +import { css } from 'lit' + +export default css` + wui-flex { + width: 100%; + background-color: var(--wui-gray-glass-005); + border-radius: var(--wui-border-radius-s); + padding: var(--wui-spacing-1xs) var(--wui-spacing-s) var(--wui-spacing-1xs) + var(--wui-spacing-1xs); + } +` diff --git a/packages/ui/src/composites/wui-chip-button/index.ts b/packages/ui/src/composites/wui-chip-button/index.ts new file mode 100644 index 0000000000..f13b223423 --- /dev/null +++ b/packages/ui/src/composites/wui-chip-button/index.ts @@ -0,0 +1,46 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-icon/index.js' +import '../../components/wui-image/index.js' +import '../../components/wui-text/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import type { ChipType, IconType } from '../../utils/TypeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' + +@customElement('wui-chip-button') +export class WuiChipButton extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public variant: ChipType = 'fill' + + @property() public imageSrc = '' + + @property({ type: Boolean }) public disabled = false + + @property() public icon: IconType = 'externalLink' + + @property() public text = '' + + // -- Render -------------------------------------------- // + public override render() { + const isSmall = + this.variant === 'success' || this.variant === 'transparent' || this.variant === 'shadeSmall' + const textVariant = isSmall ? 'small-600' : 'paragraph-600' + + return html` + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-chip-button': WuiChipButton + } +} diff --git a/packages/ui/src/composites/wui-chip-button/styles.ts b/packages/ui/src/composites/wui-chip-button/styles.ts new file mode 100644 index 0000000000..b58b8c6306 --- /dev/null +++ b/packages/ui/src/composites/wui-chip-button/styles.ts @@ -0,0 +1,184 @@ +import { css } from 'lit' + +export default css` + button { + border: 1px solid var(--wui-gray-glass-010); + border-radius: var(--wui-border-radius-3xl); + } + + wui-image { + border-radius: var(--wui-border-radius-3xl); + overflow: hidden; + } + + button.disabled > wui-icon, + button.disabled > wui-image { + filter: grayscale(1); + } + + button[data-variant='fill'] { + color: var(--wui-color-inverse-100); + background-color: var(--wui-color-accent-100); + } + + button[data-variant='shade'], + button[data-variant='shadeSmall'] { + background-color: transparent; + background-color: var(--wui-gray-glass-010); + color: var(--wui-color-fg-200); + } + + button[data-variant='success'] { + column-gap: var(--wui-spacing-xxs); + border: 1px solid var(--wui-success-glass-010); + background-color: var(--wui-success-glass-010); + color: var(--wui-color-success-100); + } + + button[data-variant='error'] { + column-gap: var(--wui-spacing-xxs); + border: 1px solid var(--wui-error-glass-010); + background-color: var(--wui-error-glass-010); + color: var(--wui-color-error-100); + } + + button[data-variant='transparent'] { + column-gap: var(--wui-spacing-xxs); + background-color: transparent; + color: var(--wui-color-fg-150); + } + + button[data-variant='transparent'], + button[data-variant='success'], + button[data-variant='shadeSmall'], + button[data-variant='error'] { + padding: 7px var(--wui-spacing-s) 7px 8px; + } + + button[data-variant='transparent']:has(wui-text:first-child), + button[data-variant='success']:has(wui-text:first-child), + button[data-variant='shadeSmall']:has(wui-text:first-child), + button[data-variant='error']:has(wui-text:first-child) { + padding: 7px var(--wui-spacing-s); + } + + button[data-variant='fill'], + button[data-variant='shade'] { + column-gap: var(--wui-spacing-xs); + padding: var(--wui-spacing-xxs) var(--wui-spacing-m) var(--wui-spacing-xxs) + var(--wui-spacing-xs); + } + + button[data-variant='fill']:has(wui-text:first-child), + button[data-variant='shade']:has(wui-text:first-child) { + padding: 9px var(--wui-spacing-m) 9px var(--wui-spacing-m); + } + + button[data-variant='fill'] > wui-image, + button[data-variant='shade'] > wui-image { + width: 24px; + height: 24px; + } + + button[data-variant='fill'] > wui-image { + box-shadow: inset 0 0 0 1px var(--wui-color-accent-090); + } + + button[data-variant='shade'] > wui-image, + button[data-variant='shadeSmall'] > wui-image { + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } + + button[data-variant='fill'] > wui-icon, + button[data-variant='shade'] > wui-icon { + width: 14px; + height: 14px; + } + + button[data-variant='transparent'] > wui-image, + button[data-variant='success'] > wui-image, + button[data-variant='shadeSmall'] > wui-image, + button[data-variant='error'] > wui-image { + width: 14px; + height: 14px; + } + + button[data-variant='transparent'] > wui-icon, + button[data-variant='success'] > wui-icon, + button[data-variant='shadeSmall'] > wui-icon, + button[data-variant='error'] > wui-icon { + width: 12px; + height: 12px; + } + + button[data-variant='fill']:focus-visible { + background-color: var(--wui-color-accent-090); + } + + button[data-variant='shade']:focus-visible, + button[data-variant='shadeSmall']:focus-visible { + background-color: var(--wui-gray-glass-015); + } + + button[data-variant='transparent']:focus-visible { + background-color: var(--wui-gray-glass-005); + } + + button[data-variant='success']:focus-visible { + background-color: var(--wui-success-glass-015); + } + + button[data-variant='error']:focus-visible { + background-color: var(--wui-error-glass-015); + } + + button.disabled { + color: var(--wui-gray-glass-015); + background-color: var(--wui-gray-glass-015); + pointer-events: none; + } + + @media (hover: hover) and (pointer: fine) { + button[data-variant='fill']:hover { + background-color: var(--wui-color-accent-090); + } + + button[data-variant='shade']:hover, + button[data-variant='shadeSmall']:hover { + background-color: var(--wui-gray-glass-015); + } + + button[data-variant='transparent']:hover { + background-color: var(--wui-gray-glass-005); + } + + button[data-variant='success']:hover { + background-color: var(--wui-success-glass-015); + } + + button[data-variant='error']:hover { + background-color: var(--wui-error-glass-015); + } + } + + button[data-variant='fill']:active { + background-color: var(--wui-color-accent-080); + } + + button[data-variant='shade']:active, + button[data-variant='shadeSmall']:active { + background-color: var(--wui-gray-glass-020); + } + + button[data-variant='transparent']:active { + background-color: var(--wui-gray-glass-010); + } + + button[data-variant='success']:active { + background-color: var(--wui-success-glass-020); + } + + button[data-variant='error']:active { + background-color: var(--wui-error-glass-020); + } +` diff --git a/packages/ui/src/composites/wui-compatible-network/index.ts b/packages/ui/src/composites/wui-compatible-network/index.ts new file mode 100644 index 0000000000..8d79aa526d --- /dev/null +++ b/packages/ui/src/composites/wui-compatible-network/index.ts @@ -0,0 +1,50 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-icon/index.js' +import '../../components/wui-text/index.js' +import '../../components/wui-image/index.js' +import '../../layout/wui-flex/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' + +@customElement('wui-compatible-network') +export class WuiCompatibleNetwork extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property({ type: Array }) networkImages: string[] = [''] + + @property() public text = '' + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ` + } + + // -- Private ------------------------------------------- // + private networksTemplate() { + const slicedNetworks = this.networkImages.slice(0, 5) + + return html` + ${slicedNetworks?.map( + network => + html` ` + )} + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-compatible-network': WuiCompatibleNetwork + } +} diff --git a/packages/ui/src/composites/wui-compatible-network/styles.ts b/packages/ui/src/composites/wui-compatible-network/styles.ts new file mode 100644 index 0000000000..a7921559b7 --- /dev/null +++ b/packages/ui/src/composites/wui-compatible-network/styles.ts @@ -0,0 +1,30 @@ +import { css } from 'lit' + +export default css` + button { + display: flex; + gap: var(--wui-spacing-xl); + width: 100%; + background-color: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-xxs); + padding: var(--wui-spacing-m) var(--wui-spacing-s); + } + + wui-text { + width: 100%; + } + + wui-flex { + width: auto; + } + + .network-icon { + width: var(--wui-spacing-2l); + height: var(--wui-spacing-2l); + border-radius: calc(var(--wui-spacing-2l) / 2); + overflow: hidden; + box-shadow: + 0 0 0 3px var(--wui-gray-glass-002), + 0 0 0 3px var(--wui-color-modal-bg); + } +` diff --git a/packages/ui/src/composites/wui-list-network/index.ts b/packages/ui/src/composites/wui-list-network/index.ts index e6c16af1d1..ad036b7f80 100644 --- a/packages/ui/src/composites/wui-list-network/index.ts +++ b/packages/ui/src/composites/wui-list-network/index.ts @@ -17,10 +17,12 @@ export class WuiListNetwork extends LitElement { @property({ type: Boolean }) public disabled = false + @property({ type: Boolean }) public transparent = false + // -- Render -------------------------------------------- // public override render() { return html` - diff --git a/packages/ui/src/composites/wui-list-network/styles.ts b/packages/ui/src/composites/wui-list-network/styles.ts index 675d84f6c8..32298534d7 100644 --- a/packages/ui/src/composites/wui-list-network/styles.ts +++ b/packages/ui/src/composites/wui-list-network/styles.ts @@ -15,6 +15,11 @@ export default css` flex: 1; } + button[data-transparent='true'] { + pointer-events: none; + background-color: transparent; + } + wui-icon { color: var(--wui-color-fg-200) !important; } diff --git a/packages/ui/src/composites/wui-profile-button/index.ts b/packages/ui/src/composites/wui-profile-button/index.ts new file mode 100644 index 0000000000..9e0a57b085 --- /dev/null +++ b/packages/ui/src/composites/wui-profile-button/index.ts @@ -0,0 +1,76 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-icon/index.js' +import '../../components/wui-text/index.js' +import '../../components/wui-image/index.js' +import '../../layout/wui-flex/index.js' +import '../wui-avatar/index.js' +import '../wui-icon-box/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' +import type { IconType } from '../../utils/TypeUtil.js' +import { UiHelperUtil } from '../../utils/UiHelperUtil.js' + +@customElement('wui-profile-button') +export class WuiProfileButton extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public networkSrc?: string = undefined + + @property() public avatarSrc?: string = undefined + + @property({ type: Boolean }) public isProfileName = false + + @property() public address = '' + + @property() public icon: IconType = 'chevronBottom' + + // -- Render -------------------------------------------- // + public override render() { + return html` ` + } + + // -- Private ------------------------------------------- // + private networkImageTemplate() { + if (this.networkSrc) { + return html`` + } + + return html` + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-profile-button': WuiProfileButton + } +} diff --git a/packages/ui/src/composites/wui-profile-button/styles.ts b/packages/ui/src/composites/wui-profile-button/styles.ts new file mode 100644 index 0000000000..faa2292e9f --- /dev/null +++ b/packages/ui/src/composites/wui-profile-button/styles.ts @@ -0,0 +1,37 @@ +import { css } from 'lit' + +export default css` + button { + background-color: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-3xl); + border: 1px solid var(--wui-gray-glass-002); + padding: var(--wui-spacing-xs) var(--wui-spacing-s) var(--wui-spacing-xs) var(--wui-spacing-xs); + position: relative; + } + + wui-avatar { + width: 32px; + height: 32px; + box-shadow: 0 0 0 0; + outline: 3px solid var(--wui-gray-glass-005); + } + + wui-icon-box, + wui-image { + width: 16px; + height: 16px; + border-radius: var(--wui-border-radius-3xl); + position: absolute; + left: 26px; + top: 24px; + } + + wui-image { + outline: 2px solid var(--wui-color-bg-125); + } + + wui-icon-box { + outline: 2px solid var(--wui-color-bg-200); + background-color: var(--wui-color-bg-250); + } +` diff --git a/packages/ui/src/composites/wui-promo/index.ts b/packages/ui/src/composites/wui-promo/index.ts new file mode 100644 index 0000000000..546a6f1130 --- /dev/null +++ b/packages/ui/src/composites/wui-promo/index.ts @@ -0,0 +1,29 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' +import '../../components/wui-text/index.js' +import '../../components/wui-icon/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' + +@customElement('wui-promo') +export class WuiPromo extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() text = '' + + // -- Render -------------------------------------------- // + public override render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-promo': WuiPromo + } +} diff --git a/packages/ui/src/composites/wui-promo/styles.ts b/packages/ui/src/composites/wui-promo/styles.ts new file mode 100644 index 0000000000..67af6e5268 --- /dev/null +++ b/packages/ui/src/composites/wui-promo/styles.ts @@ -0,0 +1,24 @@ +import { css } from 'lit' + +export default css` + button { + display: flex; + gap: var(--wui-spacing-3xs); + align-items: center; + padding: 6.25px var(--wui-spacing-xs) 7.25px var(--wui-spacing-s); + background-color: var(--wui-gray-glass-090); + border-radius: var(--wui-border-radius-3xl); + border: 1px solid var(--wui-gray-glass-060); + } + + @media (hover: hover) and (pointer: fine) { + button:hover:enabled { + background-color: var(--wui-gray-glass-080); + } + + button:active:enabled { + transition: all var(--wui-ease-out-power-2) var(--wui-duration-sm); + background-color: var(--wui-gray-glass-060); + } + } +` diff --git a/packages/ui/src/composites/wui-qr-code/index.ts b/packages/ui/src/composites/wui-qr-code/index.ts index 773427b4f5..6856bc4106 100644 --- a/packages/ui/src/composites/wui-qr-code/index.ts +++ b/packages/ui/src/composites/wui-qr-code/index.ts @@ -23,9 +23,12 @@ export class WuiQrCode extends LitElement { @property() public alt?: string = undefined + @property({ type: Boolean }) public arenaClear?: boolean = undefined + // -- Render -------------------------------------------- // public override render() { this.dataset['theme'] = this.theme + this.dataset['clear'] = String(this.arenaClear) this.style.cssText = `--local-size: ${this.size}px` return html`${this.templateVisual()} ${this.templateSvg()}` @@ -37,7 +40,7 @@ export class WuiQrCode extends LitElement { return svg` - ${QrCodeUtil.generate(this.uri, size, size / 4)} + ${QrCodeUtil.generate(this.uri, size, this.arenaClear ? 0 : size / 4)} ` } diff --git a/packages/ui/src/composites/wui-qr-code/styles.ts b/packages/ui/src/composites/wui-qr-code/styles.ts index 360a543b46..92fdd86725 100644 --- a/packages/ui/src/composites/wui-qr-code/styles.ts +++ b/packages/ui/src/composites/wui-qr-code/styles.ts @@ -21,6 +21,10 @@ export default css` background-color: var(--wui-color-bg-125); } + :host([data-clear='true']) > wui-icon { + display: none; + } + svg:first-child, wui-image, wui-icon { diff --git a/packages/ui/src/composites/wui-tabs/index.ts b/packages/ui/src/composites/wui-tabs/index.ts index 0ea57a7572..bd67ff7f32 100644 --- a/packages/ui/src/composites/wui-tabs/index.ts +++ b/packages/ui/src/composites/wui-tabs/index.ts @@ -10,7 +10,7 @@ export class WuiTabs extends LitElement { public static override styles = [resetStyles, elementStyles, styles] // -- State & Properties -------------------------------- // - @property({ type: Array }) public tabs: { icon: IconType; label: string }[] = [] + @property({ type: Array }) public tabs: { icon?: IconType; label: string }[] = [] @property() public onTabChange: (index: number) => void = () => null diff --git a/packages/ui/src/composites/wui-tooltip-select/index.ts b/packages/ui/src/composites/wui-tooltip-select/index.ts new file mode 100644 index 0000000000..46a43e3bbd --- /dev/null +++ b/packages/ui/src/composites/wui-tooltip-select/index.ts @@ -0,0 +1,59 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' +import '../wui-tooltip/index.js' +import '../../components/wui-icon/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import type { IconType } from '../../utils/TypeUtil.js' + +@customElement('wui-tooltip-select') +export class WuiTooltipSelect extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() text = '' + + @property() icon: IconType = 'card' + + // -- Render -------------------------------------------- // + public override render() { + return html` + ` + } + + // -- Private ------------------------------------------- // + private onMouseEnter() { + const element = this.shadowRoot?.querySelector('wui-tooltip') + if (element) { + element?.animate([{ opacity: 0 }, { opacity: 1 }], { + fill: 'forwards', + easing: 'ease', + duration: 250 + }) + } + } + + private onMouseLeave() { + const element = this.shadowRoot?.querySelector('wui-tooltip') + if (element) { + element?.animate([{ opacity: 1 }, { opacity: 0 }], { + fill: 'forwards', + easing: 'ease', + duration: 200 + }) + } + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-tooltip-select': WuiTooltipSelect + } +} diff --git a/packages/ui/src/composites/wui-tooltip-select/styles.ts b/packages/ui/src/composites/wui-tooltip-select/styles.ts new file mode 100644 index 0000000000..f1a06c0b6e --- /dev/null +++ b/packages/ui/src/composites/wui-tooltip-select/styles.ts @@ -0,0 +1,37 @@ +import { css } from 'lit' + +export default css` + :host { + position: relative; + } + button { + display: flex; + justify-content: center; + align-items: center; + height: 48px; + width: 100%; + background-color: var(--wui-accent-glass-010); + border-radius: var(--wui-border-radius-xs); + border: 1px solid var(--wui-accent-glass-010); + } + + wui-tooltip { + padding: 7px var(--wui-spacing-s) 8px var(--wui-spacing-s); + position: absolute; + top: -8px; + left: 50%; + transform: translate(-50%, -100%); + opacity: 0; + } + + @media (hover: hover) and (pointer: fine) { + button:hover:enabled { + background-color: var(--wui-accent-glass-015); + } + + button:active:enabled { + background-color: var(--wui-accent-glass-020); + transition: all var(--wui-ease-out-power-2) var(--wui-duration-sm); + } + } +` diff --git a/packages/ui/src/composites/wui-tooltip/index.ts b/packages/ui/src/composites/wui-tooltip/index.ts index 96db98f059..5ea3be0c38 100644 --- a/packages/ui/src/composites/wui-tooltip/index.ts +++ b/packages/ui/src/composites/wui-tooltip/index.ts @@ -14,15 +14,19 @@ export class WuiTooltip extends LitElement { // -- State & Properties -------------------------------- // @property() public placement: PlacementType = 'top' + @property() public variant: 'shade' | 'fill' = 'fill' + @property() public message = '' // -- Render -------------------------------------------- // public override render() { + this.dataset['variant'] = this.variant + return html` ${this.message}` } diff --git a/packages/ui/src/composites/wui-tooltip/styles.ts b/packages/ui/src/composites/wui-tooltip/styles.ts index bed3a8cb08..64e63c59b7 100644 --- a/packages/ui/src/composites/wui-tooltip/styles.ts +++ b/packages/ui/src/composites/wui-tooltip/styles.ts @@ -5,11 +5,25 @@ export default css` display: block; padding: 9px var(--wui-spacing-s) 10px var(--wui-spacing-s); border-radius: var(--wui-border-radius-xxs); - background-color: var(--wui-color-fg-100); + color: var(--wui-color-bg-100); position: relative; } + :host([data-variant='shade']) { + background-color: var(--wui-color-bg-150); + border: 1px solid var(--wui-gray-glass-005); + } + + :host([data-variant='shade']) > wui-text { + color: var(--wui-color-fg-150); + } + + :host([data-variant='fill']) { + background-color: var(--wui-color-fg-100); + border: none; + } + wui-icon { position: absolute; width: 12px !important; @@ -17,7 +31,7 @@ export default css` } wui-icon[data-placement='top'] { - bottom: 0; + bottom: 0px; left: 50%; transform: translate(-50%, 95%); } diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index ff0b90329e..81b9a3e2a6 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -49,6 +49,12 @@ import type { WuiListNetwork } from '../composites/wui-list-network/index.js' import type { WuiListWalletTransaction } from '../composites/wui-list-wallet-transaction/index.js' import type { WuiOnRampActivityItem } from '../composites/wui-onramp-activity-item/index.js' import type { WuiOnRampProviderItem } from '../composites/wui-onramp-provider-item/index.js' +import type { WuiPromo } from '../composites/wui-promo/index.js' +import type { WuiBalance } from '../composites/wui-balance/index.js' +import type { WuiTooltipSelect } from '../composites/wui-tooltip-select/index.js' +import type { WuiProfileButton } from '../composites/wui-profile-button/index.js' +import type { WuiBanner } from '../composites/wui-banner/index.js' +import type { WuiCompatibleNetwork } from '../composites/wui-compatible-network/index.js' import type { WuiFlex } from '../layout/wui-flex/index.js' import type { WuiGrid } from '../layout/wui-grid/index.js' @@ -113,6 +119,12 @@ declare global { 'wui-list-wallet-transaction': CustomElement 'wui-onramp-activity-item': CustomElement 'wui-onramp-provider-item': CustomElement + 'wui-promo': CustomElement + 'wui-tooltip-select': CustomElement + 'wui-balance': CustomElement + 'wui-profile-button': CustomElement + 'wui-banner': CustomElement + 'wui-compatible-network': CustomElement } } } diff --git a/packages/ui/src/utils/ThemeUtil.ts b/packages/ui/src/utils/ThemeUtil.ts index a9c401cb5f..2c17959948 100644 --- a/packages/ui/src/utils/ThemeUtil.ts +++ b/packages/ui/src/utils/ThemeUtil.ts @@ -172,6 +172,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-height-network-md: 54px; --wui-height-network-lg: 96px; + --wui-icon-size-network-xs: 12px; --wui-icon-size-network-sm: 16px; --wui-icon-size-network-md: 24px; --wui-icon-size-network-lg: 42px; @@ -536,6 +537,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-gray-glass-030: rgba(255, 255, 255, 0.3); --wui-gray-glass-060: rgba(255, 255, 255, 0.6); --wui-gray-glass-080: rgba(255, 255, 255, 0.8); + --wui-gray-glass-090: rgba(255, 255, 255, 0.9); } `, dark: css` @@ -626,6 +628,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-gray-glass-030: rgba(0, 0, 0, 0.3); --wui-gray-glass-060: rgba(0, 0, 0, 0.6); --wui-gray-glass-080: rgba(0, 0, 0, 0.8); + --wui-gray-glass-090: rgba(0, 0, 0, 0.9); } ` } diff --git a/packages/ui/src/utils/TypeUtil.ts b/packages/ui/src/utils/TypeUtil.ts index 45fee09ca0..ca13291d20 100644 --- a/packages/ui/src/utils/TypeUtil.ts +++ b/packages/ui/src/utils/TypeUtil.ts @@ -114,6 +114,7 @@ export type IconType = | 'compass' | 'copy' | 'cursor' + | 'cursorTransparent' | 'desktop' | 'disconnect' | 'discord' diff --git a/packages/ui/src/utils/UiHelperUtil.ts b/packages/ui/src/utils/UiHelperUtil.ts index e13012d743..3a90e53c10 100644 --- a/packages/ui/src/utils/UiHelperUtil.ts +++ b/packages/ui/src/utils/UiHelperUtil.ts @@ -106,5 +106,13 @@ export const UiHelperUtil = { } return 'dark' + }, + splitBalance(input: string): [string, string] { + const parts = input.split('.') as [string, string] + if (parts.length === 2) { + return [parts[0], parts[1]] + } + + return ['0', '00'] } }