@duro-app/ui 5.34.0 → 5.34.2
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/dist/components/Avatar/Avatar.d.ts +5 -17
- package/dist/components/Avatar/Avatar.d.ts.map +1 -1
- package/dist/components/Avatar/Avatar.meta.d.ts.map +1 -1
- package/dist/components/Avatar/Avatar.stories.d.ts +2 -0
- package/dist/components/Avatar/Avatar.stories.d.ts.map +1 -1
- package/dist/components/Avatar/types.d.ts +21 -0
- package/dist/components/Avatar/types.d.ts.map +1 -0
- package/dist/components/Icon/Icon.stories.d.ts.map +1 -1
- package/dist/components/Icon/phosphor-glyphs.d.ts +584 -0
- package/dist/components/Icon/phosphor-glyphs.d.ts.map +1 -1
- package/dist/index.js +3471 -2879
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Avatar/Avatar.meta.ts +2 -1
- package/src/components/Avatar/Avatar.native.tsx +37 -0
- package/src/components/Avatar/Avatar.stories.tsx +13 -0
- package/src/components/Avatar/Avatar.tsx +16 -18
- package/src/components/Avatar/types.ts +21 -0
- package/src/components/Icon/Icon.stories.tsx +6 -0
- package/src/components/Icon/phosphor-glyphs.ts +806 -81
- package/src/components/Icon/phosphor-names.json +73 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duro-app/ui",
|
|
3
|
-
"version": "5.34.
|
|
3
|
+
"version": "5.34.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"dependencies": {
|
|
93
93
|
"@tanstack/react-virtual": "^3.14.13",
|
|
94
94
|
"qrcode": "^1.5.4",
|
|
95
|
-
"@duro-app/tokens": "^5.34.
|
|
95
|
+
"@duro-app/tokens": "^5.34.2"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@babel/preset-typescript": "^7.28.0",
|
|
@@ -2,7 +2,7 @@ import type {ComponentMeta} from '../component-meta'
|
|
|
2
2
|
|
|
3
3
|
export const meta: ComponentMeta = {
|
|
4
4
|
description:
|
|
5
|
-
'A person or entity as a picture, else their initials, else a neutral placeholder. alt is required and is the accessible name in all three states. sm, md and lg map to the controlSm, thumbSm and iconXxl size tokens; shape is circle or square.
|
|
5
|
+
'A person or entity as a picture, else their initials, else a neutral placeholder. alt is required and is the accessible name in all three states. sm, md and lg map to the controlSm, thumbSm and iconXxl size tokens; shape is circle or square. svg takes a complete SVG document string generated by the app (never user-supplied markup) and draws it filling the frame. Precedence: src (until it fails to load) > svg > initials > blank.',
|
|
6
6
|
whenToUse: [
|
|
7
7
|
'The owner, author or assignee of a record, beside their name',
|
|
8
8
|
'A List.Leading slot for people or accounts',
|
|
@@ -25,6 +25,7 @@ export const meta: ComponentMeta = {
|
|
|
25
25
|
],
|
|
26
26
|
example: `<Inline gap="sm" align="center">
|
|
27
27
|
<Avatar src="/people/ada.png" alt="Ada Lovelace" />
|
|
28
|
+
<Avatar alt="Generated" svg={generatedArt} />
|
|
28
29
|
<Avatar alt="Grace Hopper" initials="GH" size="lg" />
|
|
29
30
|
<Avatar alt="Unassigned" shape="square" />
|
|
30
31
|
</Inline>`,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {useState} from 'react'
|
|
2
|
+
import {html} from 'react-strict-dom'
|
|
3
|
+
import {SvgXml} from 'react-native-svg'
|
|
4
|
+
import {styles} from './styles.css'
|
|
5
|
+
import type {AvatarProps} from './types'
|
|
6
|
+
|
|
7
|
+
export type {AvatarProps, AvatarShape, AvatarSize} from './types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Native Avatar: the web's precedence (src until it fails, svg, initials, blank). The svg document
|
|
11
|
+
* is drawn with react-native-svg's SvgXml, since RSD has no `svg`.
|
|
12
|
+
*/
|
|
13
|
+
export function Avatar({src, svg, alt, initials, size = 'md', shape = 'circle'}: AvatarProps) {
|
|
14
|
+
const [failedSrc, setFailedSrc] = useState<string | null>(null)
|
|
15
|
+
const showImage = src !== undefined && src !== failedSrc
|
|
16
|
+
const frame = [styles.base, styles[size], styles[shape]]
|
|
17
|
+
|
|
18
|
+
if (showImage) {
|
|
19
|
+
return (
|
|
20
|
+
<html.div style={frame}>
|
|
21
|
+
<html.img src={src} alt={alt} onError={() => setFailedSrc(src)} style={styles.image} />
|
|
22
|
+
</html.div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
if (svg !== undefined) {
|
|
26
|
+
return (
|
|
27
|
+
<html.div role="img" aria-label={alt} style={frame}>
|
|
28
|
+
<SvgXml xml={svg} width="100%" height="100%" />
|
|
29
|
+
</html.div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
return (
|
|
33
|
+
<html.div role="img" aria-label={alt} style={frame}>
|
|
34
|
+
{initials ? <html.span aria-hidden={true}>{initials}</html.span> : null}
|
|
35
|
+
</html.div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -77,3 +77,16 @@ export const BrokenImage: Story = {
|
|
|
77
77
|
})
|
|
78
78
|
},
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
const ART =
|
|
82
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 1"><rect width="1" height="1" fill="#c33"/><rect x="1" width="1" height="1" fill="#36c"/></svg>'
|
|
83
|
+
|
|
84
|
+
/** An app-generated SVG document fills the frame when there is no loadable src. */
|
|
85
|
+
export const GeneratedSvg: Story = {
|
|
86
|
+
args: {svg: ART, alt: 'Generated art'},
|
|
87
|
+
play: async ({canvas}) => {
|
|
88
|
+
const image = canvas.getByRole('img', {name: 'Generated art'})
|
|
89
|
+
await expect(image.tagName).toBe('IMG')
|
|
90
|
+
await expect(image.getAttribute('src')).toMatch(/^data:image\/svg\+xml/)
|
|
91
|
+
},
|
|
92
|
+
}
|
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
import {useState} from 'react'
|
|
2
2
|
import {html} from 'react-strict-dom'
|
|
3
3
|
import {styles} from './styles.css'
|
|
4
|
+
import type {AvatarProps} from './types'
|
|
4
5
|
|
|
5
|
-
export type
|
|
6
|
-
export type AvatarShape = 'circle' | 'square'
|
|
7
|
-
|
|
8
|
-
export interface AvatarProps {
|
|
9
|
-
/** Image URL. Without it, or when it fails to load, the initials show. */
|
|
10
|
-
src?: string
|
|
11
|
-
/** The accessible name: who or what this avatar stands for. Never empty. */
|
|
12
|
-
alt: string
|
|
13
|
-
/** Shown when there is no image; one or two characters. */
|
|
14
|
-
initials?: string
|
|
15
|
-
/** sm 28, md 40, lg 48 (the controlSm, thumbSm and iconXxl size tokens). Default md. */
|
|
16
|
-
size?: AvatarSize
|
|
17
|
-
/** Default circle. */
|
|
18
|
-
shape?: AvatarShape
|
|
19
|
-
}
|
|
6
|
+
export type {AvatarProps, AvatarShape, AvatarSize} from './types'
|
|
20
7
|
|
|
21
8
|
/**
|
|
22
|
-
* A person or entity as a picture, else their initials, else a neutral
|
|
23
|
-
*
|
|
9
|
+
* A person or entity as a picture, else an app-generated SVG, else their initials, else a neutral
|
|
10
|
+
* blank. Each carries `alt` as the accessible name.
|
|
24
11
|
*/
|
|
25
|
-
export function Avatar({src, alt, initials, size = 'md', shape = 'circle'}: AvatarProps) {
|
|
12
|
+
export function Avatar({src, svg, alt, initials, size = 'md', shape = 'circle'}: AvatarProps) {
|
|
26
13
|
const [failedSrc, setFailedSrc] = useState<string | null>(null)
|
|
27
14
|
const showImage = src !== undefined && src !== failedSrc
|
|
28
15
|
const frame = [styles.base, styles[size], styles[shape]]
|
|
@@ -34,6 +21,17 @@ export function Avatar({src, alt, initials, size = 'md', shape = 'circle'}: Avat
|
|
|
34
21
|
</html.div>
|
|
35
22
|
)
|
|
36
23
|
}
|
|
24
|
+
if (svg !== undefined) {
|
|
25
|
+
return (
|
|
26
|
+
<html.div style={frame}>
|
|
27
|
+
<html.img
|
|
28
|
+
src={`data:image/svg+xml,${encodeURIComponent(svg)}`}
|
|
29
|
+
alt={alt}
|
|
30
|
+
style={styles.image}
|
|
31
|
+
/>
|
|
32
|
+
</html.div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
37
35
|
return (
|
|
38
36
|
<html.div role="img" aria-label={alt} style={frame}>
|
|
39
37
|
{initials ? <html.span aria-hidden={true}>{initials}</html.span> : null}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AvatarSize = 'sm' | 'md' | 'lg'
|
|
2
|
+
export type AvatarShape = 'circle' | 'square'
|
|
3
|
+
|
|
4
|
+
export interface AvatarProps {
|
|
5
|
+
/** Image URL. Without it, or when it fails to load, the svg, else the initials, show. */
|
|
6
|
+
src?: string
|
|
7
|
+
/** The accessible name: who or what this avatar stands for. Never empty. */
|
|
8
|
+
alt: string
|
|
9
|
+
/**
|
|
10
|
+
* A complete SVG document string generated by the app (for example id-seeded art), drawn as the
|
|
11
|
+
* picture, filling the frame. Never user-supplied markup. Used when there is no `src`, or `src`
|
|
12
|
+
* failed to load; it wins over the initials.
|
|
13
|
+
*/
|
|
14
|
+
svg?: string
|
|
15
|
+
/** Shown when there is no image or svg; one or two characters. */
|
|
16
|
+
initials?: string
|
|
17
|
+
/** sm 28, md 40, lg 48 (the controlSm, thumbSm and iconXxl size tokens). Default md. */
|
|
18
|
+
size?: AvatarSize
|
|
19
|
+
/** Default circle. */
|
|
20
|
+
shape?: AvatarShape
|
|
21
|
+
}
|
|
@@ -2,6 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react'
|
|
|
2
2
|
import {css, html} from 'react-strict-dom'
|
|
3
3
|
import {Icon} from './Icon'
|
|
4
4
|
import type {IconName} from './Icon'
|
|
5
|
+
import {PHOSPHOR_GLYPHS} from './phosphor-glyphs'
|
|
5
6
|
import {spacing} from '@duro-app/tokens/tokens/spacing.css'
|
|
6
7
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
7
8
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
@@ -69,6 +70,11 @@ const groups: ReadonlyArray<{title: string; names: readonly IconName[]}> = [
|
|
|
69
70
|
'lock-filled',
|
|
70
71
|
],
|
|
71
72
|
},
|
|
73
|
+
// Generated from @phosphor-icons/core (scripts/generate-phosphor-icons.mjs), alphabetical.
|
|
74
|
+
{
|
|
75
|
+
title: 'Phosphor',
|
|
76
|
+
names: (Object.keys(PHOSPHOR_GLYPHS) as IconName[]).sort(),
|
|
77
|
+
},
|
|
72
78
|
]
|
|
73
79
|
|
|
74
80
|
const styles = css.create({
|