@carrier-dpx/air-react-library 0.7.20 → 0.7.22
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/package.json +1 -1
- package/src/components/Avatar/Avatar.figma.tsx +105 -0
- package/src/components/Avatar/Avatar.tsx +111 -0
- package/src/components/Avatar/AvatarGroup.figma.tsx +60 -0
- package/src/components/Avatar/AvatarGroup.tsx +45 -0
- package/src/components/Avatar/index.ts +9 -0
- package/src/components/utils/DataDisplayUtils.ts +24 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Code Connect Configuration for Avatar Component
|
|
3
|
+
*
|
|
4
|
+
* Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6587-47403
|
|
5
|
+
*
|
|
6
|
+
* Figma Properties:
|
|
7
|
+
* - variant (circle, rounded)
|
|
8
|
+
* - size (large: 48px, medium: 40px, small: 32px, xsmall: 24px, micro: 20px)
|
|
9
|
+
* - type (image, letter, icon)
|
|
10
|
+
* - ✏️ Letter (text property) - layer name "Letter"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import figma from "@figma/code-connect";
|
|
14
|
+
import Avatar from "./Avatar";
|
|
15
|
+
import Icon from "../Icon";
|
|
16
|
+
|
|
17
|
+
figma.connect(
|
|
18
|
+
Avatar,
|
|
19
|
+
"https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6587-47403",
|
|
20
|
+
{
|
|
21
|
+
props: {
|
|
22
|
+
/**
|
|
23
|
+
* VARIANT MAPPING
|
|
24
|
+
* Maps Figma's "variant" property to React's "variant" prop
|
|
25
|
+
* Figma: circle → React: "circular"
|
|
26
|
+
* Figma: rounded → React: "rounded"
|
|
27
|
+
*/
|
|
28
|
+
variant: figma.enum("variant", {
|
|
29
|
+
circle: "circular",
|
|
30
|
+
rounded: "rounded",
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* SIZE MAPPING
|
|
35
|
+
* Maps Figma's "size" property to React's "size" prop
|
|
36
|
+
* Figma: large (48px), medium (40px), small (32px), xsmall (24px), micro (20px)
|
|
37
|
+
*/
|
|
38
|
+
size: figma.enum("size", {
|
|
39
|
+
large: "large",
|
|
40
|
+
medium: "medium",
|
|
41
|
+
small: "small",
|
|
42
|
+
xsmall: "xsmall",
|
|
43
|
+
micro: "micro",
|
|
44
|
+
}),
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* TYPE MAPPING
|
|
48
|
+
* Maps Figma's "type" property to determine how Avatar is rendered
|
|
49
|
+
* - image: uses src prop
|
|
50
|
+
* - letter: uses children with text from "✏️ Letter"
|
|
51
|
+
* - icon: uses children with Icon component
|
|
52
|
+
*/
|
|
53
|
+
type: figma.enum("type", {
|
|
54
|
+
image: "image",
|
|
55
|
+
letter: "letter",
|
|
56
|
+
icon: "icon",
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* LETTER TEXT
|
|
61
|
+
* Maps text property "✏️ Letter" from layer "Letter"
|
|
62
|
+
* Used when type="letter"
|
|
63
|
+
*/
|
|
64
|
+
letter: figma.string("✏️ Letter"),
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* ICON NESTED PROPERTIES
|
|
68
|
+
* Access Icon's fontSize and SVG through nested properties
|
|
69
|
+
* Used when type="icon"
|
|
70
|
+
*/
|
|
71
|
+
icon: figma.nestedProps("Icon", {
|
|
72
|
+
fontSize: figma.enum("fontSize", {
|
|
73
|
+
large: "large",
|
|
74
|
+
medium: "medium",
|
|
75
|
+
small: "small",
|
|
76
|
+
xsmall: "xsmall",
|
|
77
|
+
}),
|
|
78
|
+
children: figma.instance("SVG"),
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* EXAMPLE CODE TEMPLATE
|
|
84
|
+
* Shows how Avatar should be used based on type
|
|
85
|
+
*/
|
|
86
|
+
example: ({ variant, size, type, letter, icon }) => {
|
|
87
|
+
if (type === "icon") {
|
|
88
|
+
return (
|
|
89
|
+
<Avatar variant={variant} size={size}>
|
|
90
|
+
<Icon fontSize={icon.fontSize}>
|
|
91
|
+
{icon.children}
|
|
92
|
+
</Icon>
|
|
93
|
+
</Avatar>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Default to letter type (or image if src is provided via sx)
|
|
98
|
+
return (
|
|
99
|
+
<Avatar variant={variant} size={size}>
|
|
100
|
+
{letter}
|
|
101
|
+
</Avatar>
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
|
|
3
|
+
import MuiAvatar, { AvatarProps as MuiAvatarProps } from "@mui/material/Avatar";
|
|
4
|
+
import { blueGrey } from "@mui/material/colors";
|
|
5
|
+
import { CSSObject, styled } from "@mui/material/styles";
|
|
6
|
+
|
|
7
|
+
import { styleTokens } from "../theme/constants/styleTokens";
|
|
8
|
+
import { getDisplayStringForAvatar } from "../utils/DataDisplayUtils";
|
|
9
|
+
import { getSxStyles } from "../utils/styles";
|
|
10
|
+
|
|
11
|
+
export interface AvatarProps extends MuiAvatarProps {
|
|
12
|
+
variant?: "circular" | "rounded";
|
|
13
|
+
/**
|
|
14
|
+
* Size of the Avatar
|
|
15
|
+
* - large: 48px
|
|
16
|
+
* - medium: 40px
|
|
17
|
+
* - small: 32px
|
|
18
|
+
* - xsmall: 24px
|
|
19
|
+
* - micro: 20px
|
|
20
|
+
*/
|
|
21
|
+
size?: "large" | "medium" | "small" | "xsmall" | "micro";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The Avatar component represents a unique user or entity through a custom image, initials or icon.
|
|
25
|
+
*
|
|
26
|
+
* // Default import
|
|
27
|
+
* import Avatar from '@carrier-dpx/air-react-library/Avatar'
|
|
28
|
+
*
|
|
29
|
+
* // Named import
|
|
30
|
+
* import { Avatar } from '@carrier-dpx/air-react-library'
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const MuiAvatarStyled = styled(MuiAvatar)({
|
|
34
|
+
"&.MuiAvatar-rounded": {
|
|
35
|
+
borderRadius: styleTokens.borderRadius.large,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const Avatar = forwardRef<HTMLDivElement, AvatarProps>(
|
|
40
|
+
({ children, sx, size = "medium", ...rest }, ref) => {
|
|
41
|
+
const getSize = () => {
|
|
42
|
+
switch (size) {
|
|
43
|
+
case "large":
|
|
44
|
+
return "48px";
|
|
45
|
+
case "medium":
|
|
46
|
+
return "40px";
|
|
47
|
+
case "small":
|
|
48
|
+
return "32px";
|
|
49
|
+
case "xsmall":
|
|
50
|
+
return "24px";
|
|
51
|
+
case "micro":
|
|
52
|
+
return "20px";
|
|
53
|
+
default:
|
|
54
|
+
return "40px";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const getFontSize = () => {
|
|
59
|
+
switch (size) {
|
|
60
|
+
case "large":
|
|
61
|
+
return "18px";
|
|
62
|
+
case "medium":
|
|
63
|
+
return "16px";
|
|
64
|
+
case "small":
|
|
65
|
+
return "14px";
|
|
66
|
+
case "xsmall":
|
|
67
|
+
return "12px";
|
|
68
|
+
case "micro":
|
|
69
|
+
return "10px";
|
|
70
|
+
default:
|
|
71
|
+
return "16px";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const avatarSize = getSize();
|
|
76
|
+
const isLargeVariant = size === "large";
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<MuiAvatarStyled
|
|
80
|
+
sx={(theme) => {
|
|
81
|
+
const resolvedSx = (getSxStyles(theme, sx) ?? {}) as CSSObject;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
backgroundColor: blueGrey[500],
|
|
85
|
+
cursor: "default",
|
|
86
|
+
fontSize: getFontSize(),
|
|
87
|
+
fontWeight: 600,
|
|
88
|
+
width: avatarSize,
|
|
89
|
+
height: avatarSize,
|
|
90
|
+
...(isLargeVariant && {
|
|
91
|
+
"& .MuiSvgIcon-root": {
|
|
92
|
+
fontSize: "24px",
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
...resolvedSx,
|
|
96
|
+
} as CSSObject;
|
|
97
|
+
}}
|
|
98
|
+
{...rest}
|
|
99
|
+
ref={ref}
|
|
100
|
+
>
|
|
101
|
+
{typeof children === "string"
|
|
102
|
+
? getDisplayStringForAvatar(children)
|
|
103
|
+
: children}
|
|
104
|
+
</MuiAvatarStyled>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
Avatar.displayName = "Avatar";
|
|
110
|
+
|
|
111
|
+
export default Avatar;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Code Connect Configuration for AvatarGroup Component
|
|
3
|
+
*
|
|
4
|
+
* Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=15889-77636
|
|
5
|
+
*
|
|
6
|
+
* Figma Properties:
|
|
7
|
+
* - spacing (small, medium)
|
|
8
|
+
* - size (large: 48px, medium: 40px, small: 32px, xsmall: 24px)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import figma from "@figma/code-connect";
|
|
12
|
+
import AvatarGroup from "./AvatarGroup";
|
|
13
|
+
import Avatar from "./Avatar";
|
|
14
|
+
|
|
15
|
+
figma.connect(
|
|
16
|
+
AvatarGroup,
|
|
17
|
+
"https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=15889-77636",
|
|
18
|
+
{
|
|
19
|
+
props: {
|
|
20
|
+
/**
|
|
21
|
+
* SPACING MAPPING
|
|
22
|
+
* Maps Figma's "spacing" property to React's "spacing" prop
|
|
23
|
+
* Figma: small, medium
|
|
24
|
+
*/
|
|
25
|
+
spacing: figma.enum("spacing", {
|
|
26
|
+
small: "small",
|
|
27
|
+
medium: "medium",
|
|
28
|
+
}),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* SIZE MAPPING
|
|
32
|
+
* Maps Figma's "size" property - size is applied to Avatar children, not the group
|
|
33
|
+
* Figma: large (48px), medium (40px), small (32px), xsmall (24px)
|
|
34
|
+
*/
|
|
35
|
+
size: figma.enum("size", {
|
|
36
|
+
large: "large",
|
|
37
|
+
medium: "medium",
|
|
38
|
+
small: "small",
|
|
39
|
+
xsmall: "xsmall",
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* CHILDREN
|
|
44
|
+
* Maps Avatar instances within the AvatarGroup
|
|
45
|
+
*/
|
|
46
|
+
children: figma.children("Avatar"),
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* EXAMPLE CODE TEMPLATE
|
|
51
|
+
* Shows how AvatarGroup should be used with Avatar children
|
|
52
|
+
* Note: size prop is passed to individual Avatar components, not the group
|
|
53
|
+
*/
|
|
54
|
+
example: ({ spacing, size, children }) => (
|
|
55
|
+
<AvatarGroup spacing={spacing}>
|
|
56
|
+
{children}
|
|
57
|
+
</AvatarGroup>
|
|
58
|
+
),
|
|
59
|
+
}
|
|
60
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
|
|
3
|
+
import MuiAvatarGroup, {
|
|
4
|
+
AvatarGroupProps as MuiAvatarGroupProps,
|
|
5
|
+
} from "@mui/material/AvatarGroup";
|
|
6
|
+
import { styled } from "@mui/material/styles";
|
|
7
|
+
|
|
8
|
+
export interface AvatarGroupProps extends MuiAvatarGroupProps {
|
|
9
|
+
variant?: "circular";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** The AvatarGroup component represents a set of unique users or entities through custom images, initials or icons.
|
|
13
|
+
*
|
|
14
|
+
* // Default import
|
|
15
|
+
* import AvatarGroup from '@carrier-dpx/air-react-library/Avatar'
|
|
16
|
+
*
|
|
17
|
+
* // Named import
|
|
18
|
+
* import { AvatarGroup } from '@carrier-dpx/air-react-library'
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const MuiAvatarGroupStyled = styled(MuiAvatarGroup)<AvatarGroupProps>(
|
|
22
|
+
({ spacing, theme }) => ({
|
|
23
|
+
...(spacing === "small" && {
|
|
24
|
+
".MuiAvatarGroup-avatar": {
|
|
25
|
+
marginLeft: "-12px !important",
|
|
26
|
+
":last-child": {
|
|
27
|
+
marginLeft: "0px!important",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
"& .MuiAvatarGroup-avatar": {
|
|
32
|
+
fontWeight: 600,
|
|
33
|
+
fontSize: "16px",
|
|
34
|
+
border: `2px solid ${theme.palette.base?.background.paper}`,
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const AvatarGroup = forwardRef<unknown, AvatarGroupProps>((props, ref) => {
|
|
40
|
+
return <MuiAvatarGroupStyled {...props} ref={ref} />;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
AvatarGroup.displayName = "AvatarGroup";
|
|
44
|
+
|
|
45
|
+
export default AvatarGroup;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Avatar component family exports
|
|
2
|
+
export { default } from "./Avatar";
|
|
3
|
+
export { default as Avatar } from "./Avatar";
|
|
4
|
+
export * from "./Avatar";
|
|
5
|
+
export type { AvatarProps } from "./Avatar";
|
|
6
|
+
|
|
7
|
+
export { default as AvatarGroup } from "./AvatarGroup";
|
|
8
|
+
export * from "./AvatarGroup";
|
|
9
|
+
export type { AvatarGroupProps } from "./AvatarGroup";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get display string for Avatar component
|
|
3
|
+
* Formats a string (name, initials, etc.) for display in Avatar
|
|
4
|
+
*/
|
|
5
|
+
export const getDisplayStringForAvatar = (input: string): string => {
|
|
6
|
+
if (!input) return "";
|
|
7
|
+
|
|
8
|
+
// If it's already initials (2-3 characters), return as-is
|
|
9
|
+
if (input.length <= 3 && input.split(" ").length === 1) {
|
|
10
|
+
return input.toUpperCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Extract initials from name
|
|
14
|
+
const words = input.trim().split(/\s+/);
|
|
15
|
+
if (words.length >= 2) {
|
|
16
|
+
// First letter of first word + first letter of last word
|
|
17
|
+
return (words[0][0] + words[words.length - 1][0]).toUpperCase();
|
|
18
|
+
} else if (words.length === 1 && words[0].length > 0) {
|
|
19
|
+
// Single word - take first 2 characters
|
|
20
|
+
return words[0].substring(0, 2).toUpperCase();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return input.toUpperCase();
|
|
24
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,8 @@ export { default as Toolbar } from "./components/Toolbar";
|
|
|
28
28
|
export type { ToolbarProps } from "./components/Toolbar";
|
|
29
29
|
export { default as IconButton } from "./components/IconButton";
|
|
30
30
|
export type { IconButtonProps } from "./components/IconButton";
|
|
31
|
+
export { default as Avatar, AvatarGroup } from "./components/Avatar";
|
|
32
|
+
export type { AvatarProps, AvatarGroupProps } from "./components/Avatar";
|
|
31
33
|
export * from "./components/theme";
|
|
32
34
|
|
|
33
35
|
// Demo Icons - exported from main index to avoid deep-path imports
|