Skip to content

Commit 38e0eab

Browse files
authored
feat(redesign): complete widget tool functionality (#202)
1 parent 919c3b4 commit 38e0eab

File tree

13 files changed

+321
-212
lines changed

13 files changed

+321
-212
lines changed

components/src/banner.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@
99
width: 100%;
1010
max-width: 100%;
1111
box-sizing: border-box;
12+
position: absolute;
13+
left: 0;
14+
right: 0;
15+
z-index: 10;
16+
}
17+
18+
:host(.position-top) {
19+
top: 0;
20+
}
21+
22+
:host(.position-bottom) {
23+
bottom: 0;
1224
}
1325

1426
.banner {

components/src/banner.ts

Lines changed: 25 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,16 @@ import {
88
import { property, state } from 'lit/decorators.js'
99
import defaultLogo from './assets/wm_logo_animated.svg?url'
1010
import bannerStyles from './banner.css?raw'
11-
import { getWebMonetizationLinkHref } from './utils.js'
11+
import { getWebMonetizationLinkHref, applyFontFamily } from './utils.js'
12+
import { BORDER_RADIUS } from '@shared/types'
13+
import type { FontFamilyKey, BorderRadiusKey } from '@shared/types'
1214

1315
const DEFAULT_BANNER_TITLE = 'How to support?'
1416
const DEFAULT_BANNER_DESCRIPTION =
1517
'You can support this page and my work by a one time donation or proportional to the time you spend on this website through web monetization.'
1618
const DEFAULT_BANNER_LINK_TEXT =
1719
'Install the Web Monetization browser extension'
1820

19-
type BorderRadiusKey = 'Light' | 'Pill' | 'None'
20-
const BORDER_RADIUS_VALUES: Record<BorderRadiusKey, string> = {
21-
Light: '0.375rem',
22-
Pill: '1rem',
23-
None: '0'
24-
}
25-
26-
type FontFamilyKey =
27-
| 'Cookie'
28-
| 'Roboto'
29-
| 'Open Sans'
30-
| 'Titillium Web'
31-
| 'Arial'
32-
| 'Inherit'
33-
const FONT_FAMILY_URLS: Record<
34-
Exclude<FontFamilyKey, 'Arial' | 'Inherit'>,
35-
string
36-
> = {
37-
'Open Sans':
38-
'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap',
39-
'Cookie': 'https://fonts.googleapis.com/css2?family=Cookie&display=swap',
40-
'Roboto':
41-
'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap',
42-
'Titillium Web':
43-
'https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700&display=swap'
44-
}
45-
4621
export interface BannerConfig {
4722
bannerTitleText?: string
4823
bannerDescriptionText?: string
@@ -206,6 +181,10 @@ export class BannerController implements ReactiveController {
206181
this.applyBorderRadius(updates.bannerBorderRadius)
207182
}
208183

184+
if (updates.bannerPosition) {
185+
this.applyPosition()
186+
}
187+
209188
this.host.requestUpdate()
210189
}
211190

@@ -220,51 +199,34 @@ export class BannerController implements ReactiveController {
220199
* @param borderRadius The border radius value to apply.
221200
*/
222201
private applyBorderRadius(borderRadius: BorderRadiusKey) {
223-
const borderRadiusValue = BORDER_RADIUS_VALUES[borderRadius]
202+
const borderRadiusValue = BORDER_RADIUS[borderRadius]
224203
this.host.style.setProperty(
225204
'--wm-border-radius',
226-
borderRadiusValue || BORDER_RADIUS_VALUES.None
205+
borderRadiusValue || BORDER_RADIUS.None
227206
)
228207
}
229208

230209
/**
231-
* Applies the specified font family to the host element, removing any existing font link,
232-
* loading the font if necessary, and setting the CSS custom property.
233-
*
234-
* @param fontName The name of the font family to apply.
210+
* Applies the specified position to the host element.
235211
*/
236-
private applyFontFamily(fontName: FontFamilyKey) {
237-
const existingFont = document.getElementById(
238-
'wmt-font-family-banner'
239-
) as HTMLLinkElement
240-
if (existingFont) {
241-
existingFont.remove()
242-
}
243-
244-
if (fontName === 'Inherit') {
245-
this.host.style.setProperty('--wm-font-family', 'inherit')
246-
return
247-
}
248-
249-
if (fontName === 'Arial') {
250-
this.host.style.setProperty('--wm-font-family', fontName)
251-
return
252-
}
212+
applyPosition() {
213+
this.host.classList.remove('position-top', 'position-bottom')
253214

254-
const fontFamilyUrl = FONT_FAMILY_URLS[fontName]
255-
if (!fontFamilyUrl) {
256-
this.host.style.setProperty('--wm-font-family', 'inherit')
257-
return
215+
const position = this._config.bannerPosition || 'Bottom'
216+
if (position === 'Top') {
217+
this.host.classList.add('position-top')
218+
} else {
219+
this.host.classList.add('position-bottom')
258220
}
221+
}
259222

260-
const fontLink = document.createElement('link') as HTMLLinkElement
261-
fontLink.id = 'wmt-font-family-banner'
262-
fontLink.rel = 'stylesheet'
263-
fontLink.type = 'text/css'
264-
fontLink.href = fontFamilyUrl
265-
document.head.appendChild(fontLink)
266-
267-
this.host.style.setProperty('--wm-font-family', fontName)
223+
/**
224+
* Applies the specified font family to the host element.
225+
*
226+
* @param fontName The name of the font family to apply.
227+
*/
228+
private applyFontFamily(fontName: FontFamilyKey) {
229+
applyFontFamily(this.host, fontName, 'banner')
268230
}
269231
applyTheme(element: HTMLElement) {
270232
const theme = this.config.theme

components/src/utils.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,50 @@
1+
import { FONT_FAMILY_URLS } from '@shared/types'
2+
import type { FontFamilyKey } from '@shared/types'
3+
4+
/**
5+
* Applies the specified font family to an element, removing any existing font link, loading the font if necessary
6+
* @param element - The HTML element to apply the font to
7+
* @param fontName - The name of the font family to apply
8+
* @param componentType - The type of component (for unique font link IDs)
9+
*/
10+
export const applyFontFamily = (
11+
element: HTMLElement,
12+
fontName: FontFamilyKey,
13+
componentType: 'banner' | 'widget' = 'banner'
14+
): void => {
15+
const fontLinkId = `wmt-font-family-${componentType}`
16+
const existingFont = document.getElementById(fontLinkId) as HTMLLinkElement
17+
18+
if (existingFont) {
19+
existingFont.remove()
20+
}
21+
22+
if (fontName === 'Inherit') {
23+
element.style.setProperty('--wm-font-family', 'inherit')
24+
return
25+
}
26+
27+
if (fontName === 'Arial') {
28+
element.style.setProperty('--wm-font-family', fontName)
29+
return
30+
}
31+
32+
const fontFamilyUrl = FONT_FAMILY_URLS[fontName]
33+
if (!fontFamilyUrl) {
34+
element.style.setProperty('--wm-font-family', 'inherit')
35+
return
36+
}
37+
38+
const fontLink = document.createElement('link') as HTMLLinkElement
39+
fontLink.id = fontLinkId
40+
fontLink.rel = 'stylesheet'
41+
fontLink.type = 'text/css'
42+
fontLink.href = fontFamilyUrl
43+
document.head.appendChild(fontLink)
44+
45+
element.style.setProperty('--wm-font-family', fontName)
46+
}
47+
148
/**
249
* Gets the appropriate Web Monetization extension download link based on the user agent
350
* @param userAgent - The user agent string from navigator.userAgent

components/src/widget/controller.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ import type {
44
PendingGrant,
55
WalletAddress
66
} from '@interledger/open-payments'
7+
import { applyFontFamily } from '../utils.js'
8+
import { WIDGET_POSITION, BORDER_RADIUS } from '@shared/types'
79
import type { ReactiveController, ReactiveControllerHost } from 'lit'
810
import type { WidgetConfig, FormatAmountArgs, FormattedAmount } from './types'
11+
import type { FontFamilyKey, BorderRadiusKey } from '@shared/types'
912

1013
export interface WidgetState {
1114
walletAddress: WalletAddress
@@ -46,6 +49,7 @@ export class WidgetController implements ReactiveController {
4649
this._config = { ...this._config, ...updates }
4750

4851
this.applyTheme(this.host)
52+
this.applyPosition()
4953
this.host.requestUpdate()
5054
}
5155

@@ -100,6 +104,29 @@ export class WidgetController implements ReactiveController {
100104
}
101105
}
102106

107+
private applyBorderRadius(borderRadius: BorderRadiusKey) {
108+
const borderRadiusValue = BORDER_RADIUS[borderRadius]
109+
this.host.style.setProperty(
110+
'--wm-border-radius',
111+
borderRadiusValue || BORDER_RADIUS.None
112+
)
113+
}
114+
115+
private applyPosition() {
116+
this.host.classList.remove('position-left', 'position-right')
117+
118+
const position = this._config.widgetPosition || WIDGET_POSITION.Right
119+
if (position === WIDGET_POSITION.Left) {
120+
this.host.classList.add('position-left')
121+
} else {
122+
this.host.classList.add('position-right')
123+
}
124+
}
125+
126+
private applyFontFamily(fontName: FontFamilyKey) {
127+
applyFontFamily(this.host, fontName, 'widget')
128+
}
129+
103130
applyTheme(element: HTMLElement) {
104131
const theme = this._config.theme
105132
if (!theme) return
@@ -114,7 +141,19 @@ export class WidgetController implements ReactiveController {
114141
element.style.setProperty('--wm-text-color', theme.textColor)
115142
}
116143
if (theme.fontFamily) {
117-
element.style.setProperty('--wm-font-family', theme.fontFamily)
144+
this.applyFontFamily(theme.fontFamily)
145+
}
146+
if (theme.fontSize) {
147+
element.style.setProperty('--wm-font-size', `${theme.fontSize}px`)
148+
}
149+
if (theme.widgetBorderRadius) {
150+
element.style.setProperty(
151+
'--wm-widget-border-radius',
152+
theme.widgetBorderRadius
153+
)
154+
}
155+
if (theme.widgetBorderRadius) {
156+
this.applyBorderRadius(theme.widgetBorderRadius)
118157
}
119158
if (theme.widgetButtonBackgroundColor) {
120159
element.style.setProperty(

components/src/widget/views/confirmation/confirmation.css

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
:host {
2-
display: flex;
3-
flex: auto;
42
font-family: var(--wm-font-family, system-ui, sans-serif);
5-
width: 100%;
63

74
--primary-color: var(--wm-primary-color, #56b7b5);
85
--background-color: var(--wm-background-color, #ffffff);
@@ -81,14 +78,10 @@
8178
flex-direction: column;
8279
width: 100%;
8380
box-sizing: border-box;
84-
padding: 0px var(--Paddings-2xl, 32px);
81+
padding: 0px var(--Spacings-2xl, 32px);
8582
flex: 1 1 auto;
8683
}
8784

88-
.margin-top-24 {
89-
margin-top: var(--Spacings-lg, 24px);
90-
}
91-
9285
.preset-buttons {
9386
display: flex;
9487
justify-content: center;
@@ -152,7 +145,7 @@
152145

153146
.currency-symbol {
154147
position: absolute;
155-
left: var(--Paddings-md, 12px);
148+
left: var(--Spacings-md, 12px);
156149
top: 50%;
157150
transform: translateY(-50%);
158151
color: var(--Text-paragraph-standard, #363636);
@@ -165,7 +158,7 @@
165158

166159
.payment-details {
167160
display: flex;
168-
padding: var(--Paddings-sm, 12px);
161+
padding: var(--Spacings-md, 12px);
169162
flex-direction: column;
170163
align-items: flex-start;
171164
gap: 10px;
@@ -196,7 +189,7 @@
196189

197190
.summary-label {
198191
color: var(--Text-paragraph-standard, #363636);
199-
font-size: var(--Font-Size-text-sm, 14px);
192+
font-size: var(--Font-Size-text-base, 14px);
200193
font-style: normal;
201194
font-weight: var(--Font-Weight-Regular, 400);
202195
line-height: var(--Font-Line-Height-sm, 20px);
@@ -247,10 +240,10 @@
247240
.primary-button {
248241
width: 100%;
249242
height: 44px;
250-
padding: var(--Spacings-xs) var(--Paddings-md);
243+
padding: var(--Spacings-sm) var(--Spacings-md);
251244
color: var(--wm-button-text-color);
252245
border: none;
253-
border-radius: var(--Radius-Moderate-rounding);
246+
border-radius: var(--widget-border-radius, 8px);
254247
font-family: var(--Font-Family-Inter);
255248
font-weight: var(--Font-Weight-Regular);
256249
font-size: var(--Font-Size-text-sm);
@@ -328,11 +321,11 @@
328321
.back-button {
329322
display: flex;
330323
align-items: center;
331-
gap: var(--Spacings-xs, 8px);
324+
gap: var(--Spacings-sm, 8px);
332325
background: transparent;
333326
border: 1px solid transparent;
334327
color: var(--primary-color);
335-
padding: var(--Paddings-sm, 12px) var(--Paddings-md, 12px);
328+
padding: var(--Spacings-md, 12px);
336329
border-radius: var(--Radius-Moderate-rounding, 8px);
337330
font-family: var(--Font-Family-Inter, Inter);
338331
font-size: var(--Font-Size-text-sm, 14px);
@@ -385,14 +378,14 @@
385378
}
386379

387380
.form-input.with-currency {
388-
padding-left: calc(var(--Paddings-md, 12px) + 1.5em);
381+
padding-left: calc(var(--Spacings-md, 12px) + 1.5em);
389382
}
390383

391384
.form-input,
392385
.payment-note-input {
393386
width: 100%;
394387
height: 48px;
395-
padding: var(--Paddings-sm, 12px) var(--Paddings-md, 12px);
388+
padding: var(--Spacings-md, 12px);
396389
border: 1px solid var(--Colors-silver-300, #c9c9c9);
397390
border-radius: var(--Radius-Moderate-rounding, 8px);
398391
font-family: var(--Font-Family-Inter, Inter);
@@ -430,5 +423,11 @@
430423
}
431424

432425
.form-input.with-currency {
433-
padding-left: calc(var(--Paddings-md, 12px) + 1.5em);
426+
padding-left: calc(var(--Spacings-md, 12px) + 1.5em);
427+
}
428+
429+
@media (max-width: 480px) {
430+
.widget-header-container {
431+
padding: var(--Spacings-xs) var(--Spacings-md);
432+
}
434433
}

0 commit comments

Comments
 (0)