@startupjs-ui/avatar 0.1.3
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 +20 -0
- package/README.mdx +152 -0
- package/index.cssx.styl +62 -0
- package/index.d.ts +25 -0
- package/index.tsx +105 -0
- package/package.json +22 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @startupjs-ui/avatar
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
|
|
20
|
+
* **avatar:** refactor Avatar component ([768b74f](https://github.com/startupjs/startupjs-ui/commit/768b74f47dde3b7fcfe2804d340c69fb31ab7ce0))
|
package/README.mdx
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
import { pug, styl } from 'startupjs'
|
|
3
|
+
import Avatar, { _PropsJsonSchema as AvatarPropsJsonSchema } from './index'
|
|
4
|
+
import Div from '@startupjs-ui/div'
|
|
5
|
+
import Span from '@startupjs-ui/span'
|
|
6
|
+
import Br from '@startupjs-ui/br'
|
|
7
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
8
|
+
|
|
9
|
+
# Avatar
|
|
10
|
+
|
|
11
|
+
Avatars can be used to represent people. When used with a specific logo, avatars can also be used to represent a brand. They also can be a placeholder when there is no image or image can't be loaded by some reason to be shown, showing an alternative in this case.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { Avatar } from 'startupjs-ui'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Simple example
|
|
18
|
+
|
|
19
|
+
```jsx example
|
|
20
|
+
return pug`
|
|
21
|
+
Avatar(src='/img/avatar1.jpeg')
|
|
22
|
+
`
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Sizes
|
|
26
|
+
Size is `m` by default. It can be changed by changing the `size` property by setting it to one of the values ('s', 'm', 'l') or by specifying the size in pixels.
|
|
27
|
+
|
|
28
|
+
```jsx example
|
|
29
|
+
return pug`
|
|
30
|
+
Div(row)
|
|
31
|
+
Div
|
|
32
|
+
Avatar(
|
|
33
|
+
size='s'
|
|
34
|
+
src='/img/avatar2.jpeg'
|
|
35
|
+
)
|
|
36
|
+
Div(pushed)
|
|
37
|
+
Avatar(
|
|
38
|
+
size='m'
|
|
39
|
+
src='/img/avatar3.jpeg'
|
|
40
|
+
)
|
|
41
|
+
Div(pushed)
|
|
42
|
+
Avatar(
|
|
43
|
+
size='l'
|
|
44
|
+
src='/img/avatar1.jpeg'
|
|
45
|
+
)
|
|
46
|
+
Div(pushed)
|
|
47
|
+
Avatar(
|
|
48
|
+
size=60
|
|
49
|
+
src='/img/avatar3.jpeg'
|
|
50
|
+
)
|
|
51
|
+
`
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## User status
|
|
55
|
+
Avatars can be used to display user online status. It can be set by passing string `online` or `away` to property `status`.
|
|
56
|
+
|
|
57
|
+
```jsx example
|
|
58
|
+
return pug`
|
|
59
|
+
Div(row)
|
|
60
|
+
Div
|
|
61
|
+
Avatar(
|
|
62
|
+
status='online'
|
|
63
|
+
src='/img/avatar1.jpeg'
|
|
64
|
+
)
|
|
65
|
+
Div(pushed)
|
|
66
|
+
Avatar(
|
|
67
|
+
status='away'
|
|
68
|
+
src='/img/avatar2.jpeg'
|
|
69
|
+
)
|
|
70
|
+
`
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Also you can provide custom components for the status icon using `statusComponents` prop.
|
|
74
|
+
|
|
75
|
+
```jsx example
|
|
76
|
+
const AbsentStatus = useCallback(({ style }) => (
|
|
77
|
+
<Div style={style} styleName='absent'>
|
|
78
|
+
<Div styleName='absent-line absent-line-1' />
|
|
79
|
+
<Div styleName='absent-line absent-line-2' />
|
|
80
|
+
</Div>
|
|
81
|
+
))
|
|
82
|
+
|
|
83
|
+
return pug`
|
|
84
|
+
Avatar(
|
|
85
|
+
pushed
|
|
86
|
+
src='/img/avatar2.jpeg'
|
|
87
|
+
status='absent'
|
|
88
|
+
statusComponents={
|
|
89
|
+
absent: AbsentStatus
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
`
|
|
93
|
+
|
|
94
|
+
styl`
|
|
95
|
+
.absent
|
|
96
|
+
background-color white
|
|
97
|
+
justify-content center
|
|
98
|
+
&-line
|
|
99
|
+
position absolute
|
|
100
|
+
height 2px
|
|
101
|
+
left 0
|
|
102
|
+
right 0
|
|
103
|
+
background-color red
|
|
104
|
+
&-1
|
|
105
|
+
transform rotate(45deg)
|
|
106
|
+
&-2
|
|
107
|
+
transform rotate(-45deg)
|
|
108
|
+
`
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Fallback string
|
|
112
|
+
If `url` prop is not provided or if there is an error loading the avatar image, the component using an alternative. The alternative is initials of two first words of `children` string if provided and `?` if not. In the example below alternative is 'John Doe' therefore 'JD' will be displayed.
|
|
113
|
+
|
|
114
|
+
```jsx example
|
|
115
|
+
return pug`
|
|
116
|
+
Div(row)
|
|
117
|
+
Div
|
|
118
|
+
Avatar(src='/img/non-existen-image.jpeg')
|
|
119
|
+
Div(pushed)
|
|
120
|
+
Avatar(
|
|
121
|
+
src='/img/non-existen-image.jpeg'
|
|
122
|
+
) John Doe
|
|
123
|
+
`
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Actions
|
|
127
|
+
|
|
128
|
+
Passed handler to `onPress` property will be called when the user taps the component.
|
|
129
|
+
|
|
130
|
+
```jsx example
|
|
131
|
+
const [counter, setCounter] = useState(0)
|
|
132
|
+
|
|
133
|
+
return pug`
|
|
134
|
+
Div
|
|
135
|
+
Avatar(
|
|
136
|
+
src='/img/avatar1.jpeg'
|
|
137
|
+
onPress=()=> setCounter(counter + 1)
|
|
138
|
+
)
|
|
139
|
+
Br
|
|
140
|
+
Span= 'Clicked ' + counter + ' times'
|
|
141
|
+
`
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Sandbox
|
|
145
|
+
|
|
146
|
+
<Sandbox
|
|
147
|
+
Component={Avatar}
|
|
148
|
+
propsJsonSchema={AvatarPropsJsonSchema}
|
|
149
|
+
props={{
|
|
150
|
+
onPress: () => alert('"onPress" event on "Avatar" component'),
|
|
151
|
+
}}
|
|
152
|
+
/>
|
package/index.cssx.styl
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
$this = merge(
|
|
2
|
+
{
|
|
3
|
+
avatarSizes: {
|
|
4
|
+
s: 4u,
|
|
5
|
+
m: 5u,
|
|
6
|
+
l: 6u,
|
|
7
|
+
},
|
|
8
|
+
statusSizes: {
|
|
9
|
+
s: 1.25u,
|
|
10
|
+
m: 1.5u,
|
|
11
|
+
l: 1.75u,
|
|
12
|
+
},
|
|
13
|
+
fallbackSizes: {
|
|
14
|
+
s: 1.5u,
|
|
15
|
+
m: 1.75u,
|
|
16
|
+
l: 2u,
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
$UI.Avatar,
|
|
20
|
+
true
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
.root
|
|
24
|
+
+web()
|
|
25
|
+
user-select none
|
|
26
|
+
|
|
27
|
+
.avatarWrapper
|
|
28
|
+
height 100%
|
|
29
|
+
overflow hidden
|
|
30
|
+
|
|
31
|
+
.avatar
|
|
32
|
+
justify-content center
|
|
33
|
+
align-items center
|
|
34
|
+
height 100%
|
|
35
|
+
|
|
36
|
+
.fallback
|
|
37
|
+
color var(--color-text-on-color)
|
|
38
|
+
|
|
39
|
+
.status
|
|
40
|
+
position absolute
|
|
41
|
+
z-index 1
|
|
42
|
+
border-width 2px
|
|
43
|
+
border-color var(--color-border-main-strong)
|
|
44
|
+
radius(circle)
|
|
45
|
+
|
|
46
|
+
&.circle
|
|
47
|
+
right 0
|
|
48
|
+
bottom 0
|
|
49
|
+
|
|
50
|
+
&.squared
|
|
51
|
+
&.rounded
|
|
52
|
+
bottom -0.25u
|
|
53
|
+
right @bottom
|
|
54
|
+
|
|
55
|
+
&.online
|
|
56
|
+
background-color var(--color-bg-success)
|
|
57
|
+
|
|
58
|
+
&.away
|
|
59
|
+
background-color var(--color-bg-warning)
|
|
60
|
+
|
|
61
|
+
:export
|
|
62
|
+
config: $this
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import { type ComponentType } from 'react';
|
|
5
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
6
|
+
import { type DivProps } from '@startupjs-ui/div';
|
|
7
|
+
declare const _default: ComponentType<AvatarProps>;
|
|
8
|
+
export default _default;
|
|
9
|
+
export declare const _PropsJsonSchema: {};
|
|
10
|
+
export interface AvatarProps extends DivProps {
|
|
11
|
+
/** Custom styles applied to the root view */
|
|
12
|
+
style?: StyleProp<ViewStyle>;
|
|
13
|
+
/** Avatar image source URL */
|
|
14
|
+
src?: string;
|
|
15
|
+
/** Size preset or explicit pixel value @default 'm' */
|
|
16
|
+
size?: 's' | 'm' | 'l' | number;
|
|
17
|
+
/** Status indicator name */
|
|
18
|
+
status?: 'online' | 'away' | string;
|
|
19
|
+
/** Avatar shape variant @default 'circle' */
|
|
20
|
+
shape?: DivProps['shape'];
|
|
21
|
+
/** Text used to build fallback initials @default '?' */
|
|
22
|
+
children?: string;
|
|
23
|
+
/** Custom components for status indicators keyed by status */
|
|
24
|
+
statusComponents?: Record<string, ComponentType<any>>;
|
|
25
|
+
}
|
package/index.tsx
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useState, type ComponentType, type ReactNode } from 'react'
|
|
2
|
+
import { Image, type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer, useDidUpdate } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import Div, { type DivProps } from '@startupjs-ui/div'
|
|
6
|
+
import Span from '@startupjs-ui/span'
|
|
7
|
+
import randomcolor from 'randomcolor'
|
|
8
|
+
import STYLES from './index.cssx.styl'
|
|
9
|
+
|
|
10
|
+
const { config } = STYLES
|
|
11
|
+
|
|
12
|
+
const DEFAULT_STATUSES = ['online', 'away']
|
|
13
|
+
|
|
14
|
+
export default observer(themed('Avatar', Avatar))
|
|
15
|
+
|
|
16
|
+
export const _PropsJsonSchema = {/* AvatarProps */}
|
|
17
|
+
|
|
18
|
+
export interface AvatarProps extends DivProps {
|
|
19
|
+
/** Custom styles applied to the root view */
|
|
20
|
+
style?: StyleProp<ViewStyle>
|
|
21
|
+
/** Avatar image source URL */
|
|
22
|
+
src?: string
|
|
23
|
+
/** Size preset or explicit pixel value @default 'm' */
|
|
24
|
+
size?: 's' | 'm' | 'l' | number
|
|
25
|
+
/** Status indicator name */
|
|
26
|
+
status?: 'online' | 'away' | string
|
|
27
|
+
/** Avatar shape variant @default 'circle' */
|
|
28
|
+
shape?: DivProps['shape']
|
|
29
|
+
/** Text used to build fallback initials @default '?' */
|
|
30
|
+
children?: string
|
|
31
|
+
/** Custom components for status indicators keyed by status */
|
|
32
|
+
statusComponents?: Record<string, ComponentType<any>>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Avatar ({
|
|
36
|
+
src,
|
|
37
|
+
size = 'm',
|
|
38
|
+
status,
|
|
39
|
+
shape = 'circle',
|
|
40
|
+
children = '?',
|
|
41
|
+
statusComponents,
|
|
42
|
+
disabled = false,
|
|
43
|
+
...props
|
|
44
|
+
}: AvatarProps): ReactNode {
|
|
45
|
+
const [error, setError] = useState<boolean>()
|
|
46
|
+
useDidUpdate(() => {
|
|
47
|
+
setError(undefined)
|
|
48
|
+
}, [src])
|
|
49
|
+
|
|
50
|
+
const _size = config.avatarSizes?.[size] ?? size
|
|
51
|
+
const _rootStyle = { width: _size, height: _size }
|
|
52
|
+
const _statusSize = config.statusSizes?.[size] ?? Math.round(Number(size) / 4)
|
|
53
|
+
const _statusStyle = { width: _statusSize, height: _statusSize }
|
|
54
|
+
const _fallbackFontSize = config.fallbackSizes?.[size] ?? Math.round(Number(size) / 2.5)
|
|
55
|
+
const _fallbackStyle = { fontSize: _fallbackFontSize, lineHeight: _fallbackFontSize }
|
|
56
|
+
|
|
57
|
+
const StatusComponent = getStatusComponent(statusComponents, status)
|
|
58
|
+
|
|
59
|
+
return pug`
|
|
60
|
+
Div.root(
|
|
61
|
+
part='root'
|
|
62
|
+
style=_rootStyle
|
|
63
|
+
disabled=disabled
|
|
64
|
+
...props
|
|
65
|
+
)
|
|
66
|
+
Div.avatarWrapper(shape=shape)
|
|
67
|
+
if src && !error
|
|
68
|
+
Image.avatar(
|
|
69
|
+
source={ uri: src }
|
|
70
|
+
onError=() => {
|
|
71
|
+
setError(true)
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
else
|
|
75
|
+
- const _fallback = children.trim()
|
|
76
|
+
- const [firstName, lastName] = _fallback.split(' ')
|
|
77
|
+
- const initials = (firstName ? firstName[0].toUpperCase() : '') + (lastName ? lastName[0].toUpperCase() : '')
|
|
78
|
+
Div.avatar(
|
|
79
|
+
style={backgroundColor: randomcolor({
|
|
80
|
+
luminosity: 'bright',
|
|
81
|
+
seed: _fallback
|
|
82
|
+
})}
|
|
83
|
+
)
|
|
84
|
+
Span.fallback(part='fallback' bold style=_fallbackStyle)
|
|
85
|
+
= initials
|
|
86
|
+
if status
|
|
87
|
+
StatusComponent.status(part='status' styleName=[status, shape] style=_statusStyle)
|
|
88
|
+
`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getStatusComponent (
|
|
92
|
+
statusComponents?: AvatarProps['statusComponents'],
|
|
93
|
+
status?: AvatarProps['status']
|
|
94
|
+
) {
|
|
95
|
+
if (!status) return Div
|
|
96
|
+
|
|
97
|
+
if (!DEFAULT_STATUSES.includes(status) && !statusComponents?.[status]) {
|
|
98
|
+
console.error(
|
|
99
|
+
"[@dmapper/ui -> Avatar] Custom component for status '" +
|
|
100
|
+
status +
|
|
101
|
+
"' is not specified. Use 'statusComponents' to specify it."
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
return statusComponents?.[status] ?? Div
|
|
105
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/avatar",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"main": "index.tsx",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@startupjs-ui/core": "^0.1.3",
|
|
12
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
13
|
+
"@startupjs-ui/span": "^0.1.3",
|
|
14
|
+
"randomcolor": "^0.5.4"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": "*",
|
|
18
|
+
"react-native": "*",
|
|
19
|
+
"startupjs": "*"
|
|
20
|
+
},
|
|
21
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
22
|
+
}
|