@ornikar/bumper 3.12.0 → 3.14.0
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/CHANGELOG.md +12 -140
- package/dist/definitions/index.d.ts +4 -0
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/system/core/themes/light/light.d.ts.map +1 -1
- package/dist/definitions/system/core/themes/types.d.ts +3 -1
- package/dist/definitions/system/core/themes/types.d.ts.map +1 -1
- package/dist/definitions/system/dataDisplays/Avatar/Avatar.d.ts +52 -0
- package/dist/definitions/system/dataDisplays/Avatar/Avatar.d.ts.map +1 -0
- package/dist/definitions/system/dataDisplays/Avatar/components/AvatarImage.d.ts +5 -0
- package/dist/definitions/system/dataDisplays/Avatar/components/AvatarImage.d.ts.map +1 -0
- package/dist/definitions/system/dataDisplays/Avatar/components/AvatarInitial.d.ts +6 -0
- package/dist/definitions/system/dataDisplays/Avatar/components/AvatarInitial.d.ts.map +1 -0
- package/dist/definitions/system/dataDisplays/Avatar/context.d.ts +3 -0
- package/dist/definitions/system/dataDisplays/Avatar/context.d.ts.map +1 -0
- package/dist/index-metro.es.android.js +190 -9
- package/dist/index-metro.es.android.js.map +1 -1
- package/dist/index-metro.es.ios.js +190 -9
- package/dist/index-metro.es.ios.js.map +1 -1
- package/dist/index-node-22.22.cjs.js +193 -7
- package/dist/index-node-22.22.cjs.js.map +1 -1
- package/dist/index-node-22.22.cjs.web.js +193 -7
- package/dist/index-node-22.22.cjs.web.js.map +1 -1
- package/dist/index-node-22.22.es.mjs +192 -9
- package/dist/index-node-22.22.es.mjs.map +1 -1
- package/dist/index-node-22.22.es.web.mjs +192 -9
- package/dist/index-node-22.22.es.web.mjs.map +1 -1
- package/dist/index.es.js +193 -9
- package/dist/index.es.js.map +1 -1
- package/dist/index.es.web.js +193 -9
- package/dist/index.es.web.js.map +1 -1
- package/dist/storybook-metro.es.android.js +3 -1
- package/dist/storybook-metro.es.android.js.map +1 -1
- package/dist/storybook-metro.es.ios.js +3 -1
- package/dist/storybook-metro.es.ios.js.map +1 -1
- package/dist/storybook-node-22.22.cjs.js +3 -1
- package/dist/storybook-node-22.22.cjs.js.map +1 -1
- package/dist/storybook-node-22.22.cjs.web.js +3 -1
- package/dist/storybook-node-22.22.cjs.web.js.map +1 -1
- package/dist/storybook-node-22.22.es.mjs +3 -1
- package/dist/storybook-node-22.22.es.mjs.map +1 -1
- package/dist/storybook-node-22.22.es.web.mjs +3 -1
- package/dist/storybook-node-22.22.es.web.mjs.map +1 -1
- package/dist/storybook.es.js +3 -1
- package/dist/storybook.es.js.map +1 -1
- package/dist/storybook.es.web.js +3 -1
- package/dist/storybook.es.web.js.map +1 -1
- package/dist/tsbuildinfo +1 -1
- package/docs/migration/Avatar.md +228 -0
- package/package.json +1 -1
- package/src/Bumper.mdx +1 -0
- package/src/index.ts +6 -0
- package/src/system/core/themes/light/__snapshots__/light.stories.tsx.snap +73 -0
- package/src/system/core/themes/light/__snapshots_web__/light.stories.tsx.snap +21 -0
- package/src/system/core/themes/light/light.ts +3 -0
- package/src/system/core/themes/types.ts +5 -1
- package/src/system/dataDisplays/Avatar/Avatar.features.stories.tsx +110 -0
- package/src/system/dataDisplays/Avatar/Avatar.mdx +73 -0
- package/src/system/dataDisplays/Avatar/Avatar.stories.tsx +47 -0
- package/src/system/dataDisplays/Avatar/Avatar.tsx +124 -0
- package/src/system/dataDisplays/Avatar/__snapshots__/Avatar.features.stories.tsx.snap +891 -0
- package/src/system/dataDisplays/Avatar/__snapshots__/Avatar.stories.tsx.snap +50 -0
- package/src/system/dataDisplays/Avatar/__snapshots_web__/Avatar.features.stories.tsx.snap +545 -0
- package/src/system/dataDisplays/Avatar/__snapshots_web__/Avatar.stories.tsx.snap +37 -0
- package/src/system/dataDisplays/Avatar/assets/avatar-placeholder-disabled.webp +0 -0
- package/src/system/dataDisplays/Avatar/assets/avatar-placeholder.webp +0 -0
- package/src/system/dataDisplays/Avatar/components/AvatarImage.tsx +34 -0
- package/src/system/dataDisplays/Avatar/components/AvatarInitial.tsx +30 -0
- package/src/system/dataDisplays/Avatar/context.ts +10 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Avatar → Avatar Migration Guide
|
|
2
|
+
|
|
3
|
+
> Source: `@ornikar/kitt-universal/Avatar`
|
|
4
|
+
> Target: `@ornikar/bumper/Avatar`
|
|
5
|
+
> Generated: 2026-04-27
|
|
6
|
+
|
|
7
|
+
## Import Change
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// Before
|
|
11
|
+
import { Avatar } from '@ornikar/kitt-universal';
|
|
12
|
+
import type { AvatarProps } from '@ornikar/kitt-universal';
|
|
13
|
+
|
|
14
|
+
// After
|
|
15
|
+
import { Avatar } from '@ornikar/bumper';
|
|
16
|
+
import type { AvatarProps } from '@ornikar/bumper';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Props Mapping
|
|
20
|
+
|
|
21
|
+
| Source Prop | Target Prop | Transform | Notes |
|
|
22
|
+
| ------------------------------ | ---------------------- | ----------------------------- | --------------------------------------------- |
|
|
23
|
+
| `src` | `src` | Keep as-is | Same API |
|
|
24
|
+
| `firstname` (`string \| null`) | `firstname` (`string`) | Narrow `null` to `undefined` | See [Null Name Handling](#null-name-handling) |
|
|
25
|
+
| `lastname` (`string \| null`) | `lastname` (`string`) | Narrow `null` to `undefined` | See [Null Name Handling](#null-name-handling) |
|
|
26
|
+
| `disabled` | `disabled` | Keep as-is | Same API |
|
|
27
|
+
| `width` | `width` | Keep as-is | Used only when `size` is omitted |
|
|
28
|
+
| `height` | `height` | Keep as-is | Used only when `size` is omitted |
|
|
29
|
+
| `size` (`number`) | `size` (enum) | Map number to nearest bucket | See [Size Values](#size-values) |
|
|
30
|
+
| `round` (`boolean`) | `shape` (enum) | Convert boolean to enum value | See [Shape Mapping](#shape-mapping) |
|
|
31
|
+
| `alt` | — | Remove prop | Not exposed in bumper AvatarProps |
|
|
32
|
+
| `light` | — | Remove prop | No light/dark theme variant in bumper |
|
|
33
|
+
| `dark` | — | Remove prop | No light/dark theme variant in bumper |
|
|
34
|
+
| `sizeVariant` | — | Remove prop | Only ever `'large'`; merged into `size` enum |
|
|
35
|
+
|
|
36
|
+
Bumper adds new props with no kitt-universal equivalent:
|
|
37
|
+
|
|
38
|
+
- `testID` — testing hook, optional
|
|
39
|
+
- `shape` — explicit replacement for the `round` boolean
|
|
40
|
+
|
|
41
|
+
## Value Mappings
|
|
42
|
+
|
|
43
|
+
### Size Values
|
|
44
|
+
|
|
45
|
+
Bumper sizes map to fixed pixel dimensions (square):
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
small → 32×32
|
|
49
|
+
medium → 40×40
|
|
50
|
+
large → 48×48 (default)
|
|
51
|
+
xlarge → 64×64
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Map a numeric `size` to the closest enum bucket:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
size={32} → size="small"
|
|
58
|
+
size={40} → size="medium"
|
|
59
|
+
size={48} → (remove prop — large is the default)
|
|
60
|
+
size={64} → size="xlarge"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For numeric values that do **not** match a bucket exactly, prefer preserving the literal pixel value via `width`/`height` to avoid silent visual regressions:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
// Before
|
|
67
|
+
<Avatar size={36} firstname="Jane" />
|
|
68
|
+
|
|
69
|
+
// After
|
|
70
|
+
<Avatar width={36} height={36} firstname="Jane" />
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For dynamic numeric values (`size={someNumber}`), flag for manual review.
|
|
74
|
+
|
|
75
|
+
### Shape Mapping
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
round → shape="circle" (boolean shorthand)
|
|
79
|
+
round={true} → shape="circle"
|
|
80
|
+
round={false} → (remove prop — square is the default)
|
|
81
|
+
round={expr} → shape={expr ? 'circle' : 'square'}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### sizeVariant Mapping
|
|
85
|
+
|
|
86
|
+
`sizeVariant` only accepted `'large'` and was redundant with `size`. Always remove it:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
sizeVariant="large" → (remove prop entirely)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Null Name Handling
|
|
93
|
+
|
|
94
|
+
Bumper's `firstname` and `lastname` are `string | undefined`, not `string | null`. Convert literal `null` to an omitted prop, and runtime nullable values via `?? undefined`:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// Before
|
|
98
|
+
<Avatar firstname={null} lastname={null} />
|
|
99
|
+
|
|
100
|
+
// After
|
|
101
|
+
<Avatar />
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// Before — user.firstname / user.lastname typed `string | null`
|
|
106
|
+
<Avatar firstname={user.firstname} lastname={user.lastname} />
|
|
107
|
+
|
|
108
|
+
// After
|
|
109
|
+
<Avatar firstname={user.firstname ?? undefined} lastname={user.lastname ?? undefined} />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When both names resolve to falsy and no `src` is given, bumper falls back to the placeholder image automatically — same behavior as kitt-universal.
|
|
113
|
+
|
|
114
|
+
## Behavioral Differences
|
|
115
|
+
|
|
116
|
+
| Aspect | kitt-universal | bumper | Notes |
|
|
117
|
+
| --------------------- | ---------------------------- | ---------------------------------------------------- | --------------------------------------- |
|
|
118
|
+
| **Default shape** | square (`round=false`) | `square` | Same |
|
|
119
|
+
| **Default size** | implicit (no enum) | `large` (48×48) | Bumper enforces an explicit default |
|
|
120
|
+
| **Theme variants** | `light` / `dark` props | none | Removed; rely on surrounding theme |
|
|
121
|
+
| **Image alt text** | `alt` prop | none | No accessibility alt at component level |
|
|
122
|
+
| **Border radius** | hardcoded | `$radius.m` (square) / `$radius.circle` (circle) | Token-based, may differ visually |
|
|
123
|
+
| **Disabled state** | swap to disabled placeholder | swap to disabled placeholder + `$bg.disabled.hi` bg | Similar, with explicit token background |
|
|
124
|
+
| **Initials fallback** | `firstname` + `lastname` | `firstname` + `lastname` | Same |
|
|
125
|
+
| **Responsive props** | not supported | Tamagui media query props (`$small`, `$medium`, ...) | New capability |
|
|
126
|
+
|
|
127
|
+
## Edge Cases
|
|
128
|
+
|
|
129
|
+
### Conditional `round`
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
// Before
|
|
133
|
+
<Avatar round={isCircle} firstname="Jane" />
|
|
134
|
+
|
|
135
|
+
// After
|
|
136
|
+
<Avatar shape={isCircle ? 'circle' : 'square'} firstname="Jane" />
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Spread props from AvatarProps
|
|
140
|
+
|
|
141
|
+
If components spread `AvatarProps`, destructure the removed props out and convert `round`:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// Before
|
|
145
|
+
const { alt, light, dark, sizeVariant, ...rest } = avatarProps;
|
|
146
|
+
<Avatar {...rest} />;
|
|
147
|
+
|
|
148
|
+
// After
|
|
149
|
+
const { alt, light, dark, sizeVariant, round, ...rest } = avatarProps;
|
|
150
|
+
<Avatar {...rest} shape={round ? 'circle' : 'square'} />;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Wrapper / re-export migration
|
|
154
|
+
|
|
155
|
+
Components that pick or extend `AvatarProps` need type updates:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
// Before
|
|
159
|
+
import type { AvatarProps } from '@ornikar/kitt-universal';
|
|
160
|
+
interface MyAvatarProps extends Pick<AvatarProps, 'src' | 'firstname' | 'lastname'> { ... }
|
|
161
|
+
|
|
162
|
+
// After
|
|
163
|
+
import type { AvatarProps } from '@ornikar/bumper';
|
|
164
|
+
interface MyAvatarProps extends Pick<AvatarProps, 'src' | 'firstname' | 'lastname'> { ... }
|
|
165
|
+
// firstname / lastname narrow from `string | null` to `string` — update callers if any pass null
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For wrappers that pick removed props (`alt`, `light`, `dark`, `sizeVariant`, `round`):
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
// Before
|
|
172
|
+
interface MyProps extends Pick<AvatarProps, 'round' | 'alt'> { ... }
|
|
173
|
+
|
|
174
|
+
// After — these props no longer exist on AvatarProps
|
|
175
|
+
// round → expose `shape` instead
|
|
176
|
+
// alt → drop, or expose only if the wrapper handles it itself
|
|
177
|
+
interface MyProps {
|
|
178
|
+
shape?: 'square' | 'circle';
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Numeric `size` matching the default
|
|
183
|
+
|
|
184
|
+
When the source explicitly sets `size={48}`, prefer removing the prop entirely over emitting `size="large"` — it is the default and reduces churn:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// Before
|
|
188
|
+
<Avatar size={48} firstname="Jane" />
|
|
189
|
+
|
|
190
|
+
// After
|
|
191
|
+
<Avatar firstname="Jane" />
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Migration Rules (machine-readable)
|
|
195
|
+
|
|
196
|
+
Apply these rules in order for each `<Avatar>` usage:
|
|
197
|
+
|
|
198
|
+
1. **Update import**: Replace `'@ornikar/kitt-universal'` with `'@ornikar/bumper'` for `Avatar` and `AvatarProps` imports.
|
|
199
|
+
|
|
200
|
+
2. **Remove unsupported props**: Delete `alt`, `light`, `dark`, `sizeVariant`.
|
|
201
|
+
|
|
202
|
+
3. **Convert `round`**:
|
|
203
|
+
|
|
204
|
+
- `round={true}` or bare `round` → `shape="circle"`
|
|
205
|
+
- `round={false}` → remove the prop
|
|
206
|
+
- `round={expr}` → `shape={expr ? 'circle' : 'square'}`
|
|
207
|
+
|
|
208
|
+
4. **Convert `size`** (numeric literal):
|
|
209
|
+
|
|
210
|
+
- `size={32}` → `size="small"`
|
|
211
|
+
- `size={40}` → `size="medium"`
|
|
212
|
+
- `size={48}` → remove the prop (`large` is the default)
|
|
213
|
+
- `size={64}` → `size="xlarge"`
|
|
214
|
+
- Any other literal → replace with `width={n} height={n}` and remove `size`
|
|
215
|
+
- Variable / expression → flag for manual review
|
|
216
|
+
|
|
217
|
+
5. **Narrow nullable names**:
|
|
218
|
+
- `firstname={null}` literal → remove prop
|
|
219
|
+
- `lastname={null}` literal → remove prop
|
|
220
|
+
- `firstname={value}` where `value` is typed `string | null` → `firstname={value ?? undefined}`
|
|
221
|
+
- Same for `lastname`
|
|
222
|
+
|
|
223
|
+
## Not Migratable (requires human review)
|
|
224
|
+
|
|
225
|
+
- **Dynamic numeric `size`**: When `size` receives a runtime variable (not a literal), the bucket cannot be inferred mechanically. Ask the user how to proceed, or fall back to `width`/`height` if the original value is preserved.
|
|
226
|
+
- **`alt` prop usage**: If the call site relies on `alt` for accessibility, decide whether to wrap the Avatar in an aria-labeled element or accept the regression.
|
|
227
|
+
- **`light` / `dark` props**: If the call site visibly depends on the inverted palette, the surrounding theme context may need to change. Review with design before stripping silently.
|
|
228
|
+
- **Components extending `AvatarProps`**: Proceed with the changes, only ask for user input if the linting is failing.
|
package/package.json
CHANGED
package/src/Bumper.mdx
CHANGED
|
@@ -34,6 +34,7 @@ spacing, color, radius, and typography.
|
|
|
34
34
|
|
|
35
35
|
## Data Displays
|
|
36
36
|
|
|
37
|
+
- [Avatar](?path=/docs/bumper-data-displays-avatar--docs) — User representation with image, initials, or placeholder fallback.
|
|
37
38
|
- [Badge](?path=/docs/bumper-data-displays-badge--docs) — Numeric count or dot indicator.
|
|
38
39
|
|
|
39
40
|
## Loading
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,8 @@ export type { TypographyIconProps } from './system/content/typography/Typography
|
|
|
27
27
|
export type { TypographyLinkProps } from './system/content/typography/TypographyLink';
|
|
28
28
|
|
|
29
29
|
// Data Displays
|
|
30
|
+
export type { AvatarProps } from './system/dataDisplays/Avatar/Avatar';
|
|
31
|
+
export { Avatar } from './system/dataDisplays/Avatar/Avatar';
|
|
30
32
|
export type { BadgeProps } from './system/dataDisplays/Badge/Badge';
|
|
31
33
|
export { Badge } from './system/dataDisplays/Badge/Badge';
|
|
32
34
|
export { Sticker } from './system/dataDisplays/Sticker/Sticker';
|
|
@@ -44,3 +46,7 @@ export { getValueForBreakpoint } from './system/core/breakpoints/utils/breakpoin
|
|
|
44
46
|
// Loader
|
|
45
47
|
export type { LoaderProps } from './system/loading/loader/Loader';
|
|
46
48
|
export { Loader } from './system/loading/loader/Loader';
|
|
49
|
+
|
|
50
|
+
// Layout
|
|
51
|
+
export type { DividerProps } from './system/layout/divider/Divider';
|
|
52
|
+
export { Divider } from './system/layout/divider/Divider';
|
|
@@ -5902,6 +5902,79 @@ exports[`Bumper/Core/Themes Light 1`] = `
|
|
|
5902
5902
|
#FDE4E3
|
|
5903
5903
|
</Text>
|
|
5904
5904
|
</View>
|
|
5905
|
+
<View
|
|
5906
|
+
style={
|
|
5907
|
+
{
|
|
5908
|
+
"alignItems": "center",
|
|
5909
|
+
"flexDirection": "row",
|
|
5910
|
+
"gap": 16,
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5913
|
+
>
|
|
5914
|
+
<View
|
|
5915
|
+
style={
|
|
5916
|
+
{
|
|
5917
|
+
"alignItems": "center",
|
|
5918
|
+
"backgroundColor": "#EAE3D6",
|
|
5919
|
+
"borderBottomColor": "#F1ECE4",
|
|
5920
|
+
"borderBottomLeftRadius": 4,
|
|
5921
|
+
"borderBottomRightRadius": 4,
|
|
5922
|
+
"borderBottomWidth": 1,
|
|
5923
|
+
"borderLeftColor": "#F1ECE4",
|
|
5924
|
+
"borderLeftWidth": 1,
|
|
5925
|
+
"borderRightColor": "#F1ECE4",
|
|
5926
|
+
"borderRightWidth": 1,
|
|
5927
|
+
"borderStyle": "solid",
|
|
5928
|
+
"borderTopColor": "#F1ECE4",
|
|
5929
|
+
"borderTopLeftRadius": 4,
|
|
5930
|
+
"borderTopRightRadius": 4,
|
|
5931
|
+
"borderTopWidth": 1,
|
|
5932
|
+
"height": 48,
|
|
5933
|
+
"justifyContent": "center",
|
|
5934
|
+
"width": 48,
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5937
|
+
/>
|
|
5938
|
+
<View
|
|
5939
|
+
style={
|
|
5940
|
+
{
|
|
5941
|
+
"width": 280,
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
>
|
|
5945
|
+
<Text
|
|
5946
|
+
style={
|
|
5947
|
+
{
|
|
5948
|
+
"color": "#101010",
|
|
5949
|
+
"fontFamily": "GTStandardBold",
|
|
5950
|
+
"fontSize": 14,
|
|
5951
|
+
"letterSpacing": 0.3,
|
|
5952
|
+
"lineHeight": 20,
|
|
5953
|
+
"textAlign": "left",
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
suppressHighlighting={true}
|
|
5957
|
+
>
|
|
5958
|
+
$
|
|
5959
|
+
avatar.bg
|
|
5960
|
+
</Text>
|
|
5961
|
+
</View>
|
|
5962
|
+
<Text
|
|
5963
|
+
style={
|
|
5964
|
+
{
|
|
5965
|
+
"color": "#505050",
|
|
5966
|
+
"fontFamily": "GTStandardRegular",
|
|
5967
|
+
"fontSize": 14,
|
|
5968
|
+
"letterSpacing": 0.3,
|
|
5969
|
+
"lineHeight": 20,
|
|
5970
|
+
"textAlign": "left",
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
suppressHighlighting={true}
|
|
5974
|
+
>
|
|
5975
|
+
#EAE3D6
|
|
5976
|
+
</Text>
|
|
5977
|
+
</View>
|
|
5905
5978
|
</View>
|
|
5906
5979
|
</View>
|
|
5907
5980
|
</RNCSafeAreaProvider>
|
|
@@ -1713,6 +1713,27 @@ exports[`Bumper/Core/Themes Light 1`] = `
|
|
|
1713
1713
|
#FDE4E3
|
|
1714
1714
|
</span>
|
|
1715
1715
|
</div>
|
|
1716
|
+
<div
|
|
1717
|
+
class="is_HStack _dsp-flex _fb-auto _bxs-border-box _pos-relative _minHeight-0px _minWidth-0px _flexShrink-0 _fd-row _gap-t-space-spa1366020313 _alignItems-center"
|
|
1718
|
+
>
|
|
1719
|
+
<div
|
|
1720
|
+
class="_dsp-flex _fd-column _fb-auto _bxs-border-box _pos-relative _minHeight-0px _minWidth-0px _flexShrink-0 _width-t-size-size1385508 _height-t-size-size1385508 _btlr-t-radius-ra1673638410 _btrr-t-radius-ra1673638410 _bbrr-t-radius-ra1673638410 _bblr-t-radius-ra1673638410 _backgroundColor-EAE3D635 _btw-1px _brw-1px _borderBottomWidth-1px _borderLeftWidth-1px _btc-border--bas1360416657 _brc-border--bas1360416657 _borderBottomColor-border--bas1360416657 _borderLeftColor-border--bas1360416657 _alignItems-center _justifyContent-center _borderBottomStyle-solid _borderTopStyle-solid _borderLeftStyle-solid _borderRightStyle-solid"
|
|
1721
|
+
/>
|
|
1722
|
+
<div
|
|
1723
|
+
class="_dsp-flex _alignItems-stretch _fd-column _fb-auto _bxs-border-box _pos-relative _minHeight-0px _minWidth-0px _flexShrink-0 _width-280px"
|
|
1724
|
+
>
|
|
1725
|
+
<span
|
|
1726
|
+
class="font_GTStandard _WebkitFontSmoothing-_platformweb_antialiased _dsp-inline _bxs-border-box _ww-break-word _ws-pre-wrap _marginTop-0px _marginRight-0px _marginBottom-0px _marginLeft-0px _ff-f-family _fs-f-size-body1510 _lh-f-lineHeigh201793153 _ls-f-letterSpa1099960310 _fw-f-weight-bo3448 _col-content--ba907952141 _textAlign-left"
|
|
1727
|
+
>
|
|
1728
|
+
$avatar.bg
|
|
1729
|
+
</span>
|
|
1730
|
+
</div>
|
|
1731
|
+
<span
|
|
1732
|
+
class="font_GTStandard _WebkitFontSmoothing-_platformweb_antialiased _dsp-inline _bxs-border-box _ww-break-word _ws-pre-wrap _marginTop-0px _marginRight-0px _marginBottom-0px _marginLeft-0px _ff-f-family _fs-f-size-body1510 _lh-f-lineHeigh201793153 _ls-f-letterSpa1099960310 _fw-f-weight-re98715119 _textAlign-left _col-content--ba1918259606"
|
|
1733
|
+
>
|
|
1734
|
+
#EAE3D6
|
|
1735
|
+
</span>
|
|
1736
|
+
</div>
|
|
1716
1737
|
</div>
|
|
1717
1738
|
</div>
|
|
1718
1739
|
</span>
|
|
@@ -92,4 +92,7 @@ export const light: Theme = {
|
|
|
92
92
|
'button.bg.danger.pressed': deepPurpleColorPalette['palette.red.1'],
|
|
93
93
|
'button.bg.danger.onContrasted.default': deepPurpleColorPalette['palette.white'],
|
|
94
94
|
'button.bg.danger.onContrasted.pressed': deepPurpleColorPalette['palette.red.1'],
|
|
95
|
+
|
|
96
|
+
// Avatar
|
|
97
|
+
'avatar.bg': deepPurpleColorPalette['palette.beige.3'],
|
|
95
98
|
};
|
|
@@ -102,4 +102,8 @@ export const THEME_BUTTON = [
|
|
|
102
102
|
'button.bg.danger.onContrasted.pressed',
|
|
103
103
|
] as const;
|
|
104
104
|
|
|
105
|
-
export type
|
|
105
|
+
export type ThemeAvatar = Record<(typeof THEME_AVATAR)[number], string>;
|
|
106
|
+
|
|
107
|
+
export const THEME_AVATAR = ['avatar.bg'] as const;
|
|
108
|
+
|
|
109
|
+
export type Theme = ThemeContent & ThemeBackground & ThemeBorder & ThemeDivider & ThemeButton & ThemeAvatar;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { HStack, VStack } from '../../core/primitives/Stack';
|
|
3
|
+
import { Avatar } from './Avatar';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Avatar> = {
|
|
6
|
+
title: 'Bumper/Data Displays/Avatar/Features',
|
|
7
|
+
component: Avatar,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Placeholder: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
size: 'large',
|
|
16
|
+
shape: 'square',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const WithInitials: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
size: 'large',
|
|
23
|
+
shape: 'square',
|
|
24
|
+
firstname: 'John',
|
|
25
|
+
lastname: 'Doe',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const WithImage: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
size: 'large',
|
|
32
|
+
shape: 'square',
|
|
33
|
+
src: 'https://i.pravatar.cc/150?img=5',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Sizes: Story = {
|
|
38
|
+
render: () => (
|
|
39
|
+
<HStack gap="$space.16" alignItems="center">
|
|
40
|
+
<Avatar size="small" shape="square" firstname="John" lastname="Doe" />
|
|
41
|
+
<Avatar size="medium" shape="square" firstname="John" lastname="Doe" />
|
|
42
|
+
<Avatar size="large" shape="square" firstname="John" lastname="Doe" />
|
|
43
|
+
<Avatar size="xlarge" shape="square" firstname="John" lastname="Doe" />
|
|
44
|
+
</HStack>
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const CircleSizes: Story = {
|
|
49
|
+
render: () => (
|
|
50
|
+
<HStack gap="$space.16" alignItems="center">
|
|
51
|
+
<Avatar size="small" shape="circle" firstname="John" lastname="Doe" />
|
|
52
|
+
<Avatar size="medium" shape="circle" firstname="John" lastname="Doe" />
|
|
53
|
+
<Avatar size="large" shape="circle" firstname="John" lastname="Doe" />
|
|
54
|
+
<Avatar size="xlarge" shape="circle" firstname="John" lastname="Doe" />
|
|
55
|
+
</HStack>
|
|
56
|
+
),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Shapes: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<HStack gap="$space.16" alignItems="center">
|
|
62
|
+
<Avatar size="large" shape="square" firstname="John" lastname="Doe" />
|
|
63
|
+
<Avatar size="large" shape="circle" firstname="John" lastname="Doe" />
|
|
64
|
+
</HStack>
|
|
65
|
+
),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Disabled: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<HStack gap="$space.16" alignItems="center">
|
|
71
|
+
<Avatar disabled size="large" shape="square" />
|
|
72
|
+
<Avatar disabled size="large" shape="square" firstname="John" lastname="Doe" />
|
|
73
|
+
<Avatar disabled size="large" shape="square" src="https://i.pravatar.cc/150?img=5" />
|
|
74
|
+
</HStack>
|
|
75
|
+
),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const DisabledShapes: Story = {
|
|
79
|
+
render: () => (
|
|
80
|
+
<HStack gap="$space.16" alignItems="center">
|
|
81
|
+
<Avatar disabled size="large" shape="square" firstname="John" lastname="Doe" />
|
|
82
|
+
<Avatar disabled size="large" shape="circle" firstname="John" lastname="Doe" />
|
|
83
|
+
</HStack>
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const Responsive: Story = {
|
|
88
|
+
render: () => (
|
|
89
|
+
<VStack gap="$space.16">
|
|
90
|
+
<Avatar
|
|
91
|
+
size="small"
|
|
92
|
+
shape="square"
|
|
93
|
+
firstname="John"
|
|
94
|
+
lastname="Doe"
|
|
95
|
+
$medium={{
|
|
96
|
+
size: 'large',
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
<Avatar
|
|
100
|
+
size="large"
|
|
101
|
+
shape="square"
|
|
102
|
+
firstname="John"
|
|
103
|
+
lastname="Doe"
|
|
104
|
+
$medium={{
|
|
105
|
+
shape: 'circle',
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
</VStack>
|
|
109
|
+
),
|
|
110
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Meta, Title, Subtitle, Primary, Controls, Canvas } from '@storybook/blocks';
|
|
2
|
+
import * as AvatarStories from './Avatar.stories';
|
|
3
|
+
import * as AvatarFeatures from './Avatar.features.stories';
|
|
4
|
+
|
|
5
|
+
<Meta of={AvatarStories} />
|
|
6
|
+
<Title />
|
|
7
|
+
<Subtitle />
|
|
8
|
+
|
|
9
|
+
<Primary />
|
|
10
|
+
<Controls />
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
`Avatar` represents a user with an image, initials, or a placeholder fallback. It picks the right content based on which props are passed: `src` wins, then `firstname`/`lastname` fall back to initials, and an empty avatar renders the default placeholder image.
|
|
15
|
+
|
|
16
|
+
## Placeholder
|
|
17
|
+
|
|
18
|
+
When neither `src` nor `firstname`/`lastname` are provided, the avatar shows the default placeholder image. Use this for unknown or anonymous users.
|
|
19
|
+
|
|
20
|
+
<Canvas of={AvatarFeatures.Placeholder} />
|
|
21
|
+
|
|
22
|
+
## Initials
|
|
23
|
+
|
|
24
|
+
Pass `firstname` and `lastname` to display the user's initials (e.g. "JD" for John Doe). Use this when no profile picture is available but the user identity is known.
|
|
25
|
+
|
|
26
|
+
<Canvas of={AvatarFeatures.WithInitials} />
|
|
27
|
+
|
|
28
|
+
## Image
|
|
29
|
+
|
|
30
|
+
Pass `src` to display a user's profile picture. The image is rendered as a background image filling the entire avatar frame, with `overflow: hidden` clipping it to the avatar's shape.
|
|
31
|
+
|
|
32
|
+
<Canvas of={AvatarFeatures.WithImage} />
|
|
33
|
+
|
|
34
|
+
## Sizes
|
|
35
|
+
|
|
36
|
+
The `size` prop controls the avatar's width and height. Defaults to `large`.
|
|
37
|
+
|
|
38
|
+
| Size | Dimensions |
|
|
39
|
+
| -------- | ---------- |
|
|
40
|
+
| `small` | 32 × 32 px |
|
|
41
|
+
| `medium` | 40 × 40 px |
|
|
42
|
+
| `large` | 48 × 48 px |
|
|
43
|
+
| `xlarge` | 64 × 64 px |
|
|
44
|
+
|
|
45
|
+
If you need a custom size, set `width` and `height` directly instead of `size`.
|
|
46
|
+
|
|
47
|
+
<Canvas of={AvatarFeatures.Sizes} />
|
|
48
|
+
|
|
49
|
+
<Canvas of={AvatarFeatures.CircleSizes} />
|
|
50
|
+
|
|
51
|
+
## Shapes
|
|
52
|
+
|
|
53
|
+
The `shape` prop controls the corner radius. Defaults to `square` (uses `$radius.m`). Use `circle` (uses `$radius.circle`) for profile pictures in social or chat contexts where a circular crop is the convention.
|
|
54
|
+
|
|
55
|
+
<Canvas of={AvatarFeatures.Shapes} />
|
|
56
|
+
|
|
57
|
+
## Disabled
|
|
58
|
+
|
|
59
|
+
Set `disabled` to render the avatar in a muted state — the background switches to `$bg.disabled.hi` and content (initials or image) is dimmed. Use this to indicate inactive or unavailable users.
|
|
60
|
+
|
|
61
|
+
<Canvas of={AvatarFeatures.Disabled} />
|
|
62
|
+
|
|
63
|
+
<Canvas of={AvatarFeatures.DisabledShapes} />
|
|
64
|
+
|
|
65
|
+
## Responsive
|
|
66
|
+
|
|
67
|
+
All props accept Tamagui media query overrides (`$small`, `$medium`, `$large`, `$wide`). Use them to adapt size or shape per breakpoint without rendering multiple avatars.
|
|
68
|
+
|
|
69
|
+
<Canvas of={AvatarFeatures.Responsive} />
|
|
70
|
+
|
|
71
|
+
## Related
|
|
72
|
+
|
|
73
|
+
- [Badge](?path=/docs/bumper-data-displays-badge--docs)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Avatar } from './Avatar';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Avatar> = {
|
|
5
|
+
title: 'Bumper/Data Displays/Avatar',
|
|
6
|
+
component: Avatar,
|
|
7
|
+
argTypes: {
|
|
8
|
+
size: {
|
|
9
|
+
control: 'select',
|
|
10
|
+
options: ['small', 'medium', 'large', 'xlarge'],
|
|
11
|
+
description: 'The size of the avatar.',
|
|
12
|
+
},
|
|
13
|
+
shape: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['square', 'circle'],
|
|
16
|
+
description: 'The shape of the avatar.',
|
|
17
|
+
},
|
|
18
|
+
src: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'URL of the image to display.',
|
|
21
|
+
},
|
|
22
|
+
firstname: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'First name used to generate initials.',
|
|
25
|
+
},
|
|
26
|
+
lastname: {
|
|
27
|
+
control: 'text',
|
|
28
|
+
description: 'Last name used to generate initials.',
|
|
29
|
+
},
|
|
30
|
+
disabled: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Whether the avatar is disabled.',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|
|
38
|
+
type Story = StoryObj<typeof meta>;
|
|
39
|
+
|
|
40
|
+
export const Default: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
size: 'large',
|
|
43
|
+
shape: 'square',
|
|
44
|
+
firstname: 'John',
|
|
45
|
+
lastname: 'Doe',
|
|
46
|
+
},
|
|
47
|
+
};
|