@carrier-dpx/air-react-library 0.7.31 → 0.7.32
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/Backdrop/Backdrop.figma.tsx +30 -0
- package/src/components/Backdrop/Backdrop.tsx +35 -0
- package/src/components/Backdrop/index.ts +3 -0
- package/src/components/Checkbox/Checkbox.figma.tsx +128 -0
- package/src/components/Checkbox/Checkbox.tsx +134 -0
- package/src/components/Checkbox/index.ts +3 -0
- package/src/components/Checkbox/styles.ts +111 -0
- package/src/components/Radio/Radio.figma.tsx +120 -0
- package/src/components/Radio/Radio.tsx +97 -0
- package/src/components/Radio/index.ts +3 -0
- package/src/components/Radio/styles.ts +107 -0
- package/src/components/StatusLed/StatusLed.tsx +1 -0
- package/src/components/Switch/Switch.figma.tsx +120 -0
- package/src/components/Switch/Switch.tsx +479 -0
- package/src/components/Switch/index.ts +3 -0
- package/src/components/utils/SwitchUtils.ts +25 -0
- package/src/index.ts +8 -0
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Code Connect Configuration for Backdrop Component
|
|
3
|
+
*
|
|
4
|
+
* Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6643-52207
|
|
5
|
+
*
|
|
6
|
+
* Note: This component has no props in design - it's a simple backdrop overlay component
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import figma from "@figma/code-connect";
|
|
10
|
+
import Backdrop from "./Backdrop";
|
|
11
|
+
|
|
12
|
+
figma.connect(
|
|
13
|
+
Backdrop,
|
|
14
|
+
"https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6643-52207",
|
|
15
|
+
{
|
|
16
|
+
props: {
|
|
17
|
+
// No props in design - Backdrop is used as-is
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* EXAMPLE CODE TEMPLATE
|
|
22
|
+
* Shows how Backdrop should be used
|
|
23
|
+
*/
|
|
24
|
+
example: () => (
|
|
25
|
+
<Backdrop open={true}>
|
|
26
|
+
{/* Content goes here */}
|
|
27
|
+
</Backdrop>
|
|
28
|
+
),
|
|
29
|
+
}
|
|
30
|
+
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Backdrop as MuiBackdrop,
|
|
5
|
+
BackdropProps as MuiBackdropProps,
|
|
6
|
+
CSSObject,
|
|
7
|
+
} from "@mui/material";
|
|
8
|
+
import { getSxStyles } from "../utils/styles";
|
|
9
|
+
|
|
10
|
+
export interface BackdropProps extends MuiBackdropProps {}
|
|
11
|
+
|
|
12
|
+
/** The Backdrop component signals a state change and provides emphasis to contextual or actionable content.
|
|
13
|
+
*
|
|
14
|
+
* `import Backdrop from '@carrier-io/air-react/Backdrop'`
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const Backdrop = forwardRef<unknown, BackdropProps>((props, ref) => {
|
|
18
|
+
return (
|
|
19
|
+
<MuiBackdrop
|
|
20
|
+
{...props}
|
|
21
|
+
sx={(theme) =>
|
|
22
|
+
({
|
|
23
|
+
backgroundColor: theme.palette.base?.backdropOverlay,
|
|
24
|
+
...getSxStyles(theme, props.sx),
|
|
25
|
+
} as CSSObject)
|
|
26
|
+
}
|
|
27
|
+
ref={ref}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
Backdrop.displayName = "Backdrop";
|
|
33
|
+
|
|
34
|
+
export default Backdrop;
|
|
35
|
+
export type { BackdropProps };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Code Connect Configuration for Checkbox Component
|
|
3
|
+
*
|
|
4
|
+
* Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6543-43052
|
|
5
|
+
*
|
|
6
|
+
* Figma Properties:
|
|
7
|
+
* - size (medium-40px, small-32px)
|
|
8
|
+
* - checked (true, false)
|
|
9
|
+
* - indeterminate (true, false)
|
|
10
|
+
* - disabled (true, false)
|
|
11
|
+
* - color (primary, secondary, base, error, warning, success, info)
|
|
12
|
+
* - state (enabled, hover, focus) - visual state, not a prop
|
|
13
|
+
* - labelPosition (end, start, top, bottom)
|
|
14
|
+
* - showLabel (true, false)
|
|
15
|
+
* - ✏️ Text (text label with layer name "Label")
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import figma from "@figma/code-connect";
|
|
19
|
+
import Checkbox from "./Checkbox";
|
|
20
|
+
import { FormControlLabel } from "@mui/material";
|
|
21
|
+
|
|
22
|
+
figma.connect(
|
|
23
|
+
Checkbox,
|
|
24
|
+
"https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6543-43052",
|
|
25
|
+
{
|
|
26
|
+
props: {
|
|
27
|
+
/**
|
|
28
|
+
* SIZE MAPPING
|
|
29
|
+
* Maps Figma's "size" property to React's "size" prop
|
|
30
|
+
* Figma: medium (40px), small (32px)
|
|
31
|
+
*/
|
|
32
|
+
size: figma.enum("size", {
|
|
33
|
+
"medium-40px": "medium",
|
|
34
|
+
"small-32px": "small",
|
|
35
|
+
// Handle cases where Figma might show just the size name
|
|
36
|
+
medium: "medium",
|
|
37
|
+
small: "small",
|
|
38
|
+
}),
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* CHECKED STATE
|
|
42
|
+
* Maps Figma's "checked" boolean to React's "checked" prop
|
|
43
|
+
*/
|
|
44
|
+
checked: figma.boolean("checked"),
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* INDETERMINATE STATE
|
|
48
|
+
* Maps Figma's "indeterminate" boolean to React's "indeterminate" prop
|
|
49
|
+
*/
|
|
50
|
+
indeterminate: figma.boolean("indeterminate"),
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* DISABLED STATE
|
|
54
|
+
* Maps Figma's "disabled" boolean to React's "disabled" prop
|
|
55
|
+
*/
|
|
56
|
+
disabled: figma.boolean("disabled"),
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* COLOR MAPPING
|
|
60
|
+
* Maps Figma's "color" property to React's "color" prop
|
|
61
|
+
*/
|
|
62
|
+
color: figma.enum("color", {
|
|
63
|
+
primary: "primary",
|
|
64
|
+
secondary: "secondary",
|
|
65
|
+
base: "base",
|
|
66
|
+
error: "error",
|
|
67
|
+
warning: "warning",
|
|
68
|
+
success: "success",
|
|
69
|
+
info: "info",
|
|
70
|
+
}),
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* LABEL POSITION
|
|
74
|
+
* Maps Figma's "labelPosition" property
|
|
75
|
+
* Used with FormControlLabel to position the label
|
|
76
|
+
*/
|
|
77
|
+
labelPosition: figma.enum("labelPosition", {
|
|
78
|
+
end: "end",
|
|
79
|
+
start: "start",
|
|
80
|
+
top: "top",
|
|
81
|
+
bottom: "bottom",
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* LABEL VISIBILITY
|
|
86
|
+
* Maps Figma's "showLabel" boolean property
|
|
87
|
+
* When true, shows the label; when false, hides it
|
|
88
|
+
*/
|
|
89
|
+
showLabel: figma.boolean("showLabel"),
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* LABEL TEXT CONTENT
|
|
93
|
+
* Maps text property "✏️ Text" from the "Label" layer
|
|
94
|
+
* The text property must be surfaced from the "Label" layer
|
|
95
|
+
*/
|
|
96
|
+
label: figma.string("✏️ Text"),
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* EXAMPLE CODE TEMPLATE
|
|
101
|
+
* Shows how Checkbox should be used, optionally with FormControlLabel for labels
|
|
102
|
+
*/
|
|
103
|
+
example: ({ size, checked, indeterminate, disabled, color, labelPosition, showLabel, label }) => {
|
|
104
|
+
const checkbox = (
|
|
105
|
+
<Checkbox
|
|
106
|
+
size={size}
|
|
107
|
+
checked={checked}
|
|
108
|
+
indeterminate={indeterminate}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
color={color}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// If label should be shown, wrap with FormControlLabel
|
|
115
|
+
if (showLabel && label) {
|
|
116
|
+
return (
|
|
117
|
+
<FormControlLabel
|
|
118
|
+
control={checkbox}
|
|
119
|
+
label={label}
|
|
120
|
+
labelPlacement={labelPosition || "end"}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return checkbox;
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
);
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { forwardRef, Ref } from "react";
|
|
2
|
+
|
|
3
|
+
import MuiCheckbox, {
|
|
4
|
+
CheckboxProps as MuiCheckboxProps,
|
|
5
|
+
} from "@mui/material/Checkbox";
|
|
6
|
+
import { TouchRippleActions } from "@mui/material/ButtonBase/TouchRipple";
|
|
7
|
+
import { CSSObject, Theme, useTheme } from "@mui/material/styles";
|
|
8
|
+
|
|
9
|
+
import { baseCheckboxSx, disableHoverSx } from "./styles";
|
|
10
|
+
import { getSxStyles } from "../utils/styles";
|
|
11
|
+
|
|
12
|
+
export interface CheckboxProps extends Omit<MuiCheckboxProps, "color"> {
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* The color of the component. It supports both default and custom theme colors, which can be added as shown in the <a href="https://mui.com/material-ui/customization/palette/#adding-new-colors">palette customization guide</a>.
|
|
16
|
+
* @default primary
|
|
17
|
+
*/
|
|
18
|
+
color?:
|
|
19
|
+
| "base"
|
|
20
|
+
| "primary"
|
|
21
|
+
| "secondary"
|
|
22
|
+
| "warning"
|
|
23
|
+
| "success"
|
|
24
|
+
| "info"
|
|
25
|
+
| "error";
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* A ref that points to the TouchRipple element.
|
|
29
|
+
*/
|
|
30
|
+
touchRippleRef?: Ref<TouchRippleActions>;
|
|
31
|
+
|
|
32
|
+
disableHover?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const defaultUncheckedCheckboxIcon = (theme: Theme) => (
|
|
36
|
+
<svg
|
|
37
|
+
width="24"
|
|
38
|
+
height="24"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
fill="none"
|
|
41
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
42
|
+
>
|
|
43
|
+
<rect
|
|
44
|
+
x="4"
|
|
45
|
+
y="4"
|
|
46
|
+
width="16"
|
|
47
|
+
height="16"
|
|
48
|
+
rx="3"
|
|
49
|
+
stroke={theme.palette?.base?.state.active}
|
|
50
|
+
strokeWidth="2"
|
|
51
|
+
/>
|
|
52
|
+
</svg>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const defaultCheckedCheckboxIcon = (
|
|
56
|
+
<svg
|
|
57
|
+
width="24"
|
|
58
|
+
height="24"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
61
|
+
>
|
|
62
|
+
<path
|
|
63
|
+
fillRule="evenodd"
|
|
64
|
+
clipRule="evenodd"
|
|
65
|
+
d="M7 3C4.79086 3 3 4.79086 3 7V17C3 19.2091 4.79086 21 7 21H17C19.2091 21 21 19.2091 21 17V7C21 4.79086 19.2091 3 17 3H7ZM16.7388 9.67396C17.111 9.26595 17.082 8.63345 16.674 8.26123C16.266 7.88902 15.6335 7.91803 15.2612 8.32604L10.4994 13.5457L8.71334 11.7277C8.32628 11.3338 7.69314 11.3282 7.29918 11.7152C6.90521 12.1023 6.89961 12.7354 7.28666 13.1294L9.81298 15.7008C10.0055 15.8968 10.2702 16.0049 10.5448 15.9998C10.8195 15.9947 11.0799 15.8769 11.2651 15.674L16.7388 9.67396Z"
|
|
66
|
+
/>
|
|
67
|
+
</svg>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const defaultIndeterminateCheckboxIcon = (
|
|
71
|
+
<svg
|
|
72
|
+
width="24"
|
|
73
|
+
height="24"
|
|
74
|
+
viewBox="0 0 24 24"
|
|
75
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
76
|
+
>
|
|
77
|
+
<path
|
|
78
|
+
fillRule="evenodd"
|
|
79
|
+
clipRule="evenodd"
|
|
80
|
+
d="M7 3C4.79086 3 3 4.79086 3 7V17C3 19.2091 4.79086 21 7 21H17C19.2091 21 21 19.2091 21 17V7C21 4.79086 19.2091 3 17 3H7ZM8 11C7.44772 11 7 11.4477 7 12C7 12.5523 7.44772 13 8 13H16C16.5523 13 17 12.5523 17 12C17 11.4477 16.5523 11 16 11H8Z"
|
|
81
|
+
/>
|
|
82
|
+
</svg>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
/** The Checkbox component allows users to select one or more items from a set or list.
|
|
86
|
+
*
|
|
87
|
+
* // Default import
|
|
88
|
+
* import Checkbox from '@carrier-io/air-react/Checkbox'
|
|
89
|
+
*
|
|
90
|
+
* // Named import
|
|
91
|
+
* import { Checkbox } from '@carrier-io/air-react'
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
95
|
+
({ color = "primary", disableHover = false, ...rest }, ref) => {
|
|
96
|
+
const theme = useTheme();
|
|
97
|
+
return (
|
|
98
|
+
<MuiCheckbox
|
|
99
|
+
color={color as any}
|
|
100
|
+
icon={defaultUncheckedCheckboxIcon(theme)}
|
|
101
|
+
checkedIcon={defaultCheckedCheckboxIcon}
|
|
102
|
+
indeterminateIcon={defaultIndeterminateCheckboxIcon}
|
|
103
|
+
sx={(theme) =>
|
|
104
|
+
({
|
|
105
|
+
...getSxStyles(theme, rest.sx),
|
|
106
|
+
p: 1,
|
|
107
|
+
...(rest.checked &&
|
|
108
|
+
rest.disabled && {
|
|
109
|
+
svg: { fill: () => theme.palette.base?.main },
|
|
110
|
+
}),
|
|
111
|
+
...(rest.disabled === true && {
|
|
112
|
+
svg: {
|
|
113
|
+
rect: { stroke: () => theme.palette.base?.text.disabled },
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
...(rest.size === "small" && {
|
|
117
|
+
svg: { width: "16px", height: "16px" },
|
|
118
|
+
}),
|
|
119
|
+
...(disableHover && disableHoverSx),
|
|
120
|
+
...baseCheckboxSx,
|
|
121
|
+
} as CSSObject)
|
|
122
|
+
}
|
|
123
|
+
disableRipple={true}
|
|
124
|
+
ref={ref}
|
|
125
|
+
{...rest}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
Checkbox.displayName = "Checkbox";
|
|
132
|
+
|
|
133
|
+
export default Checkbox;
|
|
134
|
+
export type { CheckboxProps };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Theme } from "@mui/material";
|
|
2
|
+
|
|
3
|
+
export const disableHoverSx = {
|
|
4
|
+
"&:hover": {
|
|
5
|
+
backgroundColor: "transparent",
|
|
6
|
+
},
|
|
7
|
+
"&.Mui-checked:hover": {
|
|
8
|
+
backgroundColor: "transparent",
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const baseCheckboxSx = {
|
|
13
|
+
"&:hover": {
|
|
14
|
+
backgroundColor: (theme: Theme) =>
|
|
15
|
+
theme.palette.base?.state.disabledBackground,
|
|
16
|
+
},
|
|
17
|
+
"&.Mui-checked": {
|
|
18
|
+
fill: (theme: Theme) => theme.palette.primary?.main,
|
|
19
|
+
|
|
20
|
+
"&:hover": {
|
|
21
|
+
backgroundColor: (theme: Theme) =>
|
|
22
|
+
theme.palette.primary?.outlinedHoverBackground,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
"&.MuiCheckbox-colorPrimary": {
|
|
26
|
+
"&.Mui-checked": {
|
|
27
|
+
fill: (theme: Theme) => theme.palette.primary?.main,
|
|
28
|
+
},
|
|
29
|
+
"&.Mui-disabled": {
|
|
30
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
31
|
+
},
|
|
32
|
+
"&:hover": {
|
|
33
|
+
backgroundColor: (theme: Theme) =>
|
|
34
|
+
theme.palette.primary?.outlinedHoverBackground,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
"&.MuiCheckbox-colorBase": {
|
|
38
|
+
"&.Mui-checked": {
|
|
39
|
+
fill: (theme: Theme) => theme.palette.base?.dark,
|
|
40
|
+
},
|
|
41
|
+
"&.Mui-disabled": {
|
|
42
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
43
|
+
},
|
|
44
|
+
"&:hover": {
|
|
45
|
+
backgroundColor: (theme: Theme) => theme.palette.base?.state.hover,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
"&.Mui-disabled": {
|
|
49
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
50
|
+
},
|
|
51
|
+
"&.MuiCheckbox-colorSecondary": {
|
|
52
|
+
"&.Mui-checked": {
|
|
53
|
+
fill: (theme: Theme) => theme.palette.secondary?.dark,
|
|
54
|
+
},
|
|
55
|
+
"&.Mui-disabled": {
|
|
56
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
57
|
+
},
|
|
58
|
+
"&:hover": {
|
|
59
|
+
backgroundColor: (theme: Theme) =>
|
|
60
|
+
theme.palette.secondary?.outlinedHoverBackground,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
"&.MuiCheckbox-colorWarning": {
|
|
64
|
+
"&.Mui-checked": {
|
|
65
|
+
fill: (theme: Theme) => theme.palette.warning?.main,
|
|
66
|
+
},
|
|
67
|
+
"&.Mui-disabled": {
|
|
68
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
69
|
+
},
|
|
70
|
+
"&:hover": {
|
|
71
|
+
backgroundColor: (theme: Theme) =>
|
|
72
|
+
theme.palette.warning?.outlinedHoverBackground,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
"&.MuiCheckbox-colorSuccess": {
|
|
76
|
+
"&.Mui-checked": {
|
|
77
|
+
fill: (theme: Theme) => theme.palette.success?.main,
|
|
78
|
+
},
|
|
79
|
+
"&.Mui-disabled": {
|
|
80
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
81
|
+
},
|
|
82
|
+
"&:hover": {
|
|
83
|
+
backgroundColor: (theme: Theme) =>
|
|
84
|
+
theme.palette.success?.outlinedHoverBackground,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
"&.MuiCheckbox-colorInfo": {
|
|
88
|
+
"&.Mui-checked": {
|
|
89
|
+
fill: (theme: Theme) => theme.palette.info?.main,
|
|
90
|
+
},
|
|
91
|
+
"&.Mui-disabled": {
|
|
92
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
93
|
+
},
|
|
94
|
+
"&:hover": {
|
|
95
|
+
backgroundColor: (theme: Theme) =>
|
|
96
|
+
theme.palette.info?.outlinedHoverBackground,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
"&.MuiCheckbox-colorError": {
|
|
100
|
+
"&.Mui-checked": {
|
|
101
|
+
fill: (theme: Theme) => theme.palette.error?.main,
|
|
102
|
+
},
|
|
103
|
+
"&.Mui-disabled": {
|
|
104
|
+
fill: (theme: Theme) => theme.palette.base?.text.disabled,
|
|
105
|
+
},
|
|
106
|
+
"&:hover": {
|
|
107
|
+
backgroundColor: (theme: Theme) =>
|
|
108
|
+
theme.palette.error?.outlinedHoverBackground,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Code Connect Configuration for Radio Component
|
|
3
|
+
*
|
|
4
|
+
* Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6558-39273
|
|
5
|
+
*
|
|
6
|
+
* Figma Properties:
|
|
7
|
+
* - size (medium-40px, small-32px)
|
|
8
|
+
* - checked (true, false)
|
|
9
|
+
* - disabled (true, false)
|
|
10
|
+
* - color (primary, secondary, base, error, warning, success, info)
|
|
11
|
+
* - state (enabled, hover, focus) - visual state, not a prop
|
|
12
|
+
* - labelPosition (end, start, top, bottom)
|
|
13
|
+
* - showLabel (true, false)
|
|
14
|
+
* - ✏️ Text (text label with layer name "Label")
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import figma from "@figma/code-connect";
|
|
18
|
+
import Radio from "./Radio";
|
|
19
|
+
import { FormControlLabel } from "@mui/material";
|
|
20
|
+
|
|
21
|
+
figma.connect(
|
|
22
|
+
Radio,
|
|
23
|
+
"https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=6558-39273",
|
|
24
|
+
{
|
|
25
|
+
props: {
|
|
26
|
+
/**
|
|
27
|
+
* SIZE MAPPING
|
|
28
|
+
* Maps Figma's "size" property to React's "size" prop
|
|
29
|
+
* Figma: medium (40px), small (32px)
|
|
30
|
+
*/
|
|
31
|
+
size: figma.enum("size", {
|
|
32
|
+
"medium-40px": "medium",
|
|
33
|
+
"small-32px": "small",
|
|
34
|
+
// Handle cases where Figma might show just the size name
|
|
35
|
+
medium: "medium",
|
|
36
|
+
small: "small",
|
|
37
|
+
}),
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* CHECKED STATE
|
|
41
|
+
* Maps Figma's "checked" boolean to React's "checked" prop
|
|
42
|
+
*/
|
|
43
|
+
checked: figma.boolean("checked"),
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* DISABLED STATE
|
|
47
|
+
* Maps Figma's "disabled" boolean to React's "disabled" prop
|
|
48
|
+
*/
|
|
49
|
+
disabled: figma.boolean("disabled"),
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* COLOR MAPPING
|
|
53
|
+
* Maps Figma's "color" property to React's "color" prop
|
|
54
|
+
*/
|
|
55
|
+
color: figma.enum("color", {
|
|
56
|
+
primary: "primary",
|
|
57
|
+
secondary: "secondary",
|
|
58
|
+
base: "base",
|
|
59
|
+
error: "error",
|
|
60
|
+
warning: "warning",
|
|
61
|
+
success: "success",
|
|
62
|
+
info: "info",
|
|
63
|
+
}),
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* LABEL POSITION
|
|
67
|
+
* Maps Figma's "labelPosition" property
|
|
68
|
+
* Used with FormControlLabel to position the label
|
|
69
|
+
*/
|
|
70
|
+
labelPosition: figma.enum("labelPosition", {
|
|
71
|
+
end: "end",
|
|
72
|
+
start: "start",
|
|
73
|
+
top: "top",
|
|
74
|
+
bottom: "bottom",
|
|
75
|
+
}),
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* LABEL VISIBILITY
|
|
79
|
+
* Maps Figma's "showLabel" boolean property
|
|
80
|
+
* When true, shows the label; when false, hides it
|
|
81
|
+
*/
|
|
82
|
+
showLabel: figma.boolean("showLabel"),
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* LABEL TEXT CONTENT
|
|
86
|
+
* Maps text property "✏️ Text" from the "Label" layer
|
|
87
|
+
* The text property must be surfaced from the "Label" layer
|
|
88
|
+
*/
|
|
89
|
+
label: figma.string("✏️ Text"),
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* EXAMPLE CODE TEMPLATE
|
|
94
|
+
* Shows how Radio should be used, optionally with FormControlLabel for labels
|
|
95
|
+
*/
|
|
96
|
+
example: ({ size, checked, disabled, color, labelPosition, showLabel, label }) => {
|
|
97
|
+
const radio = (
|
|
98
|
+
<Radio
|
|
99
|
+
size={size}
|
|
100
|
+
checked={checked}
|
|
101
|
+
disabled={disabled}
|
|
102
|
+
color={color}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// If label should be shown, wrap with FormControlLabel
|
|
107
|
+
if (showLabel && label) {
|
|
108
|
+
return (
|
|
109
|
+
<FormControlLabel
|
|
110
|
+
control={radio}
|
|
111
|
+
label={label}
|
|
112
|
+
labelPlacement={labelPosition || "end"}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return radio;
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
|
|
3
|
+
import MuiRadio, { RadioProps as MuiRadioProps } from "@mui/material/Radio";
|
|
4
|
+
import { CSSObject, Theme, useTheme } from "@mui/material/styles";
|
|
5
|
+
|
|
6
|
+
import { baseRadioSx } from "./styles";
|
|
7
|
+
import { getSxStyles } from "../utils/styles";
|
|
8
|
+
|
|
9
|
+
export interface RadioProps extends Omit<MuiRadioProps, "color"> {
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* Colors that make sense for this component
|
|
13
|
+
* @default primary
|
|
14
|
+
*/
|
|
15
|
+
color?:
|
|
16
|
+
| "base"
|
|
17
|
+
| "primary"
|
|
18
|
+
| "secondary"
|
|
19
|
+
| "warning"
|
|
20
|
+
| "success"
|
|
21
|
+
| "info"
|
|
22
|
+
| "error";
|
|
23
|
+
disableHover?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const defaultRadioIcon = (size: RadioProps["size"], theme: Theme) => (
|
|
27
|
+
<svg
|
|
28
|
+
width={size === "small" ? "20" : "24"}
|
|
29
|
+
height={size === "small" ? "20" : "24"}
|
|
30
|
+
viewBox="-1 -1 20 20"
|
|
31
|
+
fill="none"
|
|
32
|
+
stroke={theme.palette?.base?.state.active ?? "#666666"}
|
|
33
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
34
|
+
>
|
|
35
|
+
<path
|
|
36
|
+
fillRule="evenodd"
|
|
37
|
+
clipRule="evenodd"
|
|
38
|
+
d="M9 16C12.866 16 16 12.866 16 9C16 5.13401 12.866 2 9 2C5.13401 2 2 5.13401 2 9C2 12.866 5.13401 16 9 16ZM9 18C13.9706 18 18 13.9706 18 9C18 4.02944 13.9706 0 9 0C4.02944 0 0 4.02944 0 9C0 13.9706 4.02944 18 9 18Z"
|
|
39
|
+
fill={theme.palette?.base?.state.active ?? "#666666"}
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const defaultCheckedRadioIcon = (size: RadioProps["size"]) => (
|
|
45
|
+
<svg
|
|
46
|
+
width={size === "small" ? "20" : "24"}
|
|
47
|
+
height={size === "small" ? "20" : "24"}
|
|
48
|
+
viewBox="-1 -1 20 20"
|
|
49
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
50
|
+
>
|
|
51
|
+
<path
|
|
52
|
+
fillRule="evenodd"
|
|
53
|
+
clipRule="evenodd"
|
|
54
|
+
d="M9 12C10.6569 12 12 10.6569 12 9C12 7.34315 10.6569 6 9 6C7.34315 6 6 7.34315 6 9C6 10.6569 7.34315 12 9 12ZM9 18C13.9706 18 18 13.9706 18 9C18 4.02944 13.9706 0 9 0C4.02944 0 0 4.02944 0 9C0 13.9706 4.02944 18 9 18Z"
|
|
55
|
+
/>
|
|
56
|
+
</svg>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/** The Radio component allows users to make a mutually exclusive choice from a set or list.
|
|
60
|
+
*
|
|
61
|
+
* // Default import
|
|
62
|
+
* import Radio from '@carrier-io/air-react/Radio'
|
|
63
|
+
*
|
|
64
|
+
* // Named import
|
|
65
|
+
* import { Radio } from '@carrier-io/air-react'
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
const Radio = forwardRef<HTMLButtonElement, RadioProps>(
|
|
69
|
+
({ sx, disableHover = true, color = "primary", ...rest }, ref) => {
|
|
70
|
+
const theme = useTheme();
|
|
71
|
+
const radioSX = (theme: Theme) =>
|
|
72
|
+
({
|
|
73
|
+
...getSxStyles(theme, sx),
|
|
74
|
+
...(rest.size === "small"
|
|
75
|
+
? { height: 32, width: 32 }
|
|
76
|
+
: { height: 40, width: 40 }),
|
|
77
|
+
...baseRadioSx(theme, disableHover),
|
|
78
|
+
} as CSSObject);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<MuiRadio
|
|
82
|
+
color={color as any}
|
|
83
|
+
disableRipple={true}
|
|
84
|
+
checkedIcon={rest.checkedIcon ?? defaultCheckedRadioIcon(rest.size)}
|
|
85
|
+
icon={rest.icon ?? defaultRadioIcon(rest.size, theme)}
|
|
86
|
+
{...rest}
|
|
87
|
+
sx={radioSX}
|
|
88
|
+
ref={ref}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
Radio.displayName = "Radio";
|
|
95
|
+
|
|
96
|
+
export default Radio;
|
|
97
|
+
export type { RadioProps };
|