@superdispatch/ui-lab 0.50.3 → 0.50.5

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 (124) hide show
  1. package/.babelrc.js +5 -0
  2. package/package.json +38 -13
  3. package/pkg/README.md +10 -0
  4. package/{dist-node → pkg/dist-node}/index.js +52 -40
  5. package/pkg/dist-node/index.js.map +1 -0
  6. package/{dist-src → pkg/dist-src}/email-autocomplate/EmailAutocomplete.js +3 -3
  7. package/{dist-src → pkg/dist-src}/file-drop-zone/FileDropZone.js +2 -2
  8. package/{dist-src → pkg/dist-src}/flag-list/FlagListItem.js +11 -19
  9. package/{dist-src → pkg/dist-src}/index.js +1 -0
  10. package/{dist-src → pkg/dist-src}/navbar/Navbar.js +13 -4
  11. package/{dist-src → pkg/dist-src}/navbar/NavbarAccordion.js +18 -9
  12. package/{dist-src → pkg/dist-src}/navbar/NavbarList.js +1 -1
  13. package/{dist-src → pkg/dist-src}/sidebar/SidebarBackButton.js +1 -1
  14. package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItem.js +1 -1
  15. package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemAvatar.js +1 -1
  16. package/{dist-types → pkg/dist-types}/index.d.ts +8 -2
  17. package/{dist-web → pkg/dist-web}/index.js +52 -42
  18. package/pkg/dist-web/index.js.map +1 -0
  19. package/pkg/package.json +41 -0
  20. package/playroom.ts +24 -0
  21. package/src/alert/Alert.stories.tsx +162 -0
  22. package/src/alert/Alert.tsx +135 -0
  23. package/src/banner/Banner.stories.tsx +64 -0
  24. package/src/banner/Banner.tsx +120 -0
  25. package/src/box/Box.stories.tsx +20 -0
  26. package/src/box/Box.tsx +257 -0
  27. package/src/button/Button.stories.tsx +739 -0
  28. package/src/button/Button.tsx +498 -0
  29. package/src/button-area/ButtonArea.stories.tsx +65 -0
  30. package/src/button-area/ButtonArea.tsx +90 -0
  31. package/src/chat/Chat.stories.tsx +130 -0
  32. package/src/chat/Chat.tsx +57 -0
  33. package/src/chat/ChatMessage.tsx +45 -0
  34. package/src/chat/README.MD +7 -0
  35. package/src/chat/__tests__/Chat.spec.tsx +29 -0
  36. package/src/chat/__tests__/ChatMessage.spec.tsx +39 -0
  37. package/src/container/Container.tsx +48 -0
  38. package/src/description-item/DescriptionItem.stories.tsx +163 -0
  39. package/src/description-item/DescriptionItem.tsx +104 -0
  40. package/src/description-line-item/DescriptionLineItem.stories.tsx +23 -0
  41. package/src/description-line-item/DescriptionLineItem.tsx +29 -0
  42. package/src/email-autocomplate/CloseIcon.tsx +20 -0
  43. package/src/email-autocomplate/EmailAutocomplete.stories.tsx +47 -0
  44. package/src/email-autocomplate/EmailAutocomplete.tsx +138 -0
  45. package/src/file-drop-zone/FileDropZone.stories.tsx +44 -0
  46. package/src/file-drop-zone/FileDropZone.tsx +170 -0
  47. package/src/file-list-item/FileListItem.stories.tsx +37 -0
  48. package/src/file-list-item/FileListItem.tsx +148 -0
  49. package/src/file-list-item/__tests__/FileListItem.spec.tsx +339 -0
  50. package/src/flag-list/FlagList.stories.tsx +72 -0
  51. package/src/flag-list/FlagList.tsx +54 -0
  52. package/src/flag-list/FlagListItem.tsx +126 -0
  53. package/src/index.spec.ts +53 -0
  54. package/src/index.ts +36 -0
  55. package/src/linked-text/LinkeText.stories.tsx +42 -0
  56. package/src/linked-text/LinkedText.tsx +47 -0
  57. package/src/multiline-text/MultilineText.stories.tsx +30 -0
  58. package/src/multiline-text/MultilineText.ts +16 -0
  59. package/src/navbar/Navbar.stories.tsx +137 -0
  60. package/src/navbar/Navbar.tsx +132 -0
  61. package/src/navbar/NavbarAccordion.tsx +195 -0
  62. package/src/navbar/NavbarAvatar.tsx +51 -0
  63. package/src/navbar/NavbarBottomBar.tsx +135 -0
  64. package/src/navbar/NavbarContext.tsx +22 -0
  65. package/src/navbar/NavbarItem.tsx +125 -0
  66. package/src/navbar/NavbarList.tsx +247 -0
  67. package/src/navbar/NavbarMenu.tsx +102 -0
  68. package/src/passwordStepper/PasswordStrength.stories.tsx +95 -0
  69. package/src/passwordStepper/PasswordStrength.tsx +107 -0
  70. package/src/passwordStepper/PasswordUtils.tsx +42 -0
  71. package/src/passwordStepper/PasswordValidationComponents.tsx +95 -0
  72. package/src/sidebar/Sidebar.stories.tsx +376 -0
  73. package/src/sidebar/Sidebar.tsx +75 -0
  74. package/src/sidebar/SidebarBackButton.tsx +33 -0
  75. package/src/sidebar/SidebarContainer.tsx +114 -0
  76. package/src/sidebar/SidebarContent.tsx +119 -0
  77. package/src/sidebar/SidebarDivider.tsx +15 -0
  78. package/src/sidebar/SidebarMenuItem.tsx +196 -0
  79. package/src/sidebar/SidebarMenuItemAction.tsx +27 -0
  80. package/src/sidebar/SidebarMenuItemAvatar.tsx +59 -0
  81. package/src/sidebar/SidebarMenuItemContext.tsx +33 -0
  82. package/src/sidebar/SidebarSubheader.tsx +38 -0
  83. package/src/styled.d.ts +12 -0
  84. package/src/text-box/TextBox.stories.tsx +114 -0
  85. package/src/text-box/TextBox.tsx +238 -0
  86. package/src/utils/RuleNormalizer.ts +24 -0
  87. package/src/utils/mergeStyles.ts +28 -0
  88. package/tsconfig.json +19 -0
  89. package/LICENSE +0 -21
  90. package/dist-node/index.js.map +0 -1
  91. package/dist-web/index.js.map +0 -1
  92. /package/{dist-src → pkg/dist-src}/alert/Alert.js +0 -0
  93. /package/{dist-src → pkg/dist-src}/banner/Banner.js +0 -0
  94. /package/{dist-src → pkg/dist-src}/box/Box.js +0 -0
  95. /package/{dist-src → pkg/dist-src}/button/Button.js +0 -0
  96. /package/{dist-src → pkg/dist-src}/button-area/ButtonArea.js +0 -0
  97. /package/{dist-src → pkg/dist-src}/chat/Chat.js +0 -0
  98. /package/{dist-src → pkg/dist-src}/chat/ChatMessage.js +0 -0
  99. /package/{dist-src → pkg/dist-src}/container/Container.js +0 -0
  100. /package/{dist-src → pkg/dist-src}/description-item/DescriptionItem.js +0 -0
  101. /package/{dist-src → pkg/dist-src}/description-line-item/DescriptionLineItem.js +0 -0
  102. /package/{dist-src → pkg/dist-src}/email-autocomplate/CloseIcon.js +0 -0
  103. /package/{dist-src → pkg/dist-src}/file-list-item/FileListItem.js +0 -0
  104. /package/{dist-src → pkg/dist-src}/flag-list/FlagList.js +0 -0
  105. /package/{dist-src → pkg/dist-src}/linked-text/LinkedText.js +0 -0
  106. /package/{dist-src → pkg/dist-src}/multiline-text/MultilineText.js +0 -0
  107. /package/{dist-src → pkg/dist-src}/navbar/NavbarAvatar.js +0 -0
  108. /package/{dist-src → pkg/dist-src}/navbar/NavbarBottomBar.js +0 -0
  109. /package/{dist-src → pkg/dist-src}/navbar/NavbarContext.js +0 -0
  110. /package/{dist-src → pkg/dist-src}/navbar/NavbarItem.js +0 -0
  111. /package/{dist-src → pkg/dist-src}/navbar/NavbarMenu.js +0 -0
  112. /package/{dist-src → pkg/dist-src}/passwordStepper/PasswordStrength.js +0 -0
  113. /package/{dist-src → pkg/dist-src}/passwordStepper/PasswordUtils.js +0 -0
  114. /package/{dist-src → pkg/dist-src}/passwordStepper/PasswordValidationComponents.js +0 -0
  115. /package/{dist-src → pkg/dist-src}/sidebar/Sidebar.js +0 -0
  116. /package/{dist-src → pkg/dist-src}/sidebar/SidebarContainer.js +0 -0
  117. /package/{dist-src → pkg/dist-src}/sidebar/SidebarContent.js +0 -0
  118. /package/{dist-src → pkg/dist-src}/sidebar/SidebarDivider.js +0 -0
  119. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemAction.js +0 -0
  120. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemContext.js +0 -0
  121. /package/{dist-src → pkg/dist-src}/sidebar/SidebarSubheader.js +0 -0
  122. /package/{dist-src → pkg/dist-src}/text-box/TextBox.js +0 -0
  123. /package/{dist-src → pkg/dist-src}/utils/RuleNormalizer.js +0 -0
  124. /package/{dist-src → pkg/dist-src}/utils/mergeStyles.js +0 -0
@@ -0,0 +1,42 @@
1
+ import { Meta } from '@storybook/react';
2
+ import { Stack } from '@superdispatch/ui';
3
+ import { Box } from '../box/Box';
4
+ import { LinkComponentProps, LinkedText } from './LinkedText';
5
+
6
+ export default {
7
+ title: 'Lab/LinkedText',
8
+ component: LinkedText,
9
+ decorators: [
10
+ (Story) => (
11
+ <Box maxWidth="200px">
12
+ <Story />
13
+ </Box>
14
+ ),
15
+ ],
16
+ } as Meta;
17
+
18
+ export const basic = () => (
19
+ <Stack>
20
+ <LinkedText>
21
+ Component works with (http://www.google.com) or without (www.google.com)
22
+ protocols in link. Emails are detected too: email@ex.com
23
+ </LinkedText>
24
+ </Stack>
25
+ );
26
+
27
+ export const customLinkComponent = () => {
28
+ const CustomLinkComponent = ({ children, ...props }: LinkComponentProps) => (
29
+ <a {...props}>
30
+ <i>{children}</i>
31
+ </a>
32
+ );
33
+
34
+ return (
35
+ <Stack>
36
+ <LinkedText linkComponent={CustomLinkComponent}>
37
+ Component works with (http://www.google.com) or without (www.google.com)
38
+ protocols in link. Emails are detected too: email@ex.com
39
+ </LinkedText>
40
+ </Stack>
41
+ );
42
+ };
@@ -0,0 +1,47 @@
1
+ import { Link } from '@material-ui/core';
2
+ import {
3
+ AnchorHTMLAttributes,
4
+ forwardRef,
5
+ FunctionComponent,
6
+ ReactElement,
7
+ } from 'react';
8
+ import { Anchorme } from 'react-anchorme';
9
+
10
+ export type LinkComponentProps = Omit<
11
+ AnchorHTMLAttributes<HTMLAnchorElement>,
12
+ 'href'
13
+ > & {
14
+ href: string;
15
+ truncate?: number;
16
+ };
17
+
18
+ const DefaultLinkComponent = forwardRef<HTMLAnchorElement, LinkComponentProps>(
19
+ (
20
+ { color, target = '_blank', rel = 'noreferrer noopener', ...props },
21
+ ref,
22
+ ) => <Link {...props} ref={ref} rel={rel} target={target} />,
23
+ );
24
+
25
+ DefaultLinkComponent.displayName = 'DefaultLinkComponent';
26
+
27
+ export interface LinkedTextProps
28
+ extends Omit<LinkComponentProps, 'ref' | 'href'> {
29
+ children?: null | string;
30
+ linkComponent?: FunctionComponent<LinkComponentProps>;
31
+ }
32
+
33
+ export function LinkedText({
34
+ children,
35
+ linkComponent = DefaultLinkComponent,
36
+ ...props
37
+ }: LinkedTextProps): ReactElement | null {
38
+ if (!children) {
39
+ return null;
40
+ }
41
+
42
+ return (
43
+ <Anchorme {...props} linkComponent={linkComponent}>
44
+ {children}
45
+ </Anchorme>
46
+ );
47
+ }
@@ -0,0 +1,30 @@
1
+ import { Meta } from '@storybook/react';
2
+ import { Box } from '../box/Box';
3
+ import { MultilineText } from './MultilineText';
4
+
5
+ export default {
6
+ title: 'Lab/MultilineText',
7
+ component: MultilineText,
8
+ decorators: [
9
+ (Story) => (
10
+ <Box maxWidth="300px">
11
+ <Story />
12
+ </Box>
13
+ ),
14
+ ],
15
+ } as Meta;
16
+
17
+ export const basic = () => (
18
+ <Box>
19
+ <MultilineText>{`This is a multiline text with a lot of white space.
20
+
21
+ Lorem ipsum dolor sit amet,
22
+ consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
23
+
24
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
25
+
26
+
27
+
28
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.`}</MultilineText>
29
+ </Box>
30
+ );
@@ -0,0 +1,16 @@
1
+ import { Property } from 'csstype';
2
+ import styled from 'styled-components';
3
+
4
+ export interface MultilineTextProps {
5
+ overflowWrap?: Extract<
6
+ Property.OverflowWrap,
7
+ 'anywhere' | 'break-word' | 'normal'
8
+ >;
9
+ }
10
+
11
+ export const MultilineText = styled.span<MultilineTextProps>`
12
+ white-space: pre-wrap;
13
+ overflow-wrap: ${({ overflowWrap = 'normal' }) => overflowWrap};
14
+ `;
15
+
16
+ MultilineText.displayName = 'MultilineText';
@@ -0,0 +1,137 @@
1
+ import {
2
+ AccountCircle as ProfileIcon,
3
+ AllInbox,
4
+ Dashboard,
5
+ ExitToApp,
6
+ Notifications,
7
+ People,
8
+ Settings,
9
+ } from '@material-ui/icons';
10
+ import { Meta } from '@storybook/react';
11
+ import { Color } from '@superdispatch/ui';
12
+ import { noop } from 'lodash';
13
+ import styled from 'styled-components';
14
+ import { Box, Navbar, NavbarAvatar, NavbarMenu, TextBox } from '../..';
15
+
16
+ export default {
17
+ title: 'Lab/Navbar',
18
+ component: Navbar,
19
+ parameters: {
20
+ layout: 'fullscreen',
21
+ },
22
+ } as Meta;
23
+
24
+ const bottomItems = [
25
+ {
26
+ value: '/orders',
27
+ label: 'All Orders',
28
+ icon: <AllInbox fontSize="small" />,
29
+ active: true,
30
+ },
31
+ {
32
+ label: 'Customers',
33
+ value: 'Customers',
34
+ icon: <People />,
35
+ },
36
+ {
37
+ value: 'profile',
38
+ label: 'Profile',
39
+ icon: <ProfileIcon />,
40
+ },
41
+ ];
42
+
43
+ const menuItems = [
44
+ {
45
+ key: 'profile',
46
+ icon: <ProfileIcon />,
47
+ label: 'Profile',
48
+ onClick: noop,
49
+ },
50
+ {
51
+ key: 'notification-settings',
52
+ icon: <Notifications />,
53
+ label: 'Notification Settings',
54
+ onClick: noop,
55
+ },
56
+ {
57
+ key: 'logout',
58
+ icon: <ExitToApp />,
59
+ label: 'Log Out',
60
+ onClick: noop,
61
+ },
62
+ ];
63
+
64
+ const items = [
65
+ {
66
+ icon: <People />,
67
+ label: 'Customers',
68
+ groupKey: 1,
69
+ key: 'Customers',
70
+ },
71
+ {
72
+ icon: <Settings />,
73
+ label: 'Settings',
74
+ groupKey: 2,
75
+ key: 'Settings',
76
+ },
77
+ {
78
+ icon: <Dashboard />,
79
+ label: 'Dashboard',
80
+ groupKey: 2,
81
+ key: 'Dashbaord',
82
+ },
83
+ {
84
+ icon: <AllInbox />,
85
+ label: 'Orders',
86
+ groupKey: 3,
87
+ key: 'Orders',
88
+ items: [
89
+ {
90
+ label: 'Customers',
91
+ groupKey: 2,
92
+ key: 'Customers',
93
+ },
94
+ {
95
+ label: 'Settings',
96
+ groupKey: 2,
97
+ key: 'Settings',
98
+ active: true,
99
+ },
100
+ {
101
+ ident: 1,
102
+ groupKey: 2,
103
+ label: 'Requests',
104
+ key: 'Requests',
105
+ },
106
+ ],
107
+ },
108
+ ];
109
+
110
+ const HeaderText = styled(TextBox)`
111
+ color: ${Color.White};
112
+ `;
113
+
114
+ export const basic = () => {
115
+ return (
116
+ <Box height="100vh">
117
+ <Navbar
118
+ items={items}
119
+ bottomItems={bottomItems}
120
+ header={<HeaderText variant="heading-3">Super Navbar</HeaderText>}
121
+ footer={
122
+ <NavbarMenu items={menuItems}>
123
+ <NavbarAvatar
124
+ title="John"
125
+ subtitle="Smith"
126
+ src="https://images.unsplash.com/photo-1571816119607-57e48af1caa9?q=80&w=256&h=256"
127
+ >
128
+ JS
129
+ </NavbarAvatar>
130
+ </NavbarMenu>
131
+ }
132
+ >
133
+ Hello
134
+ </Navbar>
135
+ </Box>
136
+ );
137
+ };
@@ -0,0 +1,132 @@
1
+ import { Drawer, useMediaQuery, useTheme } from '@material-ui/core';
2
+ import { ColorDynamic, useResponsiveValue } from '@superdispatch/ui';
3
+ import {
4
+ CSSProperties,
5
+ ReactElement,
6
+ ReactNode,
7
+ useCallback,
8
+ useMemo,
9
+ useState,
10
+ } from 'react';
11
+ import styled from 'styled-components';
12
+ import { NavbarBottomBar, NavbarBottomBarItem } from './NavbarBottomBar';
13
+ import { NavbarContext, NavbarContextType } from './NavbarContext';
14
+ import { NavbarItemOptions, NavbarList } from './NavbarList';
15
+
16
+ const Aside = styled.aside`
17
+ display: flex;
18
+ flex-direction: column;
19
+ overflow: auto;
20
+ border-right: 1px solid ${ColorDynamic.Silver400};
21
+ `;
22
+
23
+ const Main = styled.main`
24
+ flex: 1;
25
+ display: flex;
26
+ flex-direction: column;
27
+ overflow: auto;
28
+ `;
29
+
30
+ interface NavbarProps {
31
+ containerStyle?: CSSProperties;
32
+ children: ReactNode;
33
+
34
+ header?: ReactNode;
35
+ items: NavbarItemOptions[];
36
+ bottomItems: NavbarBottomBarItem[];
37
+ footer?: ReactNode;
38
+
39
+ hasExtraBadge?: boolean;
40
+ isMenuExpanded?: boolean;
41
+ onMenuExpandedChange?: (isExpanded: boolean) => void;
42
+ }
43
+
44
+ export function Navbar({
45
+ footer,
46
+ items,
47
+ header,
48
+ bottomItems,
49
+ children,
50
+ containerStyle,
51
+ hasExtraBadge,
52
+ isMenuExpanded: controlledIsMenuExpanded,
53
+ onMenuExpandedChange,
54
+ }: NavbarProps): ReactElement {
55
+ const theme = useTheme();
56
+ const [isDrawerOpen, setDrawerOpen] = useState(false);
57
+
58
+ const platform = useResponsiveValue('mobile', 'tablet', 'desktop');
59
+ const isMobile = platform === 'mobile';
60
+
61
+ const matches = useMediaQuery(theme.breakpoints.up('md'), { noSsr: true });
62
+ const [internalIsMenuExpanded, setInternalMenuExpanded] = useState(matches);
63
+
64
+ const isMenuExpanded =
65
+ controlledIsMenuExpanded !== undefined
66
+ ? controlledIsMenuExpanded
67
+ : internalIsMenuExpanded;
68
+
69
+ const setMenuExpanded = useCallback(
70
+ (value: boolean): void => {
71
+ if (controlledIsMenuExpanded === undefined) {
72
+ setInternalMenuExpanded(value);
73
+ }
74
+ onMenuExpandedChange?.(value);
75
+ },
76
+ [controlledIsMenuExpanded, onMenuExpandedChange],
77
+ );
78
+
79
+ const hasBadge = hasExtraBadge || items.some((item) => item.badge);
80
+
81
+ const ctx = useMemo<NavbarContextType>(
82
+ () => ({
83
+ isDrawerOpen,
84
+ isMenuExpanded,
85
+ setDrawerOpen,
86
+ setMenuExpanded,
87
+ isNavbarExpanded: isMenuExpanded || isDrawerOpen,
88
+ }),
89
+ [isDrawerOpen, isMenuExpanded, setDrawerOpen, setMenuExpanded],
90
+ );
91
+
92
+ return (
93
+ <NavbarContext.Provider value={ctx}>
94
+ <div
95
+ style={{
96
+ display: 'flex',
97
+ height: '100%',
98
+ flexDirection: isMobile ? 'column' : 'row',
99
+ ...containerStyle,
100
+ }}
101
+ >
102
+ {!isMobile && (
103
+ <Aside>
104
+ <NavbarList header={header} items={items} footer={footer} />
105
+ </Aside>
106
+ )}
107
+
108
+ <Main>{children}</Main>
109
+
110
+ {isMobile && (
111
+ <NavbarBottomBar items={bottomItems} hasMenuBadge={hasBadge} />
112
+ )}
113
+
114
+ <Drawer
115
+ open={isDrawerOpen}
116
+ anchor="right"
117
+ onClose={() => {
118
+ setDrawerOpen(false);
119
+ }}
120
+ PaperProps={{
121
+ style: {
122
+ width: '280px',
123
+ minWidth: '280px',
124
+ },
125
+ }}
126
+ >
127
+ <NavbarList header={header} items={items} footer={footer} />
128
+ </Drawer>
129
+ </div>
130
+ </NavbarContext.Provider>
131
+ );
132
+ }
@@ -0,0 +1,195 @@
1
+ import { Accordion, AccordionSummary } from '@material-ui/core';
2
+ import { ExpandMore } from '@material-ui/icons';
3
+ import { Color, ColorDark, ColorDynamic, useUID } from '@superdispatch/ui';
4
+ import {
5
+ MouseEvent,
6
+ ReactElement,
7
+ ReactNode,
8
+ useEffect,
9
+ useMemo,
10
+ useState,
11
+ } from 'react';
12
+ import styled from 'styled-components';
13
+ import { useNavbarContext } from './NavbarContext';
14
+ import { NavbarItem } from './NavbarItem';
15
+ import { NavbarItemOptions } from './NavbarList';
16
+
17
+ export const NavbarAccordionLabel = styled.span`
18
+ flex-grow: 1;
19
+ overflow: hidden;
20
+ white-space: nowrap;
21
+ text-overflow: ellipsis;
22
+ &[data-expanded='false'] {
23
+ display: none;
24
+ }
25
+ `;
26
+
27
+ const NavbarAccordionRoot = styled(Accordion)`
28
+ width: 100%;
29
+ color: #c2c4c9;
30
+ font-size: 14px;
31
+ font-weight: 400;
32
+ line-height: 20px;
33
+ text-decoration: none;
34
+ outline: none;
35
+ cursor: pointer;
36
+ background-color: ${({ theme }) =>
37
+ theme.palette.type === 'dark' ? ColorDark.White : Color.Dark500};
38
+
39
+ &[aria-current] {
40
+ background-color: ${Color.White10};
41
+ color: ${ColorDynamic.White};
42
+ }
43
+
44
+ &.MuiAccordion-root:before {
45
+ background-color: #1b2638;
46
+ }
47
+
48
+ &.MuiPaper-elevation0 {
49
+ border: 0px;
50
+ }
51
+
52
+ &[data-gutter] {
53
+ margin-top: 16px;
54
+ }
55
+
56
+ &[data-gutter].MuiAccordion-root.Mui-expanded {
57
+ margin-top: 16px;
58
+ }
59
+ `;
60
+
61
+ const NavbarAccordionSummary = styled(AccordionSummary)`
62
+ border-left: 4px solid transparent;
63
+ padding-left: 20px;
64
+
65
+ &.MuiAccordionSummary-root {
66
+ max-height: 40px;
67
+ min-height: 40px;
68
+ }
69
+
70
+ &.MuiAccordionSummary-content {
71
+ align-items: center;
72
+ }
73
+
74
+ &:hover,
75
+ &[aria-current],
76
+ &[data-active='true'] {
77
+ color: ${Color.White};
78
+ background-color: ${Color.White10};
79
+ border-left-color: ${ColorDynamic.Blue300};
80
+ }
81
+
82
+ &[data-expanded='false'] {
83
+ .MuiAccordionSummary-expandIcon {
84
+ display: none;
85
+ }
86
+ }
87
+ `;
88
+
89
+ const IconWrapper = styled.div`
90
+ width: 24px;
91
+ margin-right: 8px;
92
+
93
+ & svg {
94
+ font-size: 24px;
95
+ }
96
+ `;
97
+
98
+ export interface NavbarAccordionProps {
99
+ label: ReactNode;
100
+ icon?: ReactNode;
101
+ gutter?: boolean;
102
+ items: Array<Omit<NavbarItemOptions, 'icon'>>;
103
+ onClick?: (event: MouseEvent<HTMLDivElement>) => void;
104
+ isExpanded?: boolean;
105
+ onExpandedChange?: (isExpanded: boolean) => void;
106
+ }
107
+
108
+ export function NavbarAccordion({
109
+ label,
110
+ icon,
111
+ gutter,
112
+ items,
113
+ onClick,
114
+ isExpanded: controlledIsExpanded,
115
+ onExpandedChange,
116
+ }: NavbarAccordionProps): ReactElement {
117
+ const uid = useUID();
118
+ const { setDrawerOpen, isNavbarExpanded } = useNavbarContext();
119
+
120
+ const [internalIsExpanded, setInternalExpanded] = useState(true);
121
+
122
+ useEffect(() => {
123
+ if (controlledIsExpanded === undefined) {
124
+ setInternalExpanded(isNavbarExpanded);
125
+ }
126
+ }, [isNavbarExpanded, controlledIsExpanded]);
127
+
128
+ const isExpanded =
129
+ controlledIsExpanded !== undefined
130
+ ? controlledIsExpanded
131
+ : internalIsExpanded;
132
+
133
+ function setExpanded(value: boolean): void {
134
+ if (controlledIsExpanded === undefined) {
135
+ setInternalExpanded(value);
136
+ }
137
+ onExpandedChange?.(value);
138
+ }
139
+
140
+ const filteredItems: Array<Omit<NavbarItemOptions, 'icon'>> = useMemo(
141
+ () => items.filter((item) => !item.hide),
142
+ [items],
143
+ );
144
+
145
+ return (
146
+ <NavbarAccordionRoot
147
+ square={true}
148
+ data-gutter={!gutter}
149
+ aria-labelledby={uid}
150
+ expanded={isExpanded}
151
+ onClick={(event) => {
152
+ onClick?.(event);
153
+
154
+ if (controlledIsExpanded !== undefined || isNavbarExpanded) {
155
+ setExpanded(!isExpanded);
156
+ }
157
+ }}
158
+ >
159
+ <NavbarAccordionSummary
160
+ data-active={!isExpanded && items.some((item) => item.active)}
161
+ data-expanded={isNavbarExpanded}
162
+ expandIcon={<ExpandMore />}
163
+ >
164
+ <IconWrapper>{icon}</IconWrapper>
165
+ <NavbarAccordionLabel id={uid} data-expanded={isNavbarExpanded}>
166
+ {label}
167
+ </NavbarAccordionLabel>
168
+ </NavbarAccordionSummary>
169
+
170
+ {filteredItems.map((item) => {
171
+ const index = filteredItems.indexOf(item);
172
+ const prev = filteredItems[index - 1];
173
+ const { ident = 0 } = item;
174
+
175
+ return (
176
+ <NavbarItem
177
+ {...item}
178
+ key={item.key}
179
+ ident={ident}
180
+ active={item.active}
181
+ gutter={prev && prev.groupKey !== item.groupKey}
182
+ onClick={(event) => {
183
+ event.stopPropagation();
184
+ item.onClick?.(event);
185
+
186
+ if (!event.isDefaultPrevented()) {
187
+ setDrawerOpen(false);
188
+ }
189
+ }}
190
+ />
191
+ );
192
+ })}
193
+ </NavbarAccordionRoot>
194
+ );
195
+ }
@@ -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.White};
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
+ }