Skip to content

Commit 5076ff6

Browse files
authored
[core, react, vue]: Fix - Account Center container el (#1323)
* Working as expected with styles * cleanup and props for AC localized to AC index * mark accountCenter.containerElement as deprecated * Add documentation around container element usage * Final cleanup for now * Update demo * yarn it * Remove unused link
1 parent cb8529e commit 5076ff6

File tree

14 files changed

+155
-53
lines changed

14 files changed

+155
-53
lines changed

packages/core/README.md

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ type AppMetadata = {
7878
explore?: string
7979
// if your app only supports injected wallets and when no injected wallets detected, recommend the user to install some
8080
recommendedInjectedWallets?: RecommendedInjectedWallets[]
81+
/** Gas module */
82+
gas?: typeof gas
83+
/**
84+
* Object mapping for W3O components with the key being the component and the value the DOM element to mount
85+
* the component to. This element must be available at time of package script execution.
86+
*/
87+
containerElements?: Partial<ContainerElements>
8188
}
8289

8390
type RecommendedInjectedWallets = {
@@ -106,21 +113,38 @@ type i18nOptions = Record<Locale, i18n>
106113
To see a list of all of the text values that can be internationalized or replaced, check out the [default en file](src/i18n/en.json).
107114
Onboard is using the [ICU syntax](https://formatjs.io/docs/core-concepts/icu-syntax/) for formatting under the hood.
108115
116+
**`containerElements`**
117+
An object mapping for W3O components with the key being the DOM element to mount the specified component to.
118+
This defines the DOM container element for svelte to attach the component.
119+
120+
**NOTE**: containerElement must be a DOM element with a styleSheet property attached and the element must be available on the DOM at the time of component mounting.
121+
For an example please see containerElement usage [here](https://github.com/blocknative/web3-onboard/blob/8531a73d69365f7d584320f1c4b97a5d90f1c34e/packages/demo/src/App.svelte#L227)
122+
123+
```typescript
124+
type ContainerElements = {
125+
// when using the accountCenter with a container el the accountCenter position properties are ignored
126+
accountCenter?: string
127+
}
128+
```
129+
109130
**`accountCenter`**
110131
An object that defines whether the account center UI (default and minimal) is enabled and it's position on the screen. Currently the account center is enabled for both desktop and mobile devices.
111132
112133
```typescript
113-
export type AccountCenter = {
134+
type AccountCenter = {
114135
enabled: boolean
115136
position?: AccountCenterPosition // default: 'topRight'
116137
expanded?: boolean // default: true
117138
minimal?: boolean // enabled by default for mobile
118-
containerElement?: string // defines the DOM container element for svelte to attach
119-
// **NOTE: containerElement must be a DOM element with a styleSheet property attached.
120-
// This property can normally be omitted from the config and allowed to default to document.body
139+
140+
/**
141+
* @deprecated Use top level containerElements property
142+
* with the accountCenter prop set to the desired container El
143+
*/
144+
containerElement?: string // defines the DOM container element for svelte to attach
121145
}
122146

123-
export type AccountCenterOptions = {
147+
type AccountCenterOptions = {
124148
desktop: Omit<AccountCenter, 'expanded'>
125149
mobile: Omit<AccountCenter, 'expanded'>
126150
}
@@ -170,11 +194,11 @@ unsubscribe()
170194
```
171195

172196
```typescript
173-
export type NotifyOptions = {
197+
type NotifyOptions = {
174198
desktop: Notify
175199
mobile: Notify
176200
}
177-
export type Notify = {
201+
type Notify = {
178202
enabled: boolean // default: true
179203
/**
180204
* Callback that receives all transaction events
@@ -196,17 +220,13 @@ export type Notify = {
196220
}
197221
}
198222

199-
export type CommonPositions =
200-
| 'topRight'
201-
| 'bottomRight'
202-
| 'bottomLeft'
203-
| 'topLeft'
223+
type CommonPositions = 'topRight' | 'bottomRight' | 'bottomLeft' | 'topLeft'
204224

205-
export type TransactionHandlerReturn = CustomNotification | boolean | void
225+
type TransactionHandlerReturn = CustomNotification | boolean | void
206226

207-
export type CustomNotification = Partial<Omit<Notification, 'id' | 'startTime'>>
227+
type CustomNotification = Partial<Omit<Notification, 'id' | 'startTime'>>
208228

209-
export type Notification = {
229+
type Notification = {
210230
id: string
211231
key: string
212232
type: NotificationType
@@ -219,7 +239,7 @@ export type Notification = {
219239
onClick?: (event: Event) => void
220240
}
221241

222-
export type NotificationType = 'pending' | 'success' | 'error' | 'hint'
242+
type NotificationType = 'pending' | 'success' | 'error' | 'hint'
223243

224244
export declare type Network =
225245
| 'main'
@@ -588,7 +608,9 @@ const onboard = Onboard({
588608
token: 'ETH',
589609
label: 'Ethereum Mainnet',
590610
// Only one RPC required
591-
rpcUrl: `https://mainnet.infura.io/v3/${INFURA_KEY}` || `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`
611+
rpcUrl:
612+
`https://mainnet.infura.io/v3/${INFURA_KEY}` ||
613+
`https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`
592614
}
593615
]
594616
})

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@web3-onboard/core",
3-
"version": "2.9.1-alpha.3",
3+
"version": "2.10.0-alpha.1",
44
"description": "Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.",
55
"keywords": [
66
"Ethereum",

packages/core/src/configuration.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export let configuration: Configuration = {
77
apiKey: null,
88
device: getDevice(),
99
initialWalletInit: [],
10-
gas: null
10+
gas: null,
11+
containerElements: { accountCenter: null }
1112
}
1213

1314
export function updateConfiguration(update: Partial<Configuration>): void {

packages/core/src/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export const APP_INITIAL_STATE: AppState = {
99
enabled: true,
1010
position: 'topRight',
1111
expanded: false,
12-
containerElement: 'body',
1312
minimal: configuration.device.type === 'mobile'
1413
},
1514
notify: {

packages/core/src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,12 @@ function init(options: InitOptions): OnboardAPI {
8989
apiKey,
9090
notify,
9191
gas,
92-
connect
92+
connect,
93+
containerElements
9394
} = options
9495

96+
updateConfiguration({ containerElements })
97+
9598
const { device, svelteInstance } = configuration
9699

97100
if (svelteInstance) {
@@ -122,7 +125,6 @@ function init(options: InitOptions): OnboardAPI {
122125
...accountCenter.desktop
123126
}
124127
}
125-
126128
updateAccountCenter(accountCenterUpdate)
127129
}
128130

packages/core/src/types.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,13 @@ export interface InitOptions {
5050
* Transaction notification options
5151
*/
5252
notify?: Partial<NotifyOptions> | Partial<Notify>
53-
/**Gas module */
53+
/** Gas module */
5454
gas?: typeof gas
55+
/**
56+
* Object mapping for W3O components with the key being the DOM element to mount
57+
* the component to, this defines the DOM container element for svelte to attach the component
58+
*/
59+
containerElements?: Partial<ContainerElements>
5560
}
5661

5762
export interface ConnectOptions {
@@ -131,6 +136,7 @@ export type Configuration = {
131136
appMetadata?: AppMetadata | null
132137
apiKey?: string
133138
gas?: typeof gas
139+
containerElements?: ContainerElements
134140
}
135141

136142
export type Locale = string
@@ -156,6 +162,10 @@ export type AccountCenter = {
156162
position?: AccountCenterPosition
157163
expanded?: boolean
158164
minimal?: boolean
165+
/**
166+
* @deprecated Use top level containerElements property
167+
* with the accountCenter prop set to the desired container El
168+
*/
159169
containerElement?: string
160170
}
161171

@@ -164,6 +174,10 @@ export type AccountCenterOptions = {
164174
mobile: Omit<AccountCenter, 'expanded'>
165175
}
166176

177+
export type ContainerElements = {
178+
accountCenter?: string
179+
}
180+
167181
export type Notify = {
168182
/**
169183
* Defines whether whether to subscribe to transaction events or not

packages/core/src/validation.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ const connectModalOptions = Joi.object({
165165
showSidebar: Joi.boolean()
166166
})
167167

168+
const containerElements = Joi.object({
169+
accountCenter: Joi.string()
170+
})
171+
168172
const initOptions = Joi.object({
169173
wallets: walletInit,
170174
chains: chains.required(),
@@ -180,7 +184,8 @@ const initOptions = Joi.object({
180184
get: Joi.function().required(),
181185
stream: Joi.function().required()
182186
}),
183-
connect: connectModalOptions
187+
connect: connectModalOptions,
188+
containerElements: containerElements
184189
})
185190

186191
const connectOptions = Joi.object({

packages/core/src/views/Index.svelte

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import type { Observable } from 'rxjs'
1010
import type { Notification } from '../types.js'
1111
12-
const { device } = configuration
12+
const { device, containerElements } = configuration
1313
const accountCenter$ = state
1414
.select('accountCenter')
1515
.pipe(startWith(state.get().accountCenter), shareReplay(1))
@@ -50,6 +50,7 @@
5050
: Promise.resolve(null)
5151
5252
$: sharedContainer =
53+
!accountCenterMountToElement &&
5354
$accountCenter$.enabled &&
5455
$notify$.enabled &&
5556
$notify$.position === $accountCenter$.position
@@ -58,40 +59,80 @@
5859
device.type === 'mobile' || $accountCenter$.position === $notify$.position
5960
6061
$: sharedMobileContainerCheck =
61-
device.type === 'mobile' &&
62-
(($notify$.position.includes('bottom') &&
62+
($notify$.position.includes('bottom') &&
6363
$accountCenter$.position.includes('bottom')) ||
64-
($notify$.position.includes('top') &&
65-
$accountCenter$.position.includes('top')))
66-
67-
$: separateMobileContainerCheck =
68-
device.type === 'mobile' &&
69-
(($notify$.position.includes('top') &&
70-
$accountCenter$.position.includes('bottom')) ||
71-
($notify$.position.includes('bottom') &&
72-
$accountCenter$.position.includes('top')))
64+
($notify$.position.includes('top') &&
65+
$accountCenter$.position.includes('top'))
7366
7467
$: displayNotifySeparate =
7568
$notify$.enabled &&
7669
(!$accountCenter$.enabled ||
70+
accountCenterMountToElement ||
7771
($notify$.position !== $accountCenter$.position &&
7872
device.type !== 'mobile') ||
79-
separateMobileContainerCheck ||
73+
(device.type === 'mobile' && !sharedMobileContainerCheck) ||
8074
!$wallets$.length)
8175
8276
$: displayAccountCenterSeparate =
8377
$accountCenter$.enabled &&
8478
(!$notify$.enabled ||
8579
($notify$.position !== $accountCenter$.position &&
8680
device.type !== 'mobile') ||
87-
separateMobileContainerCheck) &&
81+
(device.type === 'mobile' && !sharedMobileContainerCheck)) &&
8882
$wallets$.length
8983
9084
$: displayAccountCenterNotifySameContainer =
9185
$notify$.enabled &&
9286
$accountCenter$.enabled &&
93-
$wallets$.length &&
94-
(sharedContainer || sharedMobileContainerCheck)
87+
(sharedContainer ||
88+
(device.type === 'mobile' && sharedMobileContainerCheck)) &&
89+
$wallets$.length
90+
91+
const accountCenterMountToElement =
92+
$accountCenter$.enabled &&
93+
containerElements &&
94+
containerElements.accountCenter
95+
96+
if (accountCenterMountToElement) {
97+
const accountCenter = document.createElement('onboard-account-center')
98+
const target = accountCenter.attachShadow({ mode: 'open' })
99+
100+
let getW3OEl = document.querySelector('onboard-v2')
101+
let w3OStyleSheets = getW3OEl.shadowRoot.styleSheets
102+
const accountCenterStyleSheet = new CSSStyleSheet()
103+
104+
// Copy Onboard stylesheets over to AccountCenter shadow DOM
105+
Object.values(w3OStyleSheets).forEach(sheet => {
106+
const styleRules = Object.values(sheet.cssRules)
107+
styleRules.forEach(rule =>
108+
accountCenterStyleSheet.insertRule(rule.cssText)
109+
)
110+
})
111+
target.adoptedStyleSheets = [accountCenterStyleSheet]
112+
113+
const containerElement = document.querySelector(accountCenterMountToElement)
114+
115+
containerElement.appendChild(accountCenter)
116+
if (!containerElement) {
117+
throw new Error(
118+
`Element with query ${accountCenterMountToElement} does not exist.`
119+
)
120+
}
121+
122+
const getACComp = async () => {
123+
let acComponent = await accountCenterComponent
124+
if (acComponent) {
125+
new acComponent({
126+
target,
127+
props: {
128+
settings: $accountCenter$,
129+
mountInContainer: true
130+
}
131+
})
132+
}
133+
}
134+
getACComp()
135+
}
95136
</script>
96137

97138
<style>
@@ -389,7 +430,7 @@
389430
>
390431
{#await accountCenterComponent then AccountCenter}
391432
{#if AccountCenter}
392-
<svelte:component this={AccountCenter} settings={$accountCenter$} />
433+
<svelte:component this={AccountCenter} />
393434
{/if}
394435
{/await}
395436
</div>
@@ -432,7 +473,7 @@
432473
{#if $accountCenter$.enabled && $wallets$.length}
433474
{#await accountCenterComponent then AccountCenter}
434475
{#if AccountCenter}
435-
<svelte:component this={AccountCenter} settings={$accountCenter$} />
476+
<svelte:component this={AccountCenter} />
436477
{/if}
437478
{/await}
438479
{/if}

0 commit comments

Comments
 (0)