@superdispatch/ui-lab 0.21.11 → 0.21.13

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.
Files changed (97) hide show
  1. package/.babelrc.js +5 -0
  2. package/.turbo/turbo-version.log +28 -0
  3. package/package.json +39 -13
  4. package/pkg/README.md +10 -0
  5. package/{dist-node → pkg/dist-node}/index.js +2 -4
  6. package/pkg/dist-node/index.js.map +1 -0
  7. package/{dist-src → pkg/dist-src}/alert/Alert.js +2 -4
  8. package/{dist-web → pkg/dist-web}/index.js +2 -4
  9. package/pkg/dist-web/index.js.map +1 -0
  10. package/pkg/package.json +34 -0
  11. package/playroom.ts +23 -0
  12. package/src/alert/Alert.stories.tsx +105 -0
  13. package/src/alert/Alert.tsx +108 -0
  14. package/src/banner/Banner.stories.tsx +64 -0
  15. package/src/banner/Banner.tsx +120 -0
  16. package/src/box/Box.stories.tsx +20 -0
  17. package/src/box/Box.tsx +252 -0
  18. package/src/button/Button.stories.tsx +717 -0
  19. package/src/button/Button.tsx +460 -0
  20. package/src/button-area/ButtonArea.stories.tsx +65 -0
  21. package/src/button-area/ButtonArea.tsx +88 -0
  22. package/src/container/Container.tsx +48 -0
  23. package/src/description-item/DescriptionItem.stories.tsx +163 -0
  24. package/src/description-item/DescriptionItem.tsx +104 -0
  25. package/src/file-drop-zone/FileDropZone.stories.tsx +44 -0
  26. package/src/file-drop-zone/FileDropZone.tsx +170 -0
  27. package/src/file-list-item/FileListItem.stories.tsx +37 -0
  28. package/src/file-list-item/FileListItem.tsx +145 -0
  29. package/src/file-list-item/__tests__/FileListItem.spec.tsx +339 -0
  30. package/src/index.spec.ts +43 -0
  31. package/src/index.ts +28 -0
  32. package/src/linked-text/LinkeText.stories.tsx +42 -0
  33. package/src/linked-text/LinkedText.tsx +47 -0
  34. package/src/multiline-text/MultilineText.stories.tsx +30 -0
  35. package/src/multiline-text/MultilineText.ts +16 -0
  36. package/src/navbar/Navbar.stories.tsx +135 -0
  37. package/src/navbar/Navbar.tsx +111 -0
  38. package/src/navbar/NavbarAccordion.tsx +171 -0
  39. package/src/navbar/NavbarAvatar.tsx +51 -0
  40. package/src/navbar/NavbarBottomBar.tsx +135 -0
  41. package/src/navbar/NavbarContext.tsx +23 -0
  42. package/src/navbar/NavbarItem.tsx +119 -0
  43. package/src/navbar/NavbarList.tsx +225 -0
  44. package/src/navbar/NavbarMenu.tsx +102 -0
  45. package/src/sidebar/Sidebar.stories.tsx +363 -0
  46. package/src/sidebar/Sidebar.tsx +73 -0
  47. package/src/sidebar/SidebarBackButton.tsx +33 -0
  48. package/src/sidebar/SidebarContainer.tsx +114 -0
  49. package/src/sidebar/SidebarContent.tsx +119 -0
  50. package/src/sidebar/SidebarDivider.tsx +15 -0
  51. package/src/sidebar/SidebarMenuItem.tsx +211 -0
  52. package/src/sidebar/SidebarMenuItemAction.tsx +27 -0
  53. package/src/sidebar/SidebarMenuItemAvatar.tsx +59 -0
  54. package/src/sidebar/SidebarMenuItemContext.tsx +33 -0
  55. package/src/sidebar/SidebarSubheader.tsx +38 -0
  56. package/src/styled.d.ts +12 -0
  57. package/src/text-box/TextBox.stories.tsx +108 -0
  58. package/src/text-box/TextBox.tsx +229 -0
  59. package/src/utils/RuleNormalizer.ts +24 -0
  60. package/src/utils/mergeStyles.ts +28 -0
  61. package/tsconfig.json +19 -0
  62. package/LICENSE +0 -21
  63. package/dist-node/index.js.map +0 -1
  64. package/dist-web/index.js.map +0 -1
  65. /package/{dist-src → pkg/dist-src}/banner/Banner.js +0 -0
  66. /package/{dist-src → pkg/dist-src}/box/Box.js +0 -0
  67. /package/{dist-src → pkg/dist-src}/button/Button.js +0 -0
  68. /package/{dist-src → pkg/dist-src}/button-area/ButtonArea.js +0 -0
  69. /package/{dist-src → pkg/dist-src}/container/Container.js +0 -0
  70. /package/{dist-src → pkg/dist-src}/description-item/DescriptionItem.js +0 -0
  71. /package/{dist-src → pkg/dist-src}/file-drop-zone/FileDropZone.js +0 -0
  72. /package/{dist-src → pkg/dist-src}/file-list-item/FileListItem.js +0 -0
  73. /package/{dist-src → pkg/dist-src}/index.js +0 -0
  74. /package/{dist-src → pkg/dist-src}/linked-text/LinkedText.js +0 -0
  75. /package/{dist-src → pkg/dist-src}/multiline-text/MultilineText.js +0 -0
  76. /package/{dist-src → pkg/dist-src}/navbar/Navbar.js +0 -0
  77. /package/{dist-src → pkg/dist-src}/navbar/NavbarAccordion.js +0 -0
  78. /package/{dist-src → pkg/dist-src}/navbar/NavbarAvatar.js +0 -0
  79. /package/{dist-src → pkg/dist-src}/navbar/NavbarBottomBar.js +0 -0
  80. /package/{dist-src → pkg/dist-src}/navbar/NavbarContext.js +0 -0
  81. /package/{dist-src → pkg/dist-src}/navbar/NavbarItem.js +0 -0
  82. /package/{dist-src → pkg/dist-src}/navbar/NavbarList.js +0 -0
  83. /package/{dist-src → pkg/dist-src}/navbar/NavbarMenu.js +0 -0
  84. /package/{dist-src → pkg/dist-src}/sidebar/Sidebar.js +0 -0
  85. /package/{dist-src → pkg/dist-src}/sidebar/SidebarBackButton.js +0 -0
  86. /package/{dist-src → pkg/dist-src}/sidebar/SidebarContainer.js +0 -0
  87. /package/{dist-src → pkg/dist-src}/sidebar/SidebarContent.js +0 -0
  88. /package/{dist-src → pkg/dist-src}/sidebar/SidebarDivider.js +0 -0
  89. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItem.js +0 -0
  90. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemAction.js +0 -0
  91. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemAvatar.js +0 -0
  92. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemContext.js +0 -0
  93. /package/{dist-src → pkg/dist-src}/sidebar/SidebarSubheader.js +0 -0
  94. /package/{dist-src → pkg/dist-src}/text-box/TextBox.js +0 -0
  95. /package/{dist-src → pkg/dist-src}/utils/RuleNormalizer.js +0 -0
  96. /package/{dist-src → pkg/dist-src}/utils/mergeStyles.js +0 -0
  97. /package/{dist-types → pkg/dist-types}/index.d.ts +0 -0
@@ -0,0 +1,111 @@
1
+ import { Drawer, useMediaQuery, useTheme } from '@material-ui/core';
2
+ import { useResponsiveValue } from '@superdispatch/ui';
3
+ import {
4
+ CSSProperties,
5
+ ReactElement,
6
+ ReactNode,
7
+ useMemo,
8
+ useState,
9
+ } from 'react';
10
+ import styled from 'styled-components';
11
+ import { NavbarBottomBar, NavbarBottomBarItem } from './NavbarBottomBar';
12
+ import { NavbarContext, NavbarContextType } from './NavbarContext';
13
+ import { NavbarItemOptions, NavbarList } from './NavbarList';
14
+
15
+ const Aside = styled.aside`
16
+ display: flex;
17
+ flex-direction: column;
18
+ overflow: auto;
19
+ `;
20
+
21
+ const Main = styled.main`
22
+ flex: 1;
23
+ display: flex;
24
+ flex-direction: column;
25
+ overflow: scroll;
26
+ `;
27
+
28
+ interface NavbarProps {
29
+ containerStyle?: CSSProperties;
30
+ children: ReactNode;
31
+
32
+ header?: ReactNode;
33
+ items: NavbarItemOptions[];
34
+ bottomItems: NavbarBottomBarItem[];
35
+ footer?: ReactNode;
36
+
37
+ hasExtraBadge?: boolean;
38
+ }
39
+
40
+ export function Navbar({
41
+ footer,
42
+ items,
43
+ header,
44
+ bottomItems,
45
+ children,
46
+ containerStyle,
47
+ hasExtraBadge,
48
+ }: NavbarProps): ReactElement {
49
+ const theme = useTheme();
50
+ const [isDrawerOpen, setDrawerOpen] = useState(false);
51
+
52
+ const platform = useResponsiveValue('mobile', 'tablet', 'desktop');
53
+ const isMobile = platform === 'mobile';
54
+
55
+ const matches = useMediaQuery(theme.breakpoints.up('md'), { noSsr: true });
56
+ const [isMenuExpanded, setMenuExpanded] = useState(matches);
57
+
58
+ const hasBadge = hasExtraBadge || items.some((item) => item.badge);
59
+
60
+ const ctx = useMemo<NavbarContextType>(
61
+ () => ({
62
+ isDrawerOpen,
63
+ isMenuExpanded,
64
+ setDrawerOpen,
65
+ setMenuExpanded,
66
+ isNavbarExpanded: isMenuExpanded || isDrawerOpen,
67
+ }),
68
+ [isDrawerOpen, isMenuExpanded, setMenuExpanded, setDrawerOpen],
69
+ );
70
+
71
+ return (
72
+ <NavbarContext.Provider value={ctx}>
73
+ <div
74
+ style={{
75
+ display: 'flex',
76
+ height: '100%',
77
+ flexDirection: isMobile ? 'column' : 'row',
78
+ ...containerStyle,
79
+ }}
80
+ >
81
+ {!isMobile && (
82
+ <Aside>
83
+ <NavbarList header={header} items={items} footer={footer} />
84
+ </Aside>
85
+ )}
86
+
87
+ <Main>{children}</Main>
88
+
89
+ {isMobile && (
90
+ <NavbarBottomBar items={bottomItems} hasMenuBadge={hasBadge} />
91
+ )}
92
+
93
+ <Drawer
94
+ open={isDrawerOpen}
95
+ anchor="right"
96
+ onClose={() => {
97
+ setDrawerOpen(false);
98
+ }}
99
+ PaperProps={{
100
+ style: {
101
+ width: '280px',
102
+ minWidth: '280px',
103
+ },
104
+ }}
105
+ >
106
+ <NavbarList header={header} items={items} footer={footer} />
107
+ </Drawer>
108
+ </div>
109
+ </NavbarContext.Provider>
110
+ );
111
+ }
@@ -0,0 +1,171 @@
1
+ import { Accordion, AccordionSummary } from '@material-ui/core';
2
+ import { ExpandMore } from '@material-ui/icons';
3
+ import { Color, useUID } from '@superdispatch/ui';
4
+ import {
5
+ MouseEvent,
6
+ ReactElement,
7
+ ReactNode,
8
+ useEffect,
9
+ useState,
10
+ } from 'react';
11
+ import styled from 'styled-components';
12
+ import { useNavbarContext } from './NavbarContext';
13
+ import { NavbarItem } from './NavbarItem';
14
+ import { NavbarItemOptions } from './NavbarList';
15
+
16
+ export const NavbarAccordionLabel = styled.span`
17
+ flex-grow: 1;
18
+ overflow: hidden;
19
+ white-space: nowrap;
20
+ text-overflow: ellipsis;
21
+ &[data-expanded='false'] {
22
+ display: none;
23
+ }
24
+ `;
25
+
26
+ const NavbarAccordionRoot = styled(Accordion)`
27
+ width: 100%;
28
+ color: #c2c4c9;
29
+ font-size: 14px;
30
+ font-weight: 400;
31
+ line-height: 20px;
32
+ text-decoration: none;
33
+ outline: none;
34
+ cursor: pointer;
35
+ background-color: #1b2638;
36
+
37
+ &[aria-current] {
38
+ background-color: #2f394a;
39
+ color: ${Color.White};
40
+ }
41
+
42
+ &.MuiAccordion-root:before {
43
+ background-color: #1b2638;
44
+ }
45
+
46
+ &.MuiPaper-elevation0 {
47
+ border: 0px;
48
+ }
49
+
50
+ &[data-gutter] {
51
+ margin-top: 16px;
52
+ }
53
+
54
+ &[data-gutter].MuiAccordion-root.Mui-expanded {
55
+ margin-top: 16px;
56
+ }
57
+ `;
58
+
59
+ const NavbarAccordionSummary = styled(AccordionSummary)`
60
+ border-left: 4px solid transparent;
61
+ padding-left: 20px;
62
+
63
+ &.MuiAccordionSummary-root {
64
+ max-height: 40px;
65
+ min-height: 40px;
66
+ }
67
+
68
+ &.MuiAccordionSummary-content {
69
+ align-items: center;
70
+ }
71
+
72
+ &:hover,
73
+ &[aria-current],
74
+ &[data-active='true'] {
75
+ color: ${Color.White};
76
+ background-color: #2f394a;
77
+ border-left-color: ${Color.Blue300};
78
+ }
79
+
80
+ &[data-expanded='false'] {
81
+ .MuiAccordionSummary-expandIcon {
82
+ display: none;
83
+ }
84
+ }
85
+ `;
86
+
87
+ const IconWrapper = styled.div`
88
+ width: 24px;
89
+ margin-right: 8px;
90
+
91
+ & svg {
92
+ font-size: 24px;
93
+ }
94
+ `;
95
+
96
+ export interface NavbarAccordionProps {
97
+ label: ReactNode;
98
+ icon?: ReactNode;
99
+ gutter?: boolean;
100
+ items: Array<Omit<NavbarItemOptions, 'icon'>>;
101
+ onClick?: (event: MouseEvent<HTMLDivElement>) => void;
102
+ }
103
+
104
+ export function NavbarAccordion({
105
+ label,
106
+ icon,
107
+ gutter,
108
+ items,
109
+ onClick,
110
+ }: NavbarAccordionProps): ReactElement {
111
+ const uid = useUID();
112
+ const { setDrawerOpen, isNavbarExpanded } = useNavbarContext();
113
+
114
+ const [isExpanded, setExpanded] = useState(true);
115
+
116
+ // sync accordion state with Desktop menu state
117
+ useEffect(() => {
118
+ setExpanded(isNavbarExpanded);
119
+ }, [isNavbarExpanded]);
120
+
121
+ return (
122
+ <NavbarAccordionRoot
123
+ square={true}
124
+ data-gutter={!gutter}
125
+ aria-labelledby={uid}
126
+ expanded={isExpanded}
127
+ onClick={(event) => {
128
+ onClick?.(event);
129
+
130
+ if (isNavbarExpanded) {
131
+ setExpanded(!isExpanded);
132
+ }
133
+ }}
134
+ >
135
+ <NavbarAccordionSummary
136
+ data-active={!isExpanded && items.some((item) => item.active)}
137
+ data-expanded={isNavbarExpanded}
138
+ expandIcon={<ExpandMore />}
139
+ >
140
+ <IconWrapper>{icon}</IconWrapper>
141
+ <NavbarAccordionLabel id={uid} data-expanded={isNavbarExpanded}>
142
+ {label}
143
+ </NavbarAccordionLabel>
144
+ </NavbarAccordionSummary>
145
+
146
+ {items.map((item) => {
147
+ const index = items.indexOf(item);
148
+ const prev = items[index - 1];
149
+ const { ident = 0 } = item;
150
+
151
+ return (
152
+ <NavbarItem
153
+ {...item}
154
+ key={item.key}
155
+ ident={ident}
156
+ active={item.active}
157
+ gutter={prev && prev.groupKey !== item.groupKey}
158
+ onClick={(event) => {
159
+ event.stopPropagation();
160
+ item.onClick?.(event);
161
+
162
+ if (!event.isDefaultPrevented()) {
163
+ setDrawerOpen(false);
164
+ }
165
+ }}
166
+ />
167
+ );
168
+ })}
169
+ </NavbarAccordionRoot>
170
+ );
171
+ }
@@ -0,0 +1,51 @@
1
+ import { Avatar, AvatarProps, Typography } from '@material-ui/core';
2
+ import { Color, Column, Columns, Stack } from '@superdispatch/ui';
3
+ import { ReactElement, ReactNode } from 'react';
4
+ import styled from 'styled-components';
5
+ import { useNavbarContext } from './NavbarContext';
6
+
7
+ const StyledTypography = styled(Typography)`
8
+ color: ${Color.Silver500};
9
+ text-overflow: ellipsis;
10
+ white-space: nowrap;
11
+ overflow: hidden;
12
+
13
+ &:hover {
14
+ color: ${Color.White};
15
+ }
16
+ `;
17
+
18
+ interface NavbarAvatarProps extends Omit<AvatarProps, 'title'> {
19
+ title: ReactNode;
20
+ subtitle: ReactNode;
21
+ children: ReactNode;
22
+ }
23
+
24
+ export function NavbarAvatar({
25
+ title,
26
+ subtitle,
27
+ children,
28
+ ...props
29
+ }: NavbarAvatarProps): ReactElement {
30
+ const { isNavbarExpanded } = useNavbarContext();
31
+
32
+ if (!isNavbarExpanded) {
33
+ return <Avatar {...props}>{children}</Avatar>;
34
+ }
35
+
36
+ return (
37
+ <Columns space="xsmall" align="center">
38
+ <Column width="content">
39
+ <Avatar {...props}>{children}</Avatar>
40
+ </Column>
41
+
42
+ <Column>
43
+ <Stack space="none">
44
+ <StyledTypography variant="caption">{title}</StyledTypography>
45
+
46
+ <StyledTypography variant="caption">{subtitle}</StyledTypography>
47
+ </Stack>
48
+ </Column>
49
+ </Columns>
50
+ );
51
+ }
@@ -0,0 +1,135 @@
1
+ import { BottomNavigation, BottomNavigationAction } from '@material-ui/core';
2
+ import { Menu } from '@material-ui/icons';
3
+ import { Color } from '@superdispatch/ui';
4
+ import {
5
+ ComponentType,
6
+ HTMLAttributes,
7
+ ReactElement,
8
+ ReactNode,
9
+ useMemo,
10
+ } from 'react';
11
+ import styled from 'styled-components';
12
+ import { useNavbarContext } from './NavbarContext';
13
+
14
+ const StyledBottomNavigation = styled(BottomNavigation)`
15
+ background: ${Color.Dark500};
16
+ `;
17
+
18
+ const StyledBottomNavigationAction = styled(BottomNavigationAction)`
19
+ && {
20
+ background: #1b2638;
21
+ color: ${Color.Silver500};
22
+ padding: 6px 0 8px;
23
+ line-height: 20px;
24
+ }
25
+
26
+ &:first-child {
27
+ padding-left: 12px;
28
+ }
29
+
30
+ &:last-child {
31
+ padding-right: 12px;
32
+ }
33
+
34
+ &.Mui-selected {
35
+ color: ${Color.White};
36
+
37
+ .MuiBottomNavigationAction-label {
38
+ font-size: 0.75rem;
39
+ }
40
+ }
41
+ `;
42
+
43
+ const IconWrapper = styled.div`
44
+ position: relative;
45
+ `;
46
+
47
+ const IconLabel = styled.div`
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+
52
+ font-size: 12px;
53
+ border-radius: 50%;
54
+ color: ${Color.White};
55
+ background: ${Color.Red300};
56
+
57
+ position: absolute;
58
+ top: 0;
59
+ right: 0;
60
+
61
+ width: 8px;
62
+ height: 8px;
63
+ `;
64
+
65
+ export interface NavbarBottomBarItem {
66
+ active?: boolean;
67
+ hasBadge?: boolean;
68
+ value: string;
69
+ label: ReactNode;
70
+ icon: ReactNode;
71
+ onClick?: () => void;
72
+ component?: ComponentType<HTMLAttributes<HTMLElement>>;
73
+ }
74
+
75
+ interface NavbarBottomBarProps {
76
+ items: NavbarBottomBarItem[];
77
+ hasMenuBadge?: boolean;
78
+ }
79
+
80
+ export function NavbarBottomBar({
81
+ items,
82
+ hasMenuBadge,
83
+ }: NavbarBottomBarProps): ReactElement {
84
+ const { isDrawerOpen, setDrawerOpen } = useNavbarContext();
85
+
86
+ const activeItem = useMemo(() => items.find((item) => item.active), [items]);
87
+
88
+ return (
89
+ <StyledBottomNavigation
90
+ value={activeItem?.value}
91
+ showLabels={true}
92
+ onChange={(_event, newValue) => {
93
+ if (newValue) {
94
+ if (newValue === 'menu') {
95
+ setDrawerOpen(!isDrawerOpen);
96
+ } else {
97
+ setDrawerOpen(false);
98
+ }
99
+ }
100
+ }}
101
+ >
102
+ {items.map(({ active, hasBadge, ...item }) => (
103
+ <StyledBottomNavigationAction
104
+ {...item}
105
+ key={item.value}
106
+ icon={
107
+ hasBadge ? (
108
+ <IconWrapper>
109
+ <IconLabel />
110
+ {item.icon}
111
+ </IconWrapper>
112
+ ) : (
113
+ item.icon
114
+ )
115
+ }
116
+ />
117
+ ))}
118
+
119
+ <StyledBottomNavigationAction
120
+ value="menu"
121
+ label="Menu"
122
+ icon={
123
+ hasMenuBadge ? (
124
+ <IconWrapper>
125
+ <IconLabel />
126
+ <Menu fontSize="small" />
127
+ </IconWrapper>
128
+ ) : (
129
+ <Menu fontSize="small" />
130
+ )
131
+ }
132
+ />
133
+ </StyledBottomNavigation>
134
+ );
135
+ }
@@ -0,0 +1,23 @@
1
+ import { noop } from 'lodash';
2
+ import { createContext, useContext } from 'react';
3
+
4
+ export interface NavbarContextType {
5
+ isDrawerOpen: boolean;
6
+ isMenuExpanded: boolean;
7
+ isNavbarExpanded: boolean;
8
+
9
+ setDrawerOpen: (value: boolean) => void;
10
+ setMenuExpanded: (value: boolean) => void;
11
+ }
12
+
13
+ export const NavbarContext = createContext<NavbarContextType>({
14
+ isDrawerOpen: false,
15
+ isMenuExpanded: false,
16
+ isNavbarExpanded: false,
17
+ setMenuExpanded: noop,
18
+ setDrawerOpen: noop,
19
+ });
20
+
21
+ export function useNavbarContext(): NavbarContextType {
22
+ return useContext(NavbarContext);
23
+ }
@@ -0,0 +1,119 @@
1
+ import { Color, useUID } from '@superdispatch/ui';
2
+ import {
3
+ ComponentType,
4
+ HTMLAttributes,
5
+ MouseEvent,
6
+ ReactElement,
7
+ ReactNode,
8
+ } from 'react';
9
+ import styled from 'styled-components';
10
+
11
+ export const NavbarBadge = styled.span`
12
+ flex-shrink: 0;
13
+ padding: 4px 6px;
14
+ min-width: 20px;
15
+ line-height: 12px;
16
+ font-size: 12px;
17
+ font-weight: 400;
18
+ border-radius: 10px;
19
+ text-align: center;
20
+ background: #131c2a;
21
+
22
+ &[data-variant='primary'] {
23
+ color: ${Color.White};
24
+ background: ${Color.Blue300};
25
+ }
26
+
27
+ &[data-variant='danger'] {
28
+ color: ${Color.White};
29
+ background: ${Color.Red500};
30
+ }
31
+ `;
32
+
33
+ export const NavbarLabel = styled.span`
34
+ flex-grow: 1;
35
+ overflow: hidden;
36
+ white-space: nowrap;
37
+ text-overflow: ellipsis;
38
+ `;
39
+
40
+ const NavbarItemRoot = styled.div`
41
+ width: 100%;
42
+
43
+ display: flex;
44
+ align-items: center;
45
+ color: #c2c4c9;
46
+ font-size: 14px;
47
+ font-weight: 400;
48
+ line-height: 20px;
49
+ text-decoration: none;
50
+ outline: none;
51
+ cursor: pointer;
52
+ padding: 8px 16px;
53
+ border-left: 4px solid transparent;
54
+
55
+ &:hover,
56
+ &[aria-current],
57
+ &[data-active='true'] {
58
+ color: ${Color.White};
59
+ background-color: #2f394a;
60
+ border-left-color: ${Color.Blue300};
61
+ }
62
+ `;
63
+
64
+ const IconWrapper = styled.div`
65
+ width: 24px;
66
+ margin-right: 8px;
67
+
68
+ & svg {
69
+ font-size: 24px;
70
+ }
71
+ `;
72
+
73
+ export interface NavbarItemProps {
74
+ onClick?: (event: MouseEvent<HTMLElement>) => void;
75
+ component?: ComponentType<HTMLAttributes<HTMLElement>>;
76
+
77
+ label: ReactNode;
78
+ icon?: ReactNode;
79
+ badge?: ReactNode;
80
+ ident?: number;
81
+ gutter?: boolean;
82
+ variant?: 'danger' | 'primary';
83
+ active?: boolean;
84
+ }
85
+
86
+ export function NavbarItem({
87
+ label,
88
+ gutter,
89
+ badge,
90
+ icon,
91
+ component,
92
+ onClick,
93
+ variant,
94
+ ident = 0,
95
+ active,
96
+ }: NavbarItemProps): ReactElement {
97
+ const uid = useUID();
98
+
99
+ return (
100
+ <NavbarItemRoot
101
+ as={component}
102
+ onClick={onClick}
103
+ aria-labelledby={uid}
104
+ data-active={active}
105
+ style={{
106
+ marginTop: gutter ? '16px' : '0',
107
+ paddingLeft: (ident + 1) * 20,
108
+ }}
109
+ >
110
+ <IconWrapper>{icon}</IconWrapper>
111
+
112
+ <NavbarLabel id={uid}>{label}</NavbarLabel>
113
+
114
+ {badge != null && (
115
+ <NavbarBadge data-variant={variant}>{badge}</NavbarBadge>
116
+ )}
117
+ </NavbarItemRoot>
118
+ );
119
+ }