@carrier-dpx/air-react-library 0.7.27 → 0.7.28
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/StatusLed/StatusLed.figma.tsx +98 -0
- package/src/components/StatusLed/StatusLed.tsx +77 -0
- package/src/components/StatusLed/index.ts +4 -0
- package/src/components/StatusLed/styles.ts +39 -0
- package/src/components/StatusLed/types.ts +59 -0
- package/src/components/StatusLed/utils.ts +31 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Code Connect Configuration for StatusLed Component
|
|
3
|
+
*
|
|
4
|
+
* Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=37599-424674
|
|
5
|
+
*
|
|
6
|
+
* Figma Properties:
|
|
7
|
+
* - severity (error, warning, success, info, default, static)
|
|
8
|
+
* - size (xsmall: 24px, micro: 20px)
|
|
9
|
+
* - labelPosition (start, end)
|
|
10
|
+
* - label (true, false) - controls visibility of nested "Label" Typography component
|
|
11
|
+
*
|
|
12
|
+
* Notes:
|
|
13
|
+
* - labelPosition changes where the label is placed, either before or after the StatusLED
|
|
14
|
+
* - The "label" boolean prop hides/shows the nested "Label" component which is an instance of the Typography component
|
|
15
|
+
* - Size can be customized in code for any size, but design only supports two sizes
|
|
16
|
+
* - Size mapping: xsmall (24px) → size 12, micro (20px) → size 10 (since circleSize = size * 2)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import figma from "@figma/code-connect";
|
|
20
|
+
import StatusLed from "./StatusLed";
|
|
21
|
+
|
|
22
|
+
figma.connect(
|
|
23
|
+
StatusLed,
|
|
24
|
+
"https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=37599-424674",
|
|
25
|
+
{
|
|
26
|
+
props: {
|
|
27
|
+
/**
|
|
28
|
+
* SEVERITY MAPPING
|
|
29
|
+
* Maps Figma's "severity" property to React's "severity" prop
|
|
30
|
+
* Controls the color of the StatusLED
|
|
31
|
+
*/
|
|
32
|
+
severity: figma.enum("severity", {
|
|
33
|
+
error: "error",
|
|
34
|
+
warning: "warning",
|
|
35
|
+
success: "success",
|
|
36
|
+
info: "info",
|
|
37
|
+
default: "default",
|
|
38
|
+
static: "static",
|
|
39
|
+
}),
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* SIZE MAPPING
|
|
43
|
+
* Maps Figma's "size" property to React's "size" prop
|
|
44
|
+
* Figma: xsmall (24px), micro (20px)
|
|
45
|
+
* React: size is the inner circle size in pixels
|
|
46
|
+
* - xsmall (24px total) → size 12 (12 * 2 = 24)
|
|
47
|
+
* - micro (20px total) → size 10 (10 * 2 = 20)
|
|
48
|
+
*/
|
|
49
|
+
size: figma.enum("size", {
|
|
50
|
+
"xsmall: 24px": 12,
|
|
51
|
+
"micro: 20px": 10,
|
|
52
|
+
// Handle cases where Figma might show just the size name
|
|
53
|
+
xsmall: 12,
|
|
54
|
+
micro: 10,
|
|
55
|
+
}),
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* LABEL POSITION MAPPING
|
|
59
|
+
* Maps Figma's "labelPosition" property to React's "labelPosition" prop
|
|
60
|
+
* Controls where the label is placed relative to the StatusLED
|
|
61
|
+
*/
|
|
62
|
+
labelPosition: figma.enum("labelPosition", {
|
|
63
|
+
start: "start",
|
|
64
|
+
end: "end",
|
|
65
|
+
}),
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* LABEL VISIBILITY
|
|
69
|
+
* Maps Figma's "label" boolean property
|
|
70
|
+
* When true, shows the nested "Label" Typography component
|
|
71
|
+
* When false, hides the label
|
|
72
|
+
*/
|
|
73
|
+
showLabel: figma.boolean("label"),
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* LABEL TEXT CONTENT
|
|
77
|
+
* Maps text property "✏️ Label" from the nested "Label" Typography component
|
|
78
|
+
* The Label is an instance of the Typography component
|
|
79
|
+
* The text property must be surfaced from the "Label" layer
|
|
80
|
+
* Only used when showLabel is true
|
|
81
|
+
*/
|
|
82
|
+
label: figma.string("✏️ Label"),
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* EXAMPLE CODE TEMPLATE
|
|
87
|
+
* Shows how StatusLed should be used with optional label
|
|
88
|
+
*/
|
|
89
|
+
example: ({ severity, size, labelPosition, showLabel, label }) => (
|
|
90
|
+
<StatusLed
|
|
91
|
+
severity={severity}
|
|
92
|
+
size={size}
|
|
93
|
+
labelPosition={labelPosition}
|
|
94
|
+
label={showLabel ? label : undefined}
|
|
95
|
+
/>
|
|
96
|
+
),
|
|
97
|
+
}
|
|
98
|
+
);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { styled } from "@mui/material/styles";
|
|
3
|
+
import Box from "../Box";
|
|
4
|
+
import { Severity, StatusLedProps, StyleProps } from "./types";
|
|
5
|
+
import Typography from "../Typography";
|
|
6
|
+
import styles from "./styles";
|
|
7
|
+
|
|
8
|
+
const CircleInnerStyled = styled(Box, {
|
|
9
|
+
shouldForwardProp: (prop) => prop !== "size",
|
|
10
|
+
})<Omit<StyleProps, "theme">>(styles.circleInner);
|
|
11
|
+
|
|
12
|
+
const CircleOuterStyled = styled(Box, {
|
|
13
|
+
shouldForwardProp: (prop) => prop !== "size",
|
|
14
|
+
})<Omit<StyleProps, "theme">>(styles.circleOuter);
|
|
15
|
+
|
|
16
|
+
const BoxCircleStyled = styled(Box, {
|
|
17
|
+
shouldForwardProp: (prop) => prop !== "size",
|
|
18
|
+
})<Pick<StyleProps, "size">>(styles.containerCircle);
|
|
19
|
+
|
|
20
|
+
const BoxStyled = styled(Box)(styles.container);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The StatusLED pattern provides on and off states and serves as a visual cue intended to attract
|
|
24
|
+
* the user's attention and communicate severity level information.
|
|
25
|
+
*
|
|
26
|
+
* //Default Import
|
|
27
|
+
* `import StatusLed from '@carrier-io/air-react/StatusLed'`
|
|
28
|
+
*/
|
|
29
|
+
const StatusLed: React.FC<StatusLedProps> = ({
|
|
30
|
+
severity = Severity.Default,
|
|
31
|
+
glow = false,
|
|
32
|
+
size = 12,
|
|
33
|
+
label,
|
|
34
|
+
labelPosition = "end",
|
|
35
|
+
...rest
|
|
36
|
+
}) => {
|
|
37
|
+
const circleSize = size * 2;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<BoxStyled data-testid={rest["data-testid"]}>
|
|
41
|
+
{label && labelPosition === "start" && (
|
|
42
|
+
<Typography
|
|
43
|
+
variant="body2"
|
|
44
|
+
aria-label="label"
|
|
45
|
+
sx={(theme) => ({ mr: 0.5, color: theme.palette.base?.text.primary })}
|
|
46
|
+
>
|
|
47
|
+
{label}
|
|
48
|
+
</Typography>
|
|
49
|
+
)}
|
|
50
|
+
<BoxCircleStyled size={circleSize}>
|
|
51
|
+
{glow && (
|
|
52
|
+
<CircleOuterStyled
|
|
53
|
+
severity={severity}
|
|
54
|
+
size={size}
|
|
55
|
+
aria-label="circle-outer"
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
<CircleInnerStyled
|
|
59
|
+
severity={severity}
|
|
60
|
+
size={size}
|
|
61
|
+
aria-label="circle-inner"
|
|
62
|
+
/>
|
|
63
|
+
</BoxCircleStyled>
|
|
64
|
+
{label && labelPosition === "end" && (
|
|
65
|
+
<Typography
|
|
66
|
+
variant="body2"
|
|
67
|
+
aria-label="label"
|
|
68
|
+
sx={(theme) => ({ mr: 0.5, color: theme.palette.base?.text.primary })}
|
|
69
|
+
>
|
|
70
|
+
{label}
|
|
71
|
+
</Typography>
|
|
72
|
+
)}
|
|
73
|
+
</BoxStyled>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default StatusLed;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { CSSObject } from "@mui/material";
|
|
2
|
+
import { Theme } from "@mui/material/styles";
|
|
3
|
+
|
|
4
|
+
import { getColorInner, getColorOuter } from "./utils";
|
|
5
|
+
import { StyleProps } from "./types";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
circleInner: ({ theme, severity, size }: StyleProps): CSSObject => ({
|
|
9
|
+
height: size,
|
|
10
|
+
width: size,
|
|
11
|
+
borderRadius: "50%",
|
|
12
|
+
backgroundColor: getColorInner(theme as unknown as Theme, severity),
|
|
13
|
+
position: "absolute",
|
|
14
|
+
}),
|
|
15
|
+
circleOuter: ({ theme, severity, size }: StyleProps): CSSObject => {
|
|
16
|
+
const roundedOuterSize = Math.round(size * 1.5);
|
|
17
|
+
return {
|
|
18
|
+
height: roundedOuterSize,
|
|
19
|
+
width: roundedOuterSize,
|
|
20
|
+
borderRadius: "50%",
|
|
21
|
+
filter: `blur(${size / 6}px)`,
|
|
22
|
+
backgroundColor: getColorOuter(theme as unknown as Theme, severity),
|
|
23
|
+
"will-change": "transform",
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
containerCircle: ({ size }: Pick<StyleProps, "size">): CSSObject => ({
|
|
27
|
+
position: "relative",
|
|
28
|
+
display: "flex",
|
|
29
|
+
justifyContent: "center",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
width: size,
|
|
32
|
+
height: size,
|
|
33
|
+
}),
|
|
34
|
+
container: {
|
|
35
|
+
display: "flex",
|
|
36
|
+
flexDirection: "row",
|
|
37
|
+
alignItems: "center",
|
|
38
|
+
} as CSSObject,
|
|
39
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Theme } from "@mui/material/styles";
|
|
2
|
+
|
|
3
|
+
export enum Severity {
|
|
4
|
+
Success = "success",
|
|
5
|
+
Info = "info",
|
|
6
|
+
Warning = "warning",
|
|
7
|
+
Error = "error",
|
|
8
|
+
Default = "default",
|
|
9
|
+
Static = "static",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface StyleProps {
|
|
13
|
+
severity: Severity;
|
|
14
|
+
size: number;
|
|
15
|
+
theme: Theme;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface StatusLedProps {
|
|
19
|
+
/**
|
|
20
|
+
* The severity of the StatusLed. This defines the color.
|
|
21
|
+
*
|
|
22
|
+
* @default 'default'
|
|
23
|
+
*/
|
|
24
|
+
severity?: Severity;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A boolean that controls the presence of outer circle (glow) of the StatusLed.
|
|
28
|
+
* The overall size is rounded 150% of the size property value if glow is enabled
|
|
29
|
+
*
|
|
30
|
+
* @default False
|
|
31
|
+
*/
|
|
32
|
+
glow?: boolean;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The size of the inner circle in pixels.
|
|
36
|
+
* The size of the outer circle (glow) is rounded 50% larger if present.
|
|
37
|
+
*
|
|
38
|
+
* @default 12
|
|
39
|
+
*/
|
|
40
|
+
size?: number;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The label content.
|
|
44
|
+
*/
|
|
45
|
+
label?: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Controls where the label should be placed.
|
|
49
|
+
*
|
|
50
|
+
* @default end
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
labelPosition?: "start" | "end";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* For testing purposes
|
|
57
|
+
*/
|
|
58
|
+
"data-testid"?: string;
|
|
59
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { lighten, PaletteOptions } from "@mui/material";
|
|
2
|
+
import { Theme } from "@mui/material/styles";
|
|
3
|
+
import { SimplePaletteColorOptions } from "@mui/material/styles/createPalette";
|
|
4
|
+
|
|
5
|
+
import { Severity } from "./types";
|
|
6
|
+
|
|
7
|
+
export const getColorInner = (theme: Theme, severity: Severity): string => {
|
|
8
|
+
const key = severity.toLowerCase() as keyof PaletteOptions;
|
|
9
|
+
const palette = theme.palette[key] as SimplePaletteColorOptions;
|
|
10
|
+
const finalColor =
|
|
11
|
+
palette?.main ||
|
|
12
|
+
(severity == Severity.Static
|
|
13
|
+
? theme.palette.base?.dark || theme.palette.common.black
|
|
14
|
+
: theme.palette.base?.state?.disabledContent ||
|
|
15
|
+
lighten(theme.palette.common.black, 0.7));
|
|
16
|
+
|
|
17
|
+
return finalColor;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getColorOuter = (theme: Theme, severity: Severity): string => {
|
|
21
|
+
const key = severity.toLowerCase() as keyof PaletteOptions;
|
|
22
|
+
const palette = theme.palette[key] as SimplePaletteColorOptions;
|
|
23
|
+
const finalColor =
|
|
24
|
+
palette?.light ??
|
|
25
|
+
(severity == Severity.Static
|
|
26
|
+
? theme.palette.base?.dark || theme.palette.common.black
|
|
27
|
+
: theme.palette.base?.state?.disabledContent ||
|
|
28
|
+
lighten(theme.palette.common.black, 0.8));
|
|
29
|
+
|
|
30
|
+
return finalColor;
|
|
31
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,8 @@ export { default as Avatar, AvatarGroup } from "./components/Avatar";
|
|
|
32
32
|
export type { AvatarProps, AvatarGroupProps } from "./components/Avatar";
|
|
33
33
|
export { default as MobileStatusBar } from "./components/MobileStatusBar";
|
|
34
34
|
export type { MobileStatusBarProps } from "./components/MobileStatusBar";
|
|
35
|
+
export { default as StatusLed } from "./components/StatusLed";
|
|
36
|
+
export type { StatusLedProps, Severity } from "./components/StatusLed";
|
|
35
37
|
export * from "./components/theme";
|
|
36
38
|
|
|
37
39
|
// Demo Icons - exported from main index to avoid deep-path imports
|