@carrier-dpx/air-react-library 0.7.36 → 0.7.38

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carrier-dpx/air-react-library",
3
- "version": "0.7.36",
3
+ "version": "0.7.38",
4
4
  "description": "Air web React component library for Figma Make",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -50,4 +50,3 @@ const Breadcrumbs = forwardRef<HTMLElement, BreadcrumbsProps>(
50
50
  Breadcrumbs.displayName = "Breadcrumbs";
51
51
 
52
52
  export default Breadcrumbs;
53
- export type { BreadcrumbsProps };
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Figma Code Connect Configuration for Tab Component (Horizontal)
3
+ *
4
+ * Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=23369-17683
5
+ *
6
+ * Figma Properties:
7
+ * - selected (true, false)
8
+ * - indicatorWidth (content, full)
9
+ * - invertIndicator (true, false)
10
+ * - disabled (true, false)
11
+ * - state (enabled, hover, focus) - visual state, not a prop
12
+ * - label (true, false) - boolean hide/show
13
+ * - startAdornment (true, false) - boolean hide/show
14
+ * - endAdornment (true, false) - boolean hide/show
15
+ *
16
+ * Note: indicatorWidth and invertIndicator are Tabs-level props, but are included
17
+ * here as they may be visually represented in Figma's Tab component.
18
+ */
19
+
20
+ import figma from "@figma/code-connect";
21
+ import Tab from "./Tab";
22
+
23
+ figma.connect(
24
+ Tab,
25
+ "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=23369-17683",
26
+ {
27
+ props: {
28
+ /**
29
+ * SELECTED STATE
30
+ * Maps Figma's "selected" boolean to React's "selected" prop
31
+ */
32
+ selected: figma.boolean("selected"),
33
+
34
+ /**
35
+ * INDICATOR WIDTH
36
+ * Maps Figma's "indicatorWidth" property
37
+ * Note: This is typically a Tabs-level prop, but may be visually represented in Figma
38
+ */
39
+ indicatorWidth: figma.enum("indicatorWidth", {
40
+ content: "content",
41
+ full: "full",
42
+ }),
43
+
44
+ /**
45
+ * INVERT INDICATOR
46
+ * Maps Figma's "invertIndicator" boolean
47
+ * Note: This is typically a Tabs-level prop, but may be visually represented in Figma
48
+ */
49
+ invertIndicator: figma.boolean("invertIndicator"),
50
+
51
+ /**
52
+ * DISABLED STATE
53
+ * Maps Figma's "disabled" boolean to React's "disabled" prop
54
+ */
55
+ disabled: figma.boolean("disabled"),
56
+
57
+ /**
58
+ * LABEL VISIBILITY
59
+ * Maps Figma's "label" boolean - controls visibility of label text
60
+ */
61
+ showLabel: figma.boolean("label"),
62
+
63
+ /**
64
+ * LABEL TEXT CONTENT
65
+ * Maps text content from nested "Label" Typography component
66
+ */
67
+ labelText: figma.nestedProps("Label", {
68
+ children: figma.children("Label"),
69
+ }),
70
+
71
+ /**
72
+ * START ADORNMENT VISIBILITY
73
+ * Maps Figma's "startAdornment" boolean - controls visibility of start icon
74
+ */
75
+ showStartAdornment: figma.boolean("startAdornment"),
76
+
77
+ /**
78
+ * START ADORNMENT ICON INSTANCE
79
+ * Maps nested Icon component instance when startAdornment is visible
80
+ * Uses figma.instance() to get the Icon component instance
81
+ */
82
+ startIcon: figma.instance("startAdornment"),
83
+
84
+ /**
85
+ * END ADORNMENT VISIBILITY
86
+ * Maps Figma's "endAdornment" boolean - controls visibility of end icon
87
+ */
88
+ showEndAdornment: figma.boolean("endAdornment"),
89
+
90
+ /**
91
+ * END ADORNMENT ICON INSTANCE
92
+ * Maps nested Icon component instance when endAdornment is visible
93
+ * Uses figma.instance() to get the Icon component instance
94
+ */
95
+ endIcon: figma.instance("endAdornment"),
96
+ },
97
+
98
+ /**
99
+ * EXAMPLE CODE TEMPLATE
100
+ * Horizontal Tab - handles label, startAdornment, and endAdornment visibility
101
+ */
102
+ example: ({ selected, disabled, showLabel, labelText, showStartAdornment, startIcon, showEndAdornment, endIcon }) => {
103
+ // Handle case with label and both adornments
104
+ // Note: MUI Tab only supports one icon, so we prioritize startAdornment
105
+ if (showLabel && labelText && labelText.children && showStartAdornment && startIcon && showEndAdornment && endIcon) {
106
+ return (
107
+ <Tab
108
+ selected={selected}
109
+ disabled={disabled}
110
+ label={labelText.children}
111
+ icon={startIcon}
112
+ iconPosition="start"
113
+ />
114
+ );
115
+ }
116
+
117
+ // Handle case with label and startAdornment only
118
+ if (showLabel && labelText && labelText.children && showStartAdornment && startIcon) {
119
+ return (
120
+ <Tab
121
+ selected={selected}
122
+ disabled={disabled}
123
+ label={labelText.children}
124
+ icon={startIcon}
125
+ iconPosition="start"
126
+ />
127
+ );
128
+ }
129
+
130
+ // Handle case with label and endAdornment only
131
+ if (showLabel && labelText && labelText.children && showEndAdornment && endIcon) {
132
+ return (
133
+ <Tab
134
+ selected={selected}
135
+ disabled={disabled}
136
+ label={labelText.children}
137
+ icon={endIcon}
138
+ iconPosition="end"
139
+ />
140
+ );
141
+ }
142
+
143
+ // Handle case with label only
144
+ if (showLabel && labelText && labelText.children) {
145
+ return (
146
+ <Tab
147
+ selected={selected}
148
+ disabled={disabled}
149
+ label={labelText.children}
150
+ />
151
+ );
152
+ }
153
+
154
+ // Handle case with startAdornment only (no label)
155
+ if (showStartAdornment && startIcon) {
156
+ return (
157
+ <Tab
158
+ selected={selected}
159
+ disabled={disabled}
160
+ icon={startIcon}
161
+ />
162
+ );
163
+ }
164
+
165
+ // Handle case with endAdornment only (no label)
166
+ if (showEndAdornment && endIcon) {
167
+ return (
168
+ <Tab
169
+ selected={selected}
170
+ disabled={disabled}
171
+ icon={endIcon}
172
+ />
173
+ );
174
+ }
175
+
176
+ // Default case - no label, no adornments
177
+ return (
178
+ <Tab
179
+ selected={selected}
180
+ disabled={disabled}
181
+ />
182
+ );
183
+ },
184
+ }
185
+ );
@@ -0,0 +1,65 @@
1
+ import { forwardRef, Ref } from "react";
2
+
3
+ import MuiTab, { TabProps as MuiTabProps } from "@mui/material/Tab";
4
+ import { TouchRippleActions } from "@mui/material/ButtonBase/TouchRipple";
5
+ import { CSSObject, Theme } from "@mui/material/styles";
6
+ import { getSxStyles } from "../utils/styles";
7
+
8
+ export interface TabProps extends MuiTabProps {
9
+ /**
10
+ *
11
+ * A ref that points to the TouchRipple element.
12
+ */
13
+ touchRippleRef?: Ref<TouchRippleActions>;
14
+ }
15
+
16
+ /** Tab
17
+ *
18
+ * `import Tab from '@carrier-io/air-react/Tab'`
19
+ */
20
+ const Tab = forwardRef<HTMLDivElement, TabProps>(({ sx, ...rest }, ref) => {
21
+ return (
22
+ <MuiTab
23
+ sx={(theme: Theme) =>
24
+ ({
25
+ ...getSxStyles(theme, sx),
26
+ ...theme.typography.body2Semibold,
27
+ textTransform: "capitalize",
28
+ // Default/unselected: label and icon use base.state.active
29
+ color: theme.palette.base?.state.active,
30
+ "& .MuiTab-iconWrapper": {
31
+ color: theme.palette.base?.state.active,
32
+ },
33
+ // Hover state: label and icon use base.text.primary
34
+ "&:hover": {
35
+ color: theme.palette.base?.text.primary,
36
+ "& .MuiTab-iconWrapper": {
37
+ color: theme.palette.base?.text.primary,
38
+ },
39
+ },
40
+ // Selected tab: label and icon use base.text.primary
41
+ "&.Mui-selected": {
42
+ color: theme.palette.base?.text.primary,
43
+ "& .MuiTab-iconWrapper": {
44
+ color: theme.palette.base?.text.primary,
45
+ },
46
+ },
47
+ // Disabled tab: label and icon use base.text.disabled
48
+ "&.Mui-disabled": {
49
+ color: theme.palette.base?.text.disabled,
50
+ "& .MuiTab-iconWrapper": {
51
+ color: theme.palette.base?.state.disabledContent,
52
+ },
53
+ },
54
+ } as CSSObject)
55
+ }
56
+ {...rest}
57
+ ref={ref}
58
+ />
59
+ );
60
+ });
61
+
62
+ Tab.displayName = "Tab";
63
+
64
+ export default Tab;
65
+ export type { TabProps };
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Figma Code Connect Configuration for Tab Component (Vertical)
3
+ *
4
+ * Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=23626-42929
5
+ *
6
+ * Figma Properties:
7
+ * - selected (true, false)
8
+ * - invertIndicator (true, false)
9
+ * - disabled (true, false)
10
+ * - state (enabled, hover, focus) - visual state, not a prop
11
+ * - label (true, false) - boolean hide/show
12
+ * - startAdornment (true, false) - boolean hide/show
13
+ * - endAdornment (true, false) - boolean hide/show
14
+ */
15
+
16
+ import figma from "@figma/code-connect";
17
+ import Tab from "./Tab";
18
+
19
+ figma.connect(
20
+ Tab,
21
+ "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=23626-42929",
22
+ {
23
+ props: {
24
+ /**
25
+ * SELECTED STATE
26
+ * Maps Figma's "selected" boolean to React's "selected" prop
27
+ */
28
+ selected: figma.boolean("selected"),
29
+
30
+ /**
31
+ * INVERT INDICATOR
32
+ * Maps Figma's "invertIndicator" boolean
33
+ * Note: This is typically a Tabs-level prop, but may be visually represented in Figma
34
+ */
35
+ invertIndicator: figma.boolean("invertIndicator"),
36
+
37
+ /**
38
+ * DISABLED STATE
39
+ * Maps Figma's "disabled" boolean to React's "disabled" prop
40
+ */
41
+ disabled: figma.boolean("disabled"),
42
+
43
+ /**
44
+ * LABEL VISIBILITY
45
+ * Maps Figma's "label" boolean - controls visibility of label text
46
+ */
47
+ showLabel: figma.boolean("label"),
48
+
49
+ /**
50
+ * LABEL TEXT CONTENT
51
+ * Maps text content from nested "Label" Typography component
52
+ */
53
+ labelText: figma.nestedProps("Label", {
54
+ children: figma.children("Label"),
55
+ }),
56
+
57
+ /**
58
+ * START ADORNMENT VISIBILITY
59
+ * Maps Figma's "startAdornment" boolean - controls visibility of start icon
60
+ */
61
+ showStartAdornment: figma.boolean("startAdornment"),
62
+
63
+ /**
64
+ * START ADORNMENT ICON INSTANCE
65
+ * Maps nested Icon component instance when startAdornment is visible
66
+ * Uses figma.instance() to get the Icon component instance
67
+ */
68
+ startIcon: figma.instance("startAdornment"),
69
+
70
+ /**
71
+ * END ADORNMENT VISIBILITY
72
+ * Maps Figma's "endAdornment" boolean - controls visibility of end icon
73
+ */
74
+ showEndAdornment: figma.boolean("endAdornment"),
75
+
76
+ /**
77
+ * END ADORNMENT ICON INSTANCE
78
+ * Maps nested Icon component instance when endAdornment is visible
79
+ * Uses figma.instance() to get the Icon component instance
80
+ */
81
+ endIcon: figma.instance("endAdornment"),
82
+ },
83
+
84
+ /**
85
+ * EXAMPLE CODE TEMPLATE
86
+ * Vertical Tab - handles label, startAdornment, and endAdornment visibility
87
+ */
88
+ example: ({ selected, disabled, showLabel, labelText, showStartAdornment, startIcon, showEndAdornment, endIcon }) => {
89
+ // Handle case with label and both adornments
90
+ // Note: MUI Tab only supports one icon, so we prioritize startAdornment
91
+ if (showLabel && labelText && labelText.children && showStartAdornment && startIcon && showEndAdornment && endIcon) {
92
+ return (
93
+ <Tab
94
+ selected={selected}
95
+ disabled={disabled}
96
+ label={labelText.children}
97
+ icon={startIcon}
98
+ iconPosition="start"
99
+ />
100
+ );
101
+ }
102
+
103
+ // Handle case with label and startAdornment only
104
+ if (showLabel && labelText && labelText.children && showStartAdornment && startIcon) {
105
+ return (
106
+ <Tab
107
+ selected={selected}
108
+ disabled={disabled}
109
+ label={labelText.children}
110
+ icon={startIcon}
111
+ iconPosition="start"
112
+ />
113
+ );
114
+ }
115
+
116
+ // Handle case with label and endAdornment only
117
+ if (showLabel && labelText && labelText.children && showEndAdornment && endIcon) {
118
+ return (
119
+ <Tab
120
+ selected={selected}
121
+ disabled={disabled}
122
+ label={labelText.children}
123
+ icon={endIcon}
124
+ iconPosition="end"
125
+ />
126
+ );
127
+ }
128
+
129
+ // Handle case with label only
130
+ if (showLabel && labelText && labelText.children) {
131
+ return (
132
+ <Tab
133
+ selected={selected}
134
+ disabled={disabled}
135
+ label={labelText.children}
136
+ />
137
+ );
138
+ }
139
+
140
+ // Handle case with startAdornment only (no label)
141
+ if (showStartAdornment && startIcon) {
142
+ return (
143
+ <Tab
144
+ selected={selected}
145
+ disabled={disabled}
146
+ icon={startIcon}
147
+ />
148
+ );
149
+ }
150
+
151
+ // Handle case with endAdornment only (no label)
152
+ if (showEndAdornment && endIcon) {
153
+ return (
154
+ <Tab
155
+ selected={selected}
156
+ disabled={disabled}
157
+ icon={endIcon}
158
+ />
159
+ );
160
+ }
161
+
162
+ // Default case - no label, no adornments
163
+ return (
164
+ <Tab
165
+ selected={selected}
166
+ disabled={disabled}
167
+ />
168
+ );
169
+ },
170
+ }
171
+ );
@@ -0,0 +1,15 @@
1
+ import MuiTabContext, {
2
+ TabContextProps as MuiTabContextProps,
3
+ } from "@mui/lab/TabContext";
4
+
5
+ export interface TabContextProps extends MuiTabContextProps {}
6
+
7
+ /** TabContext
8
+ *
9
+ * `import { TabContext } from '@carrier-io/air-react'`
10
+ */
11
+ export default function TabContext(props: TabContextProps) {
12
+ return <MuiTabContext {...props} />;
13
+ }
14
+
15
+ export type { TabContextProps };
@@ -0,0 +1,66 @@
1
+ import { forwardRef } from "react";
2
+
3
+ import MuiTabList, { TabListProps as MuiTabListProps } from "@mui/lab/TabList";
4
+
5
+ export interface TabListProps extends MuiTabListProps {
6
+ /**
7
+ *
8
+ * Invert the indicator on the tab. Default set to bottom for "horizontal" orientation and right for "vertical" orientation.
9
+ */
10
+ invertIndicator?: boolean;
11
+
12
+ /**
13
+ * indicator variant
14
+ * @default "line"
15
+ */
16
+ indicatorVariant?: "line" | "standard";
17
+
18
+ /**
19
+ * indicator width
20
+ */
21
+ indicatorWidth?: "content" | "full";
22
+ }
23
+
24
+ /** TabList
25
+ *
26
+ * `import { TabList } from '@carrier-io/air-react'`
27
+ */
28
+
29
+ const indicatorInvertStyle = {
30
+ right: "unset",
31
+ borderBottomRightRadius: 4,
32
+ borderBottomLeftRadius: 0,
33
+ borderTopLeftRadius: 0,
34
+ borderTopRightRadius: 4,
35
+ };
36
+
37
+ const TabList = forwardRef<HTMLButtonElement, TabListProps>(
38
+ ({ invertIndicator, indicatorVariant, indicatorWidth, ...props }, ref) => {
39
+ const rootClassNames = `${
40
+ indicatorWidth === "full"
41
+ ? "MuiTab-indicator__full"
42
+ : "MuiTab-indicator__content"
43
+ }`;
44
+ const indicatorClassNames = `${
45
+ indicatorVariant === "line"
46
+ ? "MuiTab-indicator__line"
47
+ : "MuiTab-indicator__standard"
48
+ }`;
49
+ return (
50
+ <MuiTabList
51
+ {...props}
52
+ ref={ref}
53
+ TabIndicatorProps={{
54
+ ...(invertIndicator ? { style: indicatorInvertStyle } : undefined),
55
+ ...props.TabIndicatorProps,
56
+ }}
57
+ className={[rootClassNames, indicatorClassNames].join(" ")}
58
+ />
59
+ );
60
+ }
61
+ );
62
+
63
+ TabList.displayName = "TabList";
64
+
65
+ export default TabList;
66
+ export type { TabListProps };
@@ -0,0 +1,35 @@
1
+ import { forwardRef } from "react";
2
+
3
+ import MuiTabPanel, {
4
+ TabPanelProps as MuiTabPanelProps,
5
+ } from "@mui/lab/TabPanel";
6
+ import { Theme } from "@mui/material/styles";
7
+ import { getSxStyles } from "../utils/styles";
8
+
9
+ export interface TabPanelProps extends MuiTabPanelProps {}
10
+
11
+ /** TabPanel
12
+ *
13
+ * `import { TabPanel } from '@carrier-io/air-react'`
14
+ */
15
+
16
+ const TabPanel = forwardRef<unknown, TabPanelProps>(({ sx, ...props }, ref) => {
17
+ return (
18
+ <MuiTabPanel
19
+ sx={
20
+ ((theme: Theme) => ({
21
+ ...getSxStyles(theme, sx),
22
+ // Apply base.text.primary color to panel content for dark theme support
23
+ color: theme.palette.base?.text.primary || theme.palette.text.primary,
24
+ })) as any
25
+ }
26
+ {...props}
27
+ ref={ref}
28
+ />
29
+ );
30
+ });
31
+
32
+ TabPanel.displayName = "TabPanel";
33
+
34
+ export default TabPanel;
35
+ export type { TabPanelProps };
@@ -0,0 +1,23 @@
1
+ import { forwardRef } from "react";
2
+
3
+ import MuiTabScrollButton, {
4
+ TabScrollButtonProps as MuiTabScrollButtonProps,
5
+ } from "@mui/material/TabScrollButton";
6
+
7
+ export interface TabScrollButtonProps extends MuiTabScrollButtonProps {}
8
+
9
+ /** TabScrollButton
10
+ *
11
+ * `import { TabScrollButton } from '@carrier-io/air-react'`
12
+ */
13
+
14
+ const TabScrollButton = forwardRef<HTMLDivElement, TabScrollButtonProps>(
15
+ (props, ref) => {
16
+ return <MuiTabScrollButton {...props} ref={ref} />;
17
+ }
18
+ );
19
+
20
+ TabScrollButton.displayName = "TabScrollButton";
21
+
22
+ export default TabScrollButton;
23
+ export type { TabScrollButtonProps };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Figma Code Connect Configuration for Tabs Component (Horizontal)
3
+ *
4
+ * Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=17801-86244
5
+ *
6
+ * Figma Properties:
7
+ * - variant (group-left, group-center, group-right, full-width, scrollable)
8
+ * - disabled (true, false)
9
+ * - divider (true, false)
10
+ *
11
+ * Structure:
12
+ * - Tab 1-8: Multiple Tab instances with numbered layer names
13
+ */
14
+
15
+ import figma from "@figma/code-connect";
16
+ import Tabs from "./Tabs";
17
+ import Tab from "./Tab";
18
+
19
+ figma.connect(
20
+ Tabs,
21
+ "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=17801-86244",
22
+ {
23
+ props: {
24
+ /**
25
+ * VARIANT MAPPING
26
+ * Maps Figma's "variant" property to React's "variant" prop
27
+ */
28
+ variant: figma.enum("variant", {
29
+ "group-left": "group-left",
30
+ "group-center": "group-center",
31
+ "group-right": "group-right",
32
+ "full-width": "full-width",
33
+ scrollable: "scrollable",
34
+ }),
35
+
36
+ /**
37
+ * DISABLED STATE
38
+ * Maps Figma's "disabled" boolean to React's "disabled" prop
39
+ */
40
+ disabled: figma.boolean("disabled"),
41
+
42
+ /**
43
+ * DIVIDER VISIBILITY
44
+ * Maps Figma's "divider" boolean - controls divider visibility
45
+ */
46
+ divider: figma.boolean("divider"),
47
+
48
+ /**
49
+ * TAB CHILDREN
50
+ * Maps nested Tab instances from Figma
51
+ * Each "Tab X" is an instance of the Tab component
52
+ */
53
+ tab1: figma.children("Tab 1"),
54
+ tab2: figma.children("Tab 2"),
55
+ tab3: figma.children("Tab 3"),
56
+ tab4: figma.children("Tab 4"),
57
+ tab5: figma.children("Tab 5"),
58
+ tab6: figma.children("Tab 6"),
59
+ tab7: figma.children("Tab 7"),
60
+ tab8: figma.children("Tab 8"),
61
+ },
62
+
63
+ /**
64
+ * EXAMPLE CODE TEMPLATE
65
+ * Horizontal Tabs - sets orientation="horizontal"
66
+ */
67
+ example: ({ variant, disabled, tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8 }) => (
68
+ <Tabs
69
+ orientation="horizontal"
70
+ variant={variant}
71
+ disabled={disabled}
72
+ value={0}
73
+ onChange={() => {}}
74
+ >
75
+ {tab1}
76
+ {tab2}
77
+ {tab3}
78
+ {tab4}
79
+ {tab5}
80
+ {tab6}
81
+ {tab7}
82
+ {tab8}
83
+ </Tabs>
84
+ ),
85
+ }
86
+ );
@@ -0,0 +1,160 @@
1
+ import { forwardRef, Ref } from "react";
2
+
3
+ import MuiTabs, { TabsProps as MuiTabsProps } from "@mui/material/Tabs";
4
+ import { TouchRippleActions } from "@mui/material/ButtonBase/TouchRipple";
5
+ import { baseTabsSx } from "./styles";
6
+ import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
7
+ import ChevronRightIcon from "@mui/icons-material/ChevronRight";
8
+ import TabScrollButton, {
9
+ TabScrollButtonProps,
10
+ } from "@mui/material/TabScrollButton";
11
+
12
+ export interface TabsProps extends Omit<MuiTabsProps, "centered" | "variant"> {
13
+ /**
14
+ *
15
+ * A ref that points to the TouchRipple element.
16
+ */
17
+ touchRippleRef?: Ref<TouchRippleActions>;
18
+ /**
19
+ *
20
+ * Invert the indicator on the tab. Default set to bottom for "horizontal" orientation and right for "vertical" orientation.
21
+ */
22
+ invertIndicator?: boolean;
23
+
24
+ /**
25
+ * indicator variant
26
+ * @default "line"
27
+ */
28
+ indicatorVariant?: "line" | "standard";
29
+
30
+ /**
31
+ * indicator width
32
+ */
33
+ indicatorWidth?: "content" | "full";
34
+
35
+ /**
36
+ * Determines additional display behavior of the tabs:
37
+ *
38
+ * - `scrollable` will invoke scrolling properties and allow for horizontally
39
+ * scrolling (or swiping) of the tab bar.
40
+ * - `fullWidth` will make the tabs grow to use all the available space,
41
+ * which should be used for small views, like on mobile.
42
+ * - `group-left` will align the tabs to the left within the available space.
43
+ * - `group-center` will center-align the tabs within the available space.
44
+ * - `group-right` will align the tabs to the right within the available space.
45
+ * @default 'group-left'
46
+ */
47
+ variant?:
48
+ | "scrollable"
49
+ | "full-width"
50
+ | "group-left"
51
+ | "group-center"
52
+ | "group-right";
53
+ }
54
+
55
+ const indicatorInvertStyle = {
56
+ bottom: "unset",
57
+ top: 0,
58
+ borderBottomRightRadius: 4,
59
+ borderBottomLeftRadius: 4,
60
+ borderTopLeftRadius: 0,
61
+ borderTopRightRadius: 0,
62
+ };
63
+
64
+ const ScrollButton = ({
65
+ direction,
66
+ onClick,
67
+ orientation = "horizontal",
68
+ ...props
69
+ }: TabScrollButtonProps) => {
70
+ return (
71
+ <TabScrollButton
72
+ orientation={orientation}
73
+ {...props}
74
+ direction={direction}
75
+ onClick={onClick}
76
+ >
77
+ {direction === "left" ? (
78
+ <ChevronLeftIcon fontSize="small" />
79
+ ) : (
80
+ <ChevronRightIcon fontSize="small" />
81
+ )}
82
+ </TabScrollButton>
83
+ );
84
+ };
85
+
86
+ /** The Tabs component is useful for organizing views of related content and allowing users to switch between them.
87
+ *
88
+ * `import { Tabs, Tab, TabContext, TabList, TabPanel, TabScrollButton } from '@carrier-io/air-react'`
89
+ */
90
+
91
+ const Tabs = forwardRef<HTMLButtonElement, TabsProps>(
92
+ (
93
+ {
94
+ invertIndicator,
95
+ indicatorVariant = "line",
96
+ indicatorWidth = "content",
97
+ variant = "group-left",
98
+ visibleScrollbar = false,
99
+ ...props
100
+ },
101
+ ref
102
+ ) => {
103
+ const rootClassNames = `${
104
+ indicatorWidth === "full"
105
+ ? "MuiTab-indicator__full"
106
+ : "MuiTab-indicator__content"
107
+ }`;
108
+ const indicatorClassNames = `${
109
+ indicatorVariant === "line"
110
+ ? "MuiTab-indicator__line"
111
+ : "MuiTab-indicator__standard"
112
+ }`;
113
+
114
+ let justifyContentValue: "left" | "right" | "center" | undefined;
115
+ let componentVariant: "standard" | "scrollable" | "fullWidth" | undefined =
116
+ "standard";
117
+
118
+ switch (variant) {
119
+ case "group-center":
120
+ justifyContentValue = "center";
121
+ break;
122
+ case "group-left":
123
+ justifyContentValue = "left";
124
+ break;
125
+ case "group-right":
126
+ justifyContentValue = "right";
127
+ break;
128
+ case "full-width":
129
+ justifyContentValue = undefined;
130
+ componentVariant = "fullWidth";
131
+ break;
132
+ default:
133
+ justifyContentValue = undefined;
134
+ componentVariant = variant;
135
+ break;
136
+ }
137
+
138
+ return (
139
+ <MuiTabs
140
+ {...props}
141
+ ref={ref}
142
+ visibleScrollbar={visibleScrollbar}
143
+ ScrollButtonComponent={ScrollButton}
144
+ variant={componentVariant}
145
+ allowScrollButtonsMobile={props.allowScrollButtonsMobile || false}
146
+ sx={baseTabsSx(justifyContentValue)}
147
+ className={[rootClassNames, indicatorClassNames].join(" ")}
148
+ TabIndicatorProps={{
149
+ ...(invertIndicator ? { style: indicatorInvertStyle } : undefined),
150
+ ...props.TabIndicatorProps,
151
+ }}
152
+ />
153
+ );
154
+ }
155
+ );
156
+
157
+ Tabs.displayName = "Tabs";
158
+
159
+ export default Tabs;
160
+ export type { TabsProps };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Figma Code Connect Configuration for Tabs Component (Vertical)
3
+ *
4
+ * Figma URL: https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=23641-53725
5
+ *
6
+ * Figma Properties:
7
+ * - scrollable (true, false)
8
+ * - disabled (true, false)
9
+ * - divider (true, false)
10
+ *
11
+ * Structure:
12
+ * - Tab 1-8: Multiple Tab instances with numbered layer names
13
+ */
14
+
15
+ import figma from "@figma/code-connect";
16
+ import Tabs from "./Tabs";
17
+ import Tab from "./Tab";
18
+
19
+ figma.connect(
20
+ Tabs,
21
+ "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=23641-53725",
22
+ {
23
+ props: {
24
+ /**
25
+ * SCROLLABLE VARIANT
26
+ * Maps Figma's "scrollable" boolean to React's "variant" prop
27
+ * When true, variant="scrollable", otherwise variant="group-left" (default)
28
+ */
29
+ scrollable: figma.boolean("scrollable"),
30
+
31
+ /**
32
+ * DISABLED STATE
33
+ * Maps Figma's "disabled" boolean to React's "disabled" prop
34
+ */
35
+ disabled: figma.boolean("disabled"),
36
+
37
+ /**
38
+ * DIVIDER VISIBILITY
39
+ * Maps Figma's "divider" boolean - controls divider visibility
40
+ */
41
+ divider: figma.boolean("divider"),
42
+
43
+ /**
44
+ * TAB CHILDREN
45
+ * Maps nested Tab instances from Figma
46
+ * Each "Tab X" is an instance of the Tab component
47
+ */
48
+ tab1: figma.children("Tab 1"),
49
+ tab2: figma.children("Tab 2"),
50
+ tab3: figma.children("Tab 3"),
51
+ tab4: figma.children("Tab 4"),
52
+ tab5: figma.children("Tab 5"),
53
+ tab6: figma.children("Tab 6"),
54
+ tab7: figma.children("Tab 7"),
55
+ tab8: figma.children("Tab 8"),
56
+ },
57
+
58
+ /**
59
+ * EXAMPLE CODE TEMPLATE
60
+ * Vertical Tabs - sets orientation="vertical"
61
+ */
62
+ example: ({ scrollable, disabled, tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8 }) => {
63
+ let variant: "scrollable" | "group-left" = "group-left";
64
+ if (scrollable) {
65
+ variant = "scrollable";
66
+ }
67
+
68
+ return (
69
+ <Tabs
70
+ orientation="vertical"
71
+ variant={variant}
72
+ disabled={disabled}
73
+ value={0}
74
+ onChange={() => {}}
75
+ >
76
+ {tab1}
77
+ {tab2}
78
+ {tab3}
79
+ {tab4}
80
+ {tab5}
81
+ {tab6}
82
+ {tab7}
83
+ {tab8}
84
+ </Tabs>
85
+ );
86
+ },
87
+ }
88
+ );
@@ -0,0 +1,19 @@
1
+ // Tabs component family exports
2
+ export { default } from "./Tabs"; // For: import Tabs from "Tabs"
3
+ export { default as Tabs } from "./Tabs"; // For: import { Tabs } from "Tabs"
4
+ export * from "./Tabs"; // For: TabsProps, etc.
5
+
6
+ export { default as Tab } from "./Tab";
7
+ export * from "./Tab";
8
+
9
+ export { default as TabContext } from "./TabContext";
10
+ export * from "./TabContext";
11
+
12
+ export { default as TabList } from "./TabList";
13
+ export * from "./TabList";
14
+
15
+ export { default as TabPanel } from "./TabPanel";
16
+ export * from "./TabPanel";
17
+
18
+ export { default as TabScrollButton } from "./TabScrollButton";
19
+ export * from "./TabScrollButton";
@@ -0,0 +1,9 @@
1
+ import { CSSObject } from "@mui/material";
2
+
3
+ export const baseTabsSx = (
4
+ justifyContentValue: "left" | "right" | "center" | undefined
5
+ ): CSSObject => ({
6
+ "& .MuiTabs-flexContainer": {
7
+ justifyContent: justifyContentValue,
8
+ },
9
+ });
@@ -40,6 +40,7 @@ figma.connect(
40
40
  * purple -> primary, cyan -> info, orange -> warning, pink -> error
41
41
  */
42
42
  color: figma.enum("color", {
43
+ base: "base",
43
44
  purple: "primary",
44
45
  primary: "primary",
45
46
  info: "info",
package/src/index.ts CHANGED
@@ -63,6 +63,15 @@ export { default as Tag } from "./components/Tag";
63
63
  export type { TagProps } from "./components/Tag";
64
64
  export { default as Breadcrumbs, BreadcrumbLink } from "./components/Breadcrumbs";
65
65
  export type { BreadcrumbsProps, BreadcrumbLinkProps } from "./components/Breadcrumbs";
66
+ export * from "./components/Tabs";
67
+ export type {
68
+ TabsProps,
69
+ TabProps,
70
+ TabListProps,
71
+ TabPanelProps,
72
+ TabContextProps,
73
+ TabScrollButtonProps,
74
+ } from "./components/Tabs";
66
75
  export * from "./components/theme";
67
76
 
68
77
  // Demo Icons - exported from main index to avoid deep-path imports
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for ArrowLeft Icon
3
- *
4
- * NOTE: Import ArrowLeftIcon from your actual icon library package.
5
- * Example: import { ArrowLeftIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { ArrowLeftIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual ArrowLeftIcon import
14
- const ArrowLeftIcon = ({ variant, ...props }: any) => <span {...props}>ArrowLeftIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // ArrowLeftIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=ARROW-LEFT-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <ArrowLeftIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for ArrowRight Icon
3
- *
4
- * NOTE: Import ArrowRightIcon from your actual icon library package.
5
- * Example: import { ArrowRightIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { ArrowRightIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual ArrowRightIcon import
14
- const ArrowRightIcon = ({ variant, ...props }: any) => <span {...props}>ArrowRightIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // ArrowRightIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=ARROW-RIGHT-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <ArrowRightIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Check Icon
3
- *
4
- * NOTE: Import CheckIcon from your actual icon library package.
5
- * Example: import { CheckIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { CheckIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual CheckIcon import
14
- const CheckIcon = ({ variant, ...props }: any) => <span {...props}>CheckIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // CheckIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=CHECK-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <CheckIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Close Icon
3
- *
4
- * NOTE: Import CloseIcon from your actual icon library package.
5
- * Example: import { CloseIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { CloseIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual CloseIcon import
14
- const CloseIcon = ({ variant, ...props }: any) => <span {...props}>CloseIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // CloseIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=CLOSE-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <CloseIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Home Icon
3
- *
4
- * NOTE: Import HomeIcon from your actual icon library package.
5
- * Example: import { HomeIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { HomeIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual HomeIcon import
14
- const HomeIcon = ({ variant, ...props }: any) => <span {...props}>HomeIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // HomeIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=HOME-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <HomeIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Info Icon
3
- *
4
- * NOTE: Import InfoIcon from your actual icon library package.
5
- * Example: import { InfoIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { InfoIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual InfoIcon import
14
- const InfoIcon = ({ variant, ...props }: any) => <span {...props}>InfoIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // InfoIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=INFO-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <InfoIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Menu Icon
3
- *
4
- * NOTE: Import MenuIcon from your actual icon library package.
5
- * Example: import { MenuIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { MenuIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual MenuIcon import
14
- const MenuIcon = ({ variant, ...props }: any) => <span {...props}>MenuIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // MenuIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=MENU-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <MenuIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Search Icon
3
- *
4
- * NOTE: Import SearchIcon from your actual icon library package.
5
- * Example: import { SearchIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { SearchIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual SearchIcon import
14
- const SearchIcon = ({ variant, ...props }: any) => <span {...props}>SearchIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // SearchIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=SEARCH-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <SearchIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );
@@ -1,61 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for Settings Icon
3
- *
4
- * This connects Figma's SettingsIcon component to the React SettingsIcon component.
5
- * The icon component is wrapped in the Icon wrapper for fontSize control.
6
- *
7
- * Figma URL: https://www.figma.com/design/RT43n0bKuuIt7ylllD3DR0/Icon-Library?node-id=31-2
8
- *
9
- * NOTE: Import SettingsIcon from your actual icon library package.
10
- * Example: import { SettingsIcon } from '@carrier-io/icons';
11
- */
12
-
13
- import figma from "@figma/code-connect";
14
- import Icon from "./Icon";
15
- // TODO: Import from your actual icon library
16
- // import { SettingsIcon } from '@carrier-io/icons';
17
-
18
- // Placeholder - replace with actual SettingsIcon import
19
- const SettingsIcon = ({ variant, ...props }: any) => <span {...props}>SettingsIcon</span>;
20
-
21
- figma.connect(
22
- SettingsIcon,
23
- "https://www.figma.com/design/RT43n0bKuuIt7ylllD3DR0/Icon-Library?node-id=31-2",
24
- {
25
- props: {
26
- /**
27
- * STYLE MAPPING
28
- * Maps Figma's "Style" property to the icon component's "variant" prop
29
- * Figma uses "Style" with values "Outlined" and "Filled"
30
- * React icon component expects "variant" with values "outlined" and "filled"
31
- */
32
- variant: figma.enum("Style", {
33
- Outlined: "outlined",
34
- Filled: "filled",
35
- }),
36
-
37
- /**
38
- * FONT SIZE MAPPING (optional)
39
- * If your Figma icon component has a fontSize property, map it here
40
- * Otherwise, fontSize is set on the Icon wrapper component
41
- */
42
- // fontSize: figma.enum("fontSize", {
43
- // "xsmall-12px": "xsmall",
44
- // "small-16px": "small",
45
- // "medium-24px": "medium",
46
- // "large-32px": "large",
47
- // }),
48
- },
49
- /**
50
- * EXAMPLE CODE TEMPLATE
51
- * Shows the icon component wrapped in Icon for fontSize control.
52
- * The Icon wrapper handles fontSize (xsmall, small, medium, large).
53
- * The icon component handles variant (outlined, filled).
54
- */
55
- example: ({ variant }) => (
56
- <Icon fontSize="medium">
57
- <SettingsIcon variant={variant} />
58
- </Icon>
59
- ),
60
- }
61
- );
@@ -1,33 +0,0 @@
1
- /**
2
- * Figma Code Connect Configuration for User Icon
3
- *
4
- * NOTE: Import UserIcon from your actual icon library package.
5
- * Example: import { UserIcon } from '@carrier-io/icons';
6
- */
7
-
8
- import figma from "@figma/code-connect";
9
- import Icon from "./Icon";
10
- // TODO: Import from your actual icon library
11
- // import { UserIcon } from '@carrier-io/icons';
12
-
13
- // Placeholder - replace with actual UserIcon import
14
- const UserIcon = ({ variant, ...props }: any) => <span {...props}>UserIcon</span>;
15
-
16
- // TODO: Uncomment and update with real Figma URL when ready
17
- // figma.connect(
18
- // UserIcon,
19
- // "https://www.figma.com/design/vkoHdM6rchIhH9IWetZeP0/Air--Components?node-id=USER-ICON-NODE-ID",
20
- // {
21
- // props: {
22
- // variant: figma.enum("variant", {
23
- // outlined: "outlined",
24
- // filled: "filled",
25
- // }),
26
- // },
27
- // example: ({ variant }) => (
28
- // <Icon fontSize="medium">
29
- // <UserIcon variant={variant} />
30
- // </Icon>
31
- // ),
32
- // }
33
- // );