@transferwise/components 0.0.0-experimental-e5e0684 → 0.0.0-experimental-37d8e2c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/avatar/Avatar.js +4 -1
- package/build/avatar/Avatar.js.map +1 -1
- package/build/avatar/Avatar.mjs +4 -1
- package/build/avatar/Avatar.mjs.map +1 -1
- package/build/avatar/colors/colors.js +1 -1
- package/build/avatar/colors/colors.js.map +1 -1
- package/build/avatar/colors/colors.mjs +1 -1
- package/build/avatar/colors/colors.mjs.map +1 -1
- package/build/avatarView/AvatarView.js +207 -0
- package/build/avatarView/AvatarView.js.map +1 -0
- package/build/avatarView/AvatarView.mjs +205 -0
- package/build/avatarView/AvatarView.mjs.map +1 -0
- package/build/avatarWrapper/AvatarWrapper.js +6 -9
- package/build/avatarWrapper/AvatarWrapper.js.map +1 -1
- package/build/avatarWrapper/AvatarWrapper.mjs +5 -8
- package/build/avatarWrapper/AvatarWrapper.mjs.map +1 -1
- package/build/badge/Badge.js +6 -1
- package/build/badge/Badge.js.map +1 -1
- package/build/badge/Badge.mjs +6 -1
- package/build/badge/Badge.mjs.map +1 -1
- package/build/circularButton/CircularButton.js +4 -7
- package/build/circularButton/CircularButton.js.map +1 -1
- package/build/circularButton/CircularButton.mjs +4 -7
- package/build/circularButton/CircularButton.mjs.map +1 -1
- package/build/common/circle/Circle.js +24 -1
- package/build/common/circle/Circle.js.map +1 -1
- package/build/common/circle/Circle.mjs +24 -1
- package/build/common/circle/Circle.mjs.map +1 -1
- package/build/common/initials.js +12 -0
- package/build/common/initials.js.map +1 -0
- package/build/common/initials.mjs +10 -0
- package/build/common/initials.mjs.map +1 -0
- package/build/common/notificationDot/NotificationDot.js +16 -0
- package/build/common/notificationDot/NotificationDot.js.map +1 -0
- package/build/common/notificationDot/NotificationDot.mjs +14 -0
- package/build/common/notificationDot/NotificationDot.mjs.map +1 -0
- package/build/display/Display.js +1 -1
- package/build/display/Display.js.map +1 -1
- package/build/display/Display.mjs +1 -1
- package/build/display/Display.mjs.map +1 -1
- package/build/index.js +6 -0
- package/build/index.js.map +1 -1
- package/build/index.mjs +3 -0
- package/build/index.mjs.map +1 -1
- package/build/main.css +82 -41
- package/build/statusIcon/StatusIcon.js +4 -2
- package/build/statusIcon/StatusIcon.js.map +1 -1
- package/build/statusIcon/StatusIcon.mjs +4 -2
- package/build/statusIcon/StatusIcon.mjs.map +1 -1
- package/build/styles/avatarGroup/AvatarGroup.css +28 -0
- package/build/styles/avatarView/AvatarView.css +22 -0
- package/build/styles/badge/Badge.css +5 -5
- package/build/styles/circularButton/CircularButton.css +0 -24
- package/build/styles/common/circle/Circle.css +17 -0
- package/build/styles/common/notificationDot/NotificationDot.css +10 -0
- package/build/styles/main.css +82 -41
- package/build/styles/statusIcon/StatusIcon.css +0 -12
- package/build/typeahead/util/highlight.js +14 -2
- package/build/typeahead/util/highlight.js.map +1 -1
- package/build/typeahead/util/highlight.mjs +15 -3
- package/build/typeahead/util/highlight.mjs.map +1 -1
- package/build/types/avatar/Avatar.d.ts +3 -0
- package/build/types/avatar/Avatar.d.ts.map +1 -1
- package/build/types/avatar/colors/colors.d.ts +1 -1
- package/build/types/avatar/colors/colors.d.ts.map +1 -1
- package/build/types/avatarGroup/AvatarGroup.d.ts +19 -0
- package/build/types/avatarGroup/AvatarGroup.d.ts.map +1 -0
- package/build/types/avatarGroup/index.d.ts +3 -0
- package/build/types/avatarGroup/index.d.ts.map +1 -0
- package/build/types/avatarView/AvatarView.d.ts +25 -0
- package/build/types/avatarView/AvatarView.d.ts.map +1 -0
- package/build/types/avatarView/index.d.ts +3 -0
- package/build/types/avatarView/index.d.ts.map +1 -0
- package/build/types/avatarWrapper/AvatarWrapper.d.ts +3 -0
- package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
- package/build/types/badge/Badge.d.ts +2 -1
- package/build/types/badge/Badge.d.ts.map +1 -1
- package/build/types/circularButton/CircularButton.d.ts.map +1 -1
- package/build/types/common/circle/Circle.d.ts +1 -1
- package/build/types/common/circle/Circle.d.ts.map +1 -1
- package/build/types/common/index.d.ts +1 -0
- package/build/types/common/initials.d.ts +2 -0
- package/build/types/common/initials.d.ts.map +1 -0
- package/build/types/common/notificationDot/NotificationDot.d.ts +8 -0
- package/build/types/common/notificationDot/NotificationDot.d.ts.map +1 -0
- package/build/types/index.d.ts +4 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/statusIcon/StatusIcon.d.ts +6 -2
- package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
- package/build/types/typeahead/util/highlight.d.ts +1 -1
- package/build/types/typeahead/util/highlight.d.ts.map +1 -1
- package/build/types/uploadInput/UploadInput.d.ts +0 -9
- package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts +1 -3
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
- package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts.map +1 -1
- package/build/uploadInput/UploadInput.js +51 -57
- package/build/uploadInput/UploadInput.js.map +1 -1
- package/build/uploadInput/UploadInput.mjs +51 -57
- package/build/uploadInput/UploadInput.mjs.map +1 -1
- package/build/uploadInput/uploadItem/UploadItem.js +3 -8
- package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
- package/build/uploadInput/uploadItem/UploadItem.mjs +3 -8
- package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
- package/build/uploadInput/uploadItem/UploadItemLink.js +0 -1
- package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
- package/build/uploadInput/uploadItem/UploadItemLink.mjs +0 -1
- package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
- package/package.json +3 -3
- package/src/avatar/Avatar.tsx +4 -1
- package/src/avatar/colors/colors.ts +4 -4
- package/src/avatarGroup/AvatarGroup.css +28 -0
- package/src/avatarGroup/AvatarGroup.less +32 -0
- package/src/avatarGroup/AvatarGroup.story.tsx +249 -0
- package/src/avatarGroup/AvatarGroup.tsx +78 -0
- package/src/avatarGroup/index.ts +2 -0
- package/src/avatarView/AvatarView.css +22 -0
- package/src/avatarView/AvatarView.less +24 -0
- package/src/avatarView/AvatarView.story.tsx +425 -0
- package/src/avatarView/AvatarView.tsx +217 -0
- package/src/avatarView/index.ts +2 -0
- package/src/avatarWrapper/AvatarWrapper.tsx +12 -16
- package/src/badge/Badge.css +5 -5
- package/src/badge/Badge.less +3 -3
- package/src/badge/Badge.tsx +10 -1
- package/src/circularButton/CircularButton.css +0 -24
- package/src/circularButton/CircularButton.less +15 -15
- package/src/circularButton/CircularButton.tsx +4 -5
- package/src/common/circle/Circle.css +17 -0
- package/src/common/circle/Circle.less +22 -0
- package/src/common/circle/Circle.tsx +33 -2
- package/src/common/index.js +1 -0
- package/src/common/initials.ts +13 -0
- package/src/common/notificationDot/NotificationDot.css +10 -0
- package/src/common/notificationDot/NotificationDot.less +7 -0
- package/src/common/notificationDot/NotificationDot.tsx +10 -0
- package/src/display/Display.tsx +1 -1
- package/src/index.ts +4 -0
- package/src/main.css +82 -41
- package/src/main.less +3 -0
- package/src/statusIcon/StatusIcon.css +0 -12
- package/src/statusIcon/StatusIcon.less +6 -6
- package/src/statusIcon/StatusIcon.story.tsx +32 -28
- package/src/statusIcon/StatusIcon.tsx +18 -4
- package/src/test-utils/avatar-img.webp +0 -0
- package/src/typeahead/typeaheadOption/TypeaheadOption.spec.js +7 -5
- package/src/typeahead/util/highlight.tsx +7 -2
- package/src/uploadInput/UploadInput.spec.tsx +6 -150
- package/src/uploadInput/UploadInput.tsx +60 -81
- package/src/uploadInput/uploadItem/UploadItem.tsx +6 -12
- package/src/uploadInput/uploadItem/UploadItemLink.tsx +1 -9
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/* eslint-disable react/jsx-key */
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { storyConfig } from '../test-utils';
|
|
4
|
+
import {
|
|
5
|
+
Freeze,
|
|
6
|
+
Graph,
|
|
7
|
+
Leaf,
|
|
8
|
+
MassPayout,
|
|
9
|
+
Money,
|
|
10
|
+
Plane,
|
|
11
|
+
PlusCircle,
|
|
12
|
+
QrCode,
|
|
13
|
+
Rewards,
|
|
14
|
+
Target,
|
|
15
|
+
Team,
|
|
16
|
+
Transport,
|
|
17
|
+
Wallet,
|
|
18
|
+
Water,
|
|
19
|
+
} from '@transferwise/icons';
|
|
20
|
+
import { action } from '@storybook/addon-actions';
|
|
21
|
+
import { useState } from 'react';
|
|
22
|
+
import AvatarView, { AvatarViewProps } from '.';
|
|
23
|
+
import { Flag } from '@wise/art';
|
|
24
|
+
import Body from '../body';
|
|
25
|
+
import AvatarWrapper from '../avatarWrapper';
|
|
26
|
+
import { getInitials, Sentiment } from '../common';
|
|
27
|
+
import Display from '../display';
|
|
28
|
+
import { get } from 'lodash';
|
|
29
|
+
import { getAvatarColorFromSeed } from '../avatar/colors';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
title: 'Content/AvatarView',
|
|
33
|
+
} satisfies Meta<typeof AvatarView>;
|
|
34
|
+
|
|
35
|
+
type Story = StoryObj<typeof AvatarView>;
|
|
36
|
+
|
|
37
|
+
const profileName1 = 'Wolter White';
|
|
38
|
+
const profileName2 = 'Tyler Durden';
|
|
39
|
+
const sizes: AvatarViewProps['size'][] = [16, 24, 32, 40, 48, 56, 72];
|
|
40
|
+
|
|
41
|
+
export const Selected: Story = storyConfig(
|
|
42
|
+
{
|
|
43
|
+
render: () => {
|
|
44
|
+
const [selected, setSelected] = useState(true);
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
style={{
|
|
48
|
+
gap: '1em',
|
|
49
|
+
display: 'grid',
|
|
50
|
+
justifyContent: 'space-between',
|
|
51
|
+
gridTemplate: `auto auto / repeat(${sizes.length}, min-content)`,
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{sizes.map((size) => (
|
|
55
|
+
<AvatarView key={size} size={size} selected={selected}>
|
|
56
|
+
<Freeze />
|
|
57
|
+
</AvatarView>
|
|
58
|
+
))}
|
|
59
|
+
{sizes.map((size) => (
|
|
60
|
+
<AvatarView
|
|
61
|
+
key={size}
|
|
62
|
+
size={size}
|
|
63
|
+
selected={selected}
|
|
64
|
+
action={{
|
|
65
|
+
onClick: () => {
|
|
66
|
+
action('interactive avatar');
|
|
67
|
+
setSelected(!selected);
|
|
68
|
+
},
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<Freeze />
|
|
72
|
+
</AvatarView>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{ variants: ['light', 'dark'] },
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
export const Notification: Story = storyConfig(
|
|
82
|
+
{
|
|
83
|
+
render: () => (
|
|
84
|
+
<div
|
|
85
|
+
style={{
|
|
86
|
+
gap: '1em',
|
|
87
|
+
display: 'grid',
|
|
88
|
+
justifyContent: 'space-between',
|
|
89
|
+
gridTemplate: `auto auto / repeat(${sizes.length}, min-content)`,
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{sizes.map((size) => (
|
|
93
|
+
<AvatarView key={size} size={size} notification>
|
|
94
|
+
<MassPayout />
|
|
95
|
+
</AvatarView>
|
|
96
|
+
))}
|
|
97
|
+
{sizes.map((size) => (
|
|
98
|
+
<AvatarView
|
|
99
|
+
key={size}
|
|
100
|
+
size={size}
|
|
101
|
+
notification
|
|
102
|
+
action={{
|
|
103
|
+
onClick: () => action('interactive avatar'),
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<MassPayout />
|
|
107
|
+
</AvatarView>
|
|
108
|
+
))}
|
|
109
|
+
|
|
110
|
+
{sizes.map((size) => (
|
|
111
|
+
<AvatarView key={size} size={size} notification action={{ href: '#' }}>
|
|
112
|
+
<MassPayout />
|
|
113
|
+
</AvatarView>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
),
|
|
117
|
+
},
|
|
118
|
+
{ variants: ['light', 'dark'] },
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
export const Badge: Story = storyConfig(
|
|
122
|
+
{
|
|
123
|
+
render: () => {
|
|
124
|
+
const currencies = ['USD', 'EUR', 'GBP', 'AUD', 'CAD', 'JPY', 'CNY'];
|
|
125
|
+
const icons = [
|
|
126
|
+
<Plane />,
|
|
127
|
+
<QrCode />,
|
|
128
|
+
<Target />,
|
|
129
|
+
<Transport />,
|
|
130
|
+
<Wallet />,
|
|
131
|
+
<Water />,
|
|
132
|
+
<Team />,
|
|
133
|
+
];
|
|
134
|
+
return (
|
|
135
|
+
<div
|
|
136
|
+
style={{
|
|
137
|
+
gap: '1em',
|
|
138
|
+
display: 'grid',
|
|
139
|
+
justifyContent: 'space-between',
|
|
140
|
+
gridTemplate: 'auto auto / repeat(7, min-content)',
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
{sizes.map((size, index) => (
|
|
144
|
+
<AvatarView key={size} size={size} badge={<Flag code={currencies[index]} />}>
|
|
145
|
+
{icons[index]}
|
|
146
|
+
</AvatarView>
|
|
147
|
+
))}
|
|
148
|
+
|
|
149
|
+
{sizes.map((size, index) => (
|
|
150
|
+
<AvatarView
|
|
151
|
+
key={size}
|
|
152
|
+
size={size}
|
|
153
|
+
badge={<Flag code={currencies[index]} />}
|
|
154
|
+
action={{ onClick: action('onClick') }}
|
|
155
|
+
entity={{ name: 'Test Name' }}
|
|
156
|
+
>
|
|
157
|
+
{icons[index]}
|
|
158
|
+
</AvatarView>
|
|
159
|
+
))}
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{ variants: ['light', 'dark'] },
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
export const Interactive: Story = storyConfig(
|
|
168
|
+
{
|
|
169
|
+
render: () => {
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
style={{
|
|
173
|
+
gap: '1em',
|
|
174
|
+
display: 'grid',
|
|
175
|
+
justifyContent: 'space-between',
|
|
176
|
+
gridTemplate: 'auto auto / repeat(2, min-content)',
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
<Body type="body-large-bold">Button</Body>
|
|
180
|
+
<Body type="body-large-bold">Link</Body>
|
|
181
|
+
|
|
182
|
+
<AvatarView
|
|
183
|
+
action={{ onClick: action('onClick') }}
|
|
184
|
+
entity={{
|
|
185
|
+
imageSrc: '../avatar-img.webp',
|
|
186
|
+
}}
|
|
187
|
+
/>
|
|
188
|
+
|
|
189
|
+
<AvatarView
|
|
190
|
+
action={{ href: '#' }}
|
|
191
|
+
entity={{
|
|
192
|
+
imageSrc: '../avatar-img.webp',
|
|
193
|
+
}}
|
|
194
|
+
/>
|
|
195
|
+
|
|
196
|
+
<AvatarView
|
|
197
|
+
action={{ onClick: action('onClick') }}
|
|
198
|
+
badge={<Flag code="EU" />}
|
|
199
|
+
entity={{
|
|
200
|
+
imageSrc: '../avatar-img.webp',
|
|
201
|
+
name: 'Test Name',
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
|
|
205
|
+
<AvatarView
|
|
206
|
+
action={{ href: '#' }}
|
|
207
|
+
badge={<Flag code="EU" />}
|
|
208
|
+
entity={{
|
|
209
|
+
imageSrc: '../avatar-img.webp',
|
|
210
|
+
name: 'Test Name',
|
|
211
|
+
}}
|
|
212
|
+
/>
|
|
213
|
+
|
|
214
|
+
<AvatarView action={{ onClick: action('onClick') }} entity={{ name: 'Test Name' }} />
|
|
215
|
+
|
|
216
|
+
<AvatarView action={{ href: '#' }} entity={{ name: 'Test Name' }} />
|
|
217
|
+
|
|
218
|
+
<AvatarView
|
|
219
|
+
action={{ onClick: action('onClick') }}
|
|
220
|
+
badge={<Flag code="EU" />}
|
|
221
|
+
entity={{ name: 'Test Name' }}
|
|
222
|
+
/>
|
|
223
|
+
|
|
224
|
+
<AvatarView
|
|
225
|
+
action={{ href: '#' }}
|
|
226
|
+
badge={<Flag code="EU" />}
|
|
227
|
+
entity={{ name: 'Test Name' }}
|
|
228
|
+
/>
|
|
229
|
+
|
|
230
|
+
<AvatarView action={{ onClick: action('onClick') }}>
|
|
231
|
+
<Leaf />
|
|
232
|
+
</AvatarView>
|
|
233
|
+
|
|
234
|
+
<AvatarView action={{ href: '#' }}>
|
|
235
|
+
<Leaf />
|
|
236
|
+
</AvatarView>
|
|
237
|
+
|
|
238
|
+
<AvatarView action={{ onClick: action('onClick') }} badge={<Flag code="EU" />}>
|
|
239
|
+
<Leaf />
|
|
240
|
+
</AvatarView>
|
|
241
|
+
|
|
242
|
+
<AvatarView action={{ href: '#' }} badge={<Flag code="EU" />}>
|
|
243
|
+
<Leaf />
|
|
244
|
+
</AvatarView>
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{ variants: ['light', 'dark'] },
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
export const Entity: Story = storyConfig(
|
|
253
|
+
{
|
|
254
|
+
render: () => {
|
|
255
|
+
return (
|
|
256
|
+
<div
|
|
257
|
+
style={{
|
|
258
|
+
gap: '1em',
|
|
259
|
+
display: 'grid',
|
|
260
|
+
justifyContent: 'space-between',
|
|
261
|
+
gridTemplate: 'auto auto / repeat(7, min-content)',
|
|
262
|
+
}}
|
|
263
|
+
>
|
|
264
|
+
{sizes.map((size) => (
|
|
265
|
+
<AvatarView
|
|
266
|
+
key={size}
|
|
267
|
+
size={size}
|
|
268
|
+
entity={{
|
|
269
|
+
imageSrc: '../avatar-img.webp',
|
|
270
|
+
name: 'Test Name',
|
|
271
|
+
profileType: 'business',
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
))}
|
|
275
|
+
|
|
276
|
+
{sizes.map((size) => (
|
|
277
|
+
<AvatarView
|
|
278
|
+
key={size}
|
|
279
|
+
size={size}
|
|
280
|
+
action={{ onClick: action('onClick') }}
|
|
281
|
+
entity={{
|
|
282
|
+
imageSrc: '../avatar-img.webp',
|
|
283
|
+
name: 'Test Name',
|
|
284
|
+
profileType: 'business',
|
|
285
|
+
}}
|
|
286
|
+
/>
|
|
287
|
+
))}
|
|
288
|
+
|
|
289
|
+
{sizes.map((size) => (
|
|
290
|
+
<AvatarView key={size} size={size} entity={{ name: 'Test Name' }} />
|
|
291
|
+
))}
|
|
292
|
+
|
|
293
|
+
{sizes.map((size) => (
|
|
294
|
+
<AvatarView
|
|
295
|
+
key={size}
|
|
296
|
+
size={size}
|
|
297
|
+
action={{ onClick: action('onClick') }}
|
|
298
|
+
entity={{ name: 'Test Name' }}
|
|
299
|
+
/>
|
|
300
|
+
))}
|
|
301
|
+
|
|
302
|
+
{sizes.map((size) => (
|
|
303
|
+
<AvatarView key={size} size={size} entity={{ profileType: 'business' }} />
|
|
304
|
+
))}
|
|
305
|
+
|
|
306
|
+
{sizes.map((size) => (
|
|
307
|
+
<AvatarView
|
|
308
|
+
key={size}
|
|
309
|
+
size={size}
|
|
310
|
+
action={{ onClick: action('onClick') }}
|
|
311
|
+
entity={{ profileType: 'business' }}
|
|
312
|
+
/>
|
|
313
|
+
))}
|
|
314
|
+
|
|
315
|
+
{sizes.map((size) => (
|
|
316
|
+
<AvatarView key={size} size={size} />
|
|
317
|
+
))}
|
|
318
|
+
|
|
319
|
+
{sizes.map((size) => (
|
|
320
|
+
<AvatarView key={size} size={size} action={{ onClick: action('onClick') }} />
|
|
321
|
+
))}
|
|
322
|
+
</div>
|
|
323
|
+
);
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
{ variants: ['light', 'dark'] },
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
export const BrokenImageFallback: Story = {
|
|
330
|
+
render: () => <AvatarView entity={{ imageSrc: '../img-wrong-url.lol' }} />,
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
export const EdgeInstaces: Story = {
|
|
334
|
+
render: () => {
|
|
335
|
+
const css = `.custom-variant {
|
|
336
|
+
border: 1px dotted black;
|
|
337
|
+
}`;
|
|
338
|
+
return (
|
|
339
|
+
<div
|
|
340
|
+
style={{
|
|
341
|
+
gap: '1em',
|
|
342
|
+
display: 'grid',
|
|
343
|
+
justifyContent: 'space-between',
|
|
344
|
+
gridTemplate: 'auto auto / repeat(2, min-content)',
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
<style>{css}</style>
|
|
348
|
+
{/* <Body>- Custom colors</Body> */}
|
|
349
|
+
<AvatarView
|
|
350
|
+
action={{ onClick: action('onClick') }}
|
|
351
|
+
badge={<Flag code="eu" />}
|
|
352
|
+
style={{ backgroundColor: 'var(--color-bright-orange)' }}
|
|
353
|
+
>
|
|
354
|
+
<Money />
|
|
355
|
+
</AvatarView>
|
|
356
|
+
<AvatarView
|
|
357
|
+
badge={<Flag code="eu" />}
|
|
358
|
+
style={{ backgroundColor: 'var(--color-bright-orange)' }}
|
|
359
|
+
>
|
|
360
|
+
<Money />
|
|
361
|
+
</AvatarView>
|
|
362
|
+
{/* <Body>- Add example with dashed avatar as in shared balances</Body> */}
|
|
363
|
+
<AvatarView action={{ onClick: action('onClick') }} className="custom-variant">
|
|
364
|
+
<PlusCircle />
|
|
365
|
+
</AvatarView>
|
|
366
|
+
<AvatarView action={{ href: '#' }} className="custom-variant">
|
|
367
|
+
<PlusCircle />
|
|
368
|
+
</AvatarView>
|
|
369
|
+
<AvatarView action={{ href: '#' }} className="custom-variant">
|
|
370
|
+
{null}
|
|
371
|
+
</AvatarView>
|
|
372
|
+
{/* <Body> - Add example custom backgrounds as in account summary (https://wise.com/account-summary)</Body> */}
|
|
373
|
+
<AvatarView style={{ backgroundColor: 'rgb(203, 217, 195)' }}>
|
|
374
|
+
<span style={{ color: 'rgb(22, 51, 0)' }}>
|
|
375
|
+
<Graph />
|
|
376
|
+
</span>
|
|
377
|
+
</AvatarView>
|
|
378
|
+
<AvatarView
|
|
379
|
+
action={{ onClick: action('onClikc') }}
|
|
380
|
+
style={{ backgroundColor: 'rgb(203, 217, 195)' }}
|
|
381
|
+
>
|
|
382
|
+
<span style={{ color: 'rgb(22, 51, 0)' }}>
|
|
383
|
+
<Graph />
|
|
384
|
+
</span>
|
|
385
|
+
</AvatarView>
|
|
386
|
+
<AvatarView style={{ backgroundColor: 'rgb(22, 51, 0)' }}>
|
|
387
|
+
<span style={{ color: 'rgb(203, 217, 195)' }}>
|
|
388
|
+
<Rewards />
|
|
389
|
+
</span>
|
|
390
|
+
</AvatarView>
|
|
391
|
+
<AvatarView
|
|
392
|
+
action={{ onClick: action('onClikc') }}
|
|
393
|
+
style={{ backgroundColor: 'rgb(22, 51, 0)' }}
|
|
394
|
+
>
|
|
395
|
+
<span style={{ color: 'rgb(203, 217, 195)' }}>
|
|
396
|
+
<Rewards />
|
|
397
|
+
</span>
|
|
398
|
+
</AvatarView>
|
|
399
|
+
{/* <Body>- Add example Wise Sand + custom background from hash</Body> */}
|
|
400
|
+
<AvatarView
|
|
401
|
+
badge={<Flag code="eu" />}
|
|
402
|
+
style={{ backgroundColor: getAvatarColorFromSeed(profileName1) }}
|
|
403
|
+
>
|
|
404
|
+
<Display>{getInitials(profileName1)}</Display>
|
|
405
|
+
</AvatarView>
|
|
406
|
+
<AvatarView style={{ backgroundColor: getAvatarColorFromSeed(profileName1) }}>
|
|
407
|
+
<Display>{getInitials(profileName1)}</Display>
|
|
408
|
+
</AvatarView>
|
|
409
|
+
<AvatarView
|
|
410
|
+
action={{ onClick: action('onClick') }}
|
|
411
|
+
style={{ backgroundColor: getAvatarColorFromSeed(profileName2) }}
|
|
412
|
+
>
|
|
413
|
+
<Display>{getInitials(profileName1)}</Display>
|
|
414
|
+
</AvatarView>
|
|
415
|
+
<AvatarView
|
|
416
|
+
action={{ href: '#' }}
|
|
417
|
+
badge={<Flag code="eu" />}
|
|
418
|
+
style={{ backgroundColor: getAvatarColorFromSeed(profileName2) }}
|
|
419
|
+
>
|
|
420
|
+
<Display>{getInitials(profileName1)}</Display>
|
|
421
|
+
</AvatarView>
|
|
422
|
+
</div>
|
|
423
|
+
);
|
|
424
|
+
},
|
|
425
|
+
};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import Badge from '../badge';
|
|
2
|
+
import { getInitials } from '../common';
|
|
3
|
+
import NotificationDot from '../common/notificationDot/NotificationDot';
|
|
4
|
+
import StatusIcon from '../statusIcon';
|
|
5
|
+
import Circle from '../common/circle';
|
|
6
|
+
import { HTMLAttributes, PropsWithChildren, useState } from 'react';
|
|
7
|
+
import { ActionOptions as ActionProps } from '../common/action/Action';
|
|
8
|
+
import { clsx } from 'clsx';
|
|
9
|
+
import { Briefcase, Profile } from '@transferwise/icons';
|
|
10
|
+
|
|
11
|
+
type InteractiveProps = Omit<ActionProps, 'text'> &
|
|
12
|
+
Pick<HTMLAttributes<HTMLDivElement>, 'className' | 'aria-labelledby'>;
|
|
13
|
+
|
|
14
|
+
const AvatarViewDefaultSize = 48;
|
|
15
|
+
|
|
16
|
+
export type Props = PropsWithChildren<{
|
|
17
|
+
size?: 16 | 24 | 32 | 40 | 48 | 56 | 72;
|
|
18
|
+
/** adds prebuilt notification dot via Badge component */
|
|
19
|
+
notification?: boolean;
|
|
20
|
+
/** adds custom badge to avatar */
|
|
21
|
+
badge?: React.ReactNode;
|
|
22
|
+
entity?: EntityProps;
|
|
23
|
+
/** changes background color to neutral and wraps component with button */
|
|
24
|
+
action?: InteractiveProps;
|
|
25
|
+
/** changes background color to neutral and adds predefined Badge */
|
|
26
|
+
selected?: boolean;
|
|
27
|
+
/** often use case to override `background-color` or `border` */
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
}> &
|
|
30
|
+
Pick<HTMLAttributes<HTMLDivElement>, 'className' | 'style'>;
|
|
31
|
+
|
|
32
|
+
/** Non interactive avatar view has custom border width */
|
|
33
|
+
const mapNonInteractiveBorderWidth = {
|
|
34
|
+
16: 0.667,
|
|
35
|
+
24: 1,
|
|
36
|
+
32: 1,
|
|
37
|
+
40: 1.25,
|
|
38
|
+
48: 1.5,
|
|
39
|
+
56: 1.75,
|
|
40
|
+
72: 2.25,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Selected state has custom border width */
|
|
44
|
+
const mapSelectedBorderWidth = {
|
|
45
|
+
16: 1,
|
|
46
|
+
24: 1,
|
|
47
|
+
32: 1,
|
|
48
|
+
40: 2,
|
|
49
|
+
48: 2,
|
|
50
|
+
56: 2,
|
|
51
|
+
72: 2,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function AvatarView({
|
|
55
|
+
children = undefined,
|
|
56
|
+
size = AvatarViewDefaultSize,
|
|
57
|
+
selected,
|
|
58
|
+
notification,
|
|
59
|
+
badge,
|
|
60
|
+
entity,
|
|
61
|
+
action,
|
|
62
|
+
className,
|
|
63
|
+
style,
|
|
64
|
+
}: Props) {
|
|
65
|
+
return (
|
|
66
|
+
<Interactive
|
|
67
|
+
{...action}
|
|
68
|
+
className={clsx('np-avatar-view', { 'np-avatar-view-selected': selected }, className)}
|
|
69
|
+
>
|
|
70
|
+
<Badges asset={badge} avatarProps={{ size, notification, selected }}>
|
|
71
|
+
<Circle
|
|
72
|
+
size={size}
|
|
73
|
+
fixedSize
|
|
74
|
+
style={{
|
|
75
|
+
// @ts-expect-error CSS custom props allowed
|
|
76
|
+
'--avatar-view-selected-border-width': `${mapSelectedBorderWidth[size]}px`,
|
|
77
|
+
'--avatar-view-non-interactive-border-width': `${mapNonInteractiveBorderWidth[size]}px`,
|
|
78
|
+
...style,
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
{children === undefined ? <Entity {...entity} /> : children}
|
|
82
|
+
</Circle>
|
|
83
|
+
</Badges>
|
|
84
|
+
</Interactive>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function Interactive({
|
|
89
|
+
children,
|
|
90
|
+
onClick,
|
|
91
|
+
href,
|
|
92
|
+
target,
|
|
93
|
+
className,
|
|
94
|
+
'aria-label': ariaLabel,
|
|
95
|
+
'aria-labelledby': ariaLabelledby,
|
|
96
|
+
}: PropsWithChildren<InteractiveProps>) {
|
|
97
|
+
const elementName: React.ElementType | null = onClick ? 'button' : href ? 'a' : null;
|
|
98
|
+
if (elementName === null) {
|
|
99
|
+
// non interactive
|
|
100
|
+
return <div className={className}>{children}</div>;
|
|
101
|
+
}
|
|
102
|
+
const a11yProps = { ariaLabel, ariaLabelledby };
|
|
103
|
+
if (elementName === 'button') {
|
|
104
|
+
return (
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
className={clsx(className, 'np-avatar-view-interactive', 'btn-unstyled')}
|
|
108
|
+
onClick={onClick}
|
|
109
|
+
{...a11yProps}
|
|
110
|
+
>
|
|
111
|
+
{children}
|
|
112
|
+
</button>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (elementName === 'a') {
|
|
116
|
+
return (
|
|
117
|
+
// eslint-disable-next-line react/jsx-no-target-blank
|
|
118
|
+
<a
|
|
119
|
+
href={href}
|
|
120
|
+
target={target}
|
|
121
|
+
rel={target === '_blank' ? 'noreferrer' : undefined}
|
|
122
|
+
className={clsx(className, 'np-avatar-view-interactive', 'text-no-decoration')}
|
|
123
|
+
{...a11yProps}
|
|
124
|
+
>
|
|
125
|
+
{children}
|
|
126
|
+
</a>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const mapBadgeSize = {
|
|
132
|
+
16: 'sm',
|
|
133
|
+
24: 'sm',
|
|
134
|
+
32: 'sm',
|
|
135
|
+
40: 'sm',
|
|
136
|
+
48: 'sm',
|
|
137
|
+
56: 'lg',
|
|
138
|
+
72: 'lg',
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/** AvatarView has a custom sizes for badge */
|
|
142
|
+
const mapNotificationSize = {
|
|
143
|
+
16: 6,
|
|
144
|
+
24: 8,
|
|
145
|
+
32: 10,
|
|
146
|
+
40: 10,
|
|
147
|
+
48: 14,
|
|
148
|
+
56: 16,
|
|
149
|
+
72: 20,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
type BadgesProps = PropsWithChildren<{
|
|
153
|
+
asset: React.ReactNode;
|
|
154
|
+
avatarProps: Pick<Props, 'selected' | 'size' | 'notification'>;
|
|
155
|
+
}>;
|
|
156
|
+
|
|
157
|
+
function Badges({ children, asset, avatarProps }: BadgesProps) {
|
|
158
|
+
const { size = AvatarViewDefaultSize, selected, notification } = avatarProps;
|
|
159
|
+
if (asset || selected || notification) {
|
|
160
|
+
if (asset && size > 16) {
|
|
161
|
+
return (
|
|
162
|
+
<Badge badge={asset} size={mapBadgeSize[size]}>
|
|
163
|
+
{children}
|
|
164
|
+
</Badge>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
if (selected && size > 16) {
|
|
168
|
+
return (
|
|
169
|
+
<Badge
|
|
170
|
+
badge={<StatusIcon sentiment="positive" size={mapBadgeSize[size]} />}
|
|
171
|
+
size={mapBadgeSize[size]}
|
|
172
|
+
>
|
|
173
|
+
{children}
|
|
174
|
+
</Badge>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (notification) {
|
|
178
|
+
return (
|
|
179
|
+
<Badge
|
|
180
|
+
badge={
|
|
181
|
+
<NotificationDot
|
|
182
|
+
// @ts-expect-error CSS custom props allowed
|
|
183
|
+
style={{ '--badge-notification-dot-size': `${mapNotificationSize[size]}px` }}
|
|
184
|
+
/>
|
|
185
|
+
}
|
|
186
|
+
position="top-right"
|
|
187
|
+
size={mapBadgeSize[size]}
|
|
188
|
+
>
|
|
189
|
+
{children}
|
|
190
|
+
</Badge>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return children;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export type EntityProps = {
|
|
198
|
+
imageSrc?: string | null;
|
|
199
|
+
name?: string | null;
|
|
200
|
+
profileType?: 'personal' | 'business' | null;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
function Entity({ imageSrc = null, name = null, profileType = null }: EntityProps) {
|
|
204
|
+
const [tryLoadImage, setTryLoadImage] = useState(true);
|
|
205
|
+
if (imageSrc && tryLoadImage) {
|
|
206
|
+
return <img src={imageSrc} alt="" onError={() => setTryLoadImage(false)} />;
|
|
207
|
+
}
|
|
208
|
+
if (profileType) {
|
|
209
|
+
return profileType === 'business' ? <Briefcase /> : <Profile />;
|
|
210
|
+
}
|
|
211
|
+
if (name) {
|
|
212
|
+
return <>{getInitials(name)}</>;
|
|
213
|
+
}
|
|
214
|
+
return <Profile />;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export default AvatarView;
|
|
@@ -3,7 +3,14 @@ import { useState, useEffect } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import Avatar, { AvatarProps, AvatarType } from '../avatar';
|
|
5
5
|
import Badge, { BadgeProps } from '../badge';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ProfileType,
|
|
8
|
+
ProfileTypePersonal,
|
|
9
|
+
ProfileTypeBusiness,
|
|
10
|
+
Size,
|
|
11
|
+
Sentiment,
|
|
12
|
+
getInitials,
|
|
13
|
+
} from '../common';
|
|
7
14
|
import StatusIcon from '../statusIcon/StatusIcon';
|
|
8
15
|
|
|
9
16
|
interface OptionalBadgeProps extends Omit<BadgeProps, 'badge'> {
|
|
@@ -68,6 +75,9 @@ export type AvatarWrapperProps = {
|
|
|
68
75
|
}
|
|
69
76
|
);
|
|
70
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @deprecated Use `AvatarView` component instead
|
|
80
|
+
*/
|
|
71
81
|
const AvatarWrapper = ({
|
|
72
82
|
url,
|
|
73
83
|
'aria-label': ariaLabel,
|
|
@@ -109,7 +119,7 @@ const AvatarWrapper = ({
|
|
|
109
119
|
return {
|
|
110
120
|
type: AvatarType.INITIALS,
|
|
111
121
|
children: <>{getInitials(name)}</>,
|
|
112
|
-
backgroundColorSeed:
|
|
122
|
+
backgroundColorSeed: name?.toString(),
|
|
113
123
|
...updatedAvatarProps,
|
|
114
124
|
};
|
|
115
125
|
}
|
|
@@ -133,18 +143,4 @@ const AvatarWrapper = ({
|
|
|
133
143
|
);
|
|
134
144
|
};
|
|
135
145
|
|
|
136
|
-
function getInitials(name: string) {
|
|
137
|
-
const allInitials = name
|
|
138
|
-
.split(' ')
|
|
139
|
-
.map((part) => part[0])
|
|
140
|
-
.join('')
|
|
141
|
-
.toUpperCase();
|
|
142
|
-
|
|
143
|
-
if (allInitials.length === 1) {
|
|
144
|
-
return allInitials[0];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return allInitials[0] + allInitials.slice(-1);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
146
|
export default AvatarWrapper;
|