@superdispatch/ui-lab 0.21.13 → 0.21.15

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/LICENSE +21 -0
  2. package/{pkg/dist-node → dist-node}/index.js +4 -3
  3. package/dist-node/index.js.map +1 -0
  4. package/{pkg/dist-src → dist-src}/navbar/NavbarAccordion.js +5 -4
  5. package/{pkg/dist-web → dist-web}/index.js +4 -3
  6. package/dist-web/index.js.map +1 -0
  7. package/package.json +13 -39
  8. package/.babelrc.js +0 -5
  9. package/.turbo/turbo-version.log +0 -28
  10. package/pkg/README.md +0 -10
  11. package/pkg/dist-node/index.js.map +0 -1
  12. package/pkg/dist-web/index.js.map +0 -1
  13. package/pkg/package.json +0 -34
  14. package/playroom.ts +0 -23
  15. package/src/alert/Alert.stories.tsx +0 -105
  16. package/src/alert/Alert.tsx +0 -108
  17. package/src/banner/Banner.stories.tsx +0 -64
  18. package/src/banner/Banner.tsx +0 -120
  19. package/src/box/Box.stories.tsx +0 -20
  20. package/src/box/Box.tsx +0 -252
  21. package/src/button/Button.stories.tsx +0 -717
  22. package/src/button/Button.tsx +0 -460
  23. package/src/button-area/ButtonArea.stories.tsx +0 -65
  24. package/src/button-area/ButtonArea.tsx +0 -88
  25. package/src/container/Container.tsx +0 -48
  26. package/src/description-item/DescriptionItem.stories.tsx +0 -163
  27. package/src/description-item/DescriptionItem.tsx +0 -104
  28. package/src/file-drop-zone/FileDropZone.stories.tsx +0 -44
  29. package/src/file-drop-zone/FileDropZone.tsx +0 -170
  30. package/src/file-list-item/FileListItem.stories.tsx +0 -37
  31. package/src/file-list-item/FileListItem.tsx +0 -145
  32. package/src/file-list-item/__tests__/FileListItem.spec.tsx +0 -339
  33. package/src/index.spec.ts +0 -43
  34. package/src/index.ts +0 -28
  35. package/src/linked-text/LinkeText.stories.tsx +0 -42
  36. package/src/linked-text/LinkedText.tsx +0 -47
  37. package/src/multiline-text/MultilineText.stories.tsx +0 -30
  38. package/src/multiline-text/MultilineText.ts +0 -16
  39. package/src/navbar/Navbar.stories.tsx +0 -135
  40. package/src/navbar/Navbar.tsx +0 -111
  41. package/src/navbar/NavbarAccordion.tsx +0 -171
  42. package/src/navbar/NavbarAvatar.tsx +0 -51
  43. package/src/navbar/NavbarBottomBar.tsx +0 -135
  44. package/src/navbar/NavbarContext.tsx +0 -23
  45. package/src/navbar/NavbarItem.tsx +0 -119
  46. package/src/navbar/NavbarList.tsx +0 -225
  47. package/src/navbar/NavbarMenu.tsx +0 -102
  48. package/src/sidebar/Sidebar.stories.tsx +0 -363
  49. package/src/sidebar/Sidebar.tsx +0 -73
  50. package/src/sidebar/SidebarBackButton.tsx +0 -33
  51. package/src/sidebar/SidebarContainer.tsx +0 -114
  52. package/src/sidebar/SidebarContent.tsx +0 -119
  53. package/src/sidebar/SidebarDivider.tsx +0 -15
  54. package/src/sidebar/SidebarMenuItem.tsx +0 -211
  55. package/src/sidebar/SidebarMenuItemAction.tsx +0 -27
  56. package/src/sidebar/SidebarMenuItemAvatar.tsx +0 -59
  57. package/src/sidebar/SidebarMenuItemContext.tsx +0 -33
  58. package/src/sidebar/SidebarSubheader.tsx +0 -38
  59. package/src/styled.d.ts +0 -12
  60. package/src/text-box/TextBox.stories.tsx +0 -108
  61. package/src/text-box/TextBox.tsx +0 -229
  62. package/src/utils/RuleNormalizer.ts +0 -24
  63. package/src/utils/mergeStyles.ts +0 -28
  64. package/tsconfig.json +0 -19
  65. /package/{pkg/dist-src → dist-src}/alert/Alert.js +0 -0
  66. /package/{pkg/dist-src → dist-src}/banner/Banner.js +0 -0
  67. /package/{pkg/dist-src → dist-src}/box/Box.js +0 -0
  68. /package/{pkg/dist-src → dist-src}/button/Button.js +0 -0
  69. /package/{pkg/dist-src → dist-src}/button-area/ButtonArea.js +0 -0
  70. /package/{pkg/dist-src → dist-src}/container/Container.js +0 -0
  71. /package/{pkg/dist-src → dist-src}/description-item/DescriptionItem.js +0 -0
  72. /package/{pkg/dist-src → dist-src}/file-drop-zone/FileDropZone.js +0 -0
  73. /package/{pkg/dist-src → dist-src}/file-list-item/FileListItem.js +0 -0
  74. /package/{pkg/dist-src → dist-src}/index.js +0 -0
  75. /package/{pkg/dist-src → dist-src}/linked-text/LinkedText.js +0 -0
  76. /package/{pkg/dist-src → dist-src}/multiline-text/MultilineText.js +0 -0
  77. /package/{pkg/dist-src → dist-src}/navbar/Navbar.js +0 -0
  78. /package/{pkg/dist-src → dist-src}/navbar/NavbarAvatar.js +0 -0
  79. /package/{pkg/dist-src → dist-src}/navbar/NavbarBottomBar.js +0 -0
  80. /package/{pkg/dist-src → dist-src}/navbar/NavbarContext.js +0 -0
  81. /package/{pkg/dist-src → dist-src}/navbar/NavbarItem.js +0 -0
  82. /package/{pkg/dist-src → dist-src}/navbar/NavbarList.js +0 -0
  83. /package/{pkg/dist-src → dist-src}/navbar/NavbarMenu.js +0 -0
  84. /package/{pkg/dist-src → dist-src}/sidebar/Sidebar.js +0 -0
  85. /package/{pkg/dist-src → dist-src}/sidebar/SidebarBackButton.js +0 -0
  86. /package/{pkg/dist-src → dist-src}/sidebar/SidebarContainer.js +0 -0
  87. /package/{pkg/dist-src → dist-src}/sidebar/SidebarContent.js +0 -0
  88. /package/{pkg/dist-src → dist-src}/sidebar/SidebarDivider.js +0 -0
  89. /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItem.js +0 -0
  90. /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItemAction.js +0 -0
  91. /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItemAvatar.js +0 -0
  92. /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItemContext.js +0 -0
  93. /package/{pkg/dist-src → dist-src}/sidebar/SidebarSubheader.js +0 -0
  94. /package/{pkg/dist-src → dist-src}/text-box/TextBox.js +0 -0
  95. /package/{pkg/dist-src → dist-src}/utils/RuleNormalizer.js +0 -0
  96. /package/{pkg/dist-src → dist-src}/utils/mergeStyles.js +0 -0
  97. /package/{pkg/dist-types → dist-types}/index.d.ts +0 -0
@@ -1,163 +0,0 @@
1
- import { Link } from '@material-ui/core';
2
- import CalendarTodayIcon from '@material-ui/icons/CalendarToday';
3
- import DomainIcon from '@material-ui/icons/Domain';
4
- import FingerprintIcon from '@material-ui/icons/Fingerprint';
5
- import FlightLandIcon from '@material-ui/icons/FlightLand';
6
- import FlightTakeoffIcon from '@material-ui/icons/FlightTakeoff';
7
- import NotesIcon from '@material-ui/icons/Notes';
8
- import PaymentIcon from '@material-ui/icons/Payment';
9
- import PersonIcon from '@material-ui/icons/Person';
10
- import RoomIcon from '@material-ui/icons/Room';
11
- import { Meta } from '@storybook/react';
12
- import { PhoneLink } from '@superdispatch/phones';
13
- import { Column, Columns, Inline, Stack } from '@superdispatch/ui';
14
- import { Box } from '../box/Box';
15
- import { TextBox } from '../text-box/TextBox';
16
- import { DescriptionItem } from './DescriptionItem';
17
-
18
- export default {
19
- title: 'Lab/DescriptionItem',
20
- component: DescriptionItem,
21
- } as Meta;
22
-
23
- export const basic = () => (
24
- <Box maxWidth="200px">
25
- <Stack>
26
- <DescriptionItem icon={<PaymentIcon />} aria-label="payment">
27
- <Inline space="xxsmall">
28
- <TextBox color="purple">$1,503</TextBox>
29
- COD
30
- <TextBox color="secondary">$49.94/mi</TextBox>
31
- </Inline>
32
- </DescriptionItem>
33
-
34
- <DescriptionItem icon={<CalendarTodayIcon />} label="Posted">
35
- 4 hr. ago
36
- </DescriptionItem>
37
-
38
- <DescriptionItem icon={<RoomIcon />} aria-label="address">
39
- 167 Zosh Rd, Dallas, PA 18612
40
- </DescriptionItem>
41
-
42
- <DescriptionItem icon={<FingerprintIcon />} label="ID">
43
- 202CB962AC59075B964B07152D234B70
44
- </DescriptionItem>
45
-
46
- <DescriptionItem icon={<NotesIcon />} label="Notes">
47
- Lorem ipsum dolor sit amet, consectetur adipisicing elit.
48
- </DescriptionItem>
49
- </Stack>
50
- </Box>
51
- );
52
-
53
- export const wrap = () => (
54
- <Box maxWidth="200px">
55
- <Stack>
56
- <DescriptionItem icon={<RoomIcon />} wrap={true}>
57
- 167 Zosh Rd, Dallas, PA 18612
58
- </DescriptionItem>
59
-
60
- <DescriptionItem icon={<FingerprintIcon />} label="ID" wrap={true}>
61
- 202CB962AC59075B964B07152D234B70
62
- </DescriptionItem>
63
-
64
- <DescriptionItem icon={<NotesIcon />} label="Notes" wrap={true}>
65
- Lorem ipsum dolor sit amet, consectetur adipisicing elit.
66
- </DescriptionItem>
67
- </Stack>
68
- </Box>
69
- );
70
-
71
- export const fallback = () => (
72
- <Stack>
73
- <DescriptionItem
74
- icon={<CalendarTodayIcon />}
75
- label="Posted on"
76
- fallback="N/A"
77
- >
78
- {null}
79
- </DescriptionItem>
80
- <DescriptionItem icon={<RoomIcon />} fallback="No address available" />
81
- <DescriptionItem icon={<NotesIcon />} fallback="No delivery notes">
82
- {false}
83
- </DescriptionItem>
84
- </Stack>
85
- );
86
-
87
- export const inset = () => (
88
- <Stack>
89
- <Stack space="xxsmall">
90
- <DescriptionItem icon={<PersonIcon />}>Antony Hoffman</DescriptionItem>
91
- <DescriptionItem inset={true}>
92
- <PhoneLink phone="303 555 0105" format="national" />
93
- </DescriptionItem>
94
- <DescriptionItem inset={true}>
95
- <Link href="mailto:dustin.russel@example.com">
96
- dustin.russel@example.com
97
- </Link>
98
- </DescriptionItem>
99
- </Stack>
100
-
101
- <DescriptionItem icon={<RoomIcon />}>
102
- 167 Zosh Rd, Dallas, PA 18612
103
- </DescriptionItem>
104
- </Stack>
105
- );
106
-
107
- export const complex = () => (
108
- <Box maxWidth="320px">
109
- <Stack>
110
- <Columns space="xsmall">
111
- <Column width="adaptive">
112
- <DescriptionItem icon={<DomainIcon />}>Fast Shipper</DescriptionItem>
113
- </Column>
114
-
115
- <Column width="adaptive">
116
- <DescriptionItem icon={<CalendarTodayIcon />} label="Received">
117
- 4 hr. ago
118
- </DescriptionItem>
119
- </Column>
120
- </Columns>
121
-
122
- <Columns space="xsmall">
123
- <Column width="adaptive">
124
- <DescriptionItem icon={<FingerprintIcon />} label="Load ID">
125
- 02323
126
- </DescriptionItem>
127
- </Column>
128
-
129
- <Column width="adaptive">
130
- <DescriptionItem icon={<CalendarTodayIcon />} label="Received">
131
- 4 hr. ago
132
- </DescriptionItem>
133
- </Column>
134
- </Columns>
135
-
136
- <DescriptionItem icon={<FlightTakeoffIcon />}>
137
- <Columns space="xxsmall">
138
- <Column width="adaptive">
139
- <TextBox noWrap={true}>Kansas City, MO 64105</TextBox>
140
- </Column>
141
- <Column width="adaptive">
142
- <TextBox noWrap={true} color="secondary">
143
- Oct 04, 2019
144
- </TextBox>
145
- </Column>
146
- </Columns>
147
- </DescriptionItem>
148
-
149
- <DescriptionItem icon={<FlightLandIcon />}>
150
- <Columns space="xxsmall">
151
- <Column width="adaptive">
152
- <TextBox noWrap={true}>Roeland Park, KS 66205</TextBox>
153
- </Column>
154
- <Column width="adaptive">
155
- <TextBox noWrap={true} color="secondary">
156
- Oct 04, 2019
157
- </TextBox>
158
- </Column>
159
- </Columns>
160
- </DescriptionItem>
161
- </Stack>
162
- </Box>
163
- );
@@ -1,104 +0,0 @@
1
- import {
2
- Color,
3
- Column,
4
- Columns,
5
- Inline,
6
- isEmptyReactNode,
7
- useUID,
8
- } from '@superdispatch/ui';
9
- import { forwardRef, ReactNode } from 'react';
10
- import styled, { css, SimpleInterpolation } from 'styled-components';
11
- import { TextBox } from '../text-box/TextBox';
12
-
13
- function descriptionItemIconMixin(
14
- size: 16 | 20,
15
- ): readonly SimpleInterpolation[] {
16
- return css`
17
- width: ${size}px;
18
- height: ${size + 4}px;
19
-
20
- & > .MuiSvgIcon-root {
21
- font-size: ${size}px;
22
- }
23
- `;
24
- }
25
-
26
- const DescriptionItemIcon = styled.div(
27
- ({ theme }) =>
28
- css`
29
- display: flex;
30
- align-items: center;
31
-
32
- & > .MuiSvgIcon-root {
33
- color: ${Color.Dark100};
34
- }
35
-
36
- ${descriptionItemIconMixin(20)};
37
-
38
- ${theme.breakpoints.up('sm')} {
39
- ${descriptionItemIconMixin(16)};
40
- }
41
- `,
42
- );
43
-
44
- export interface DescriptionItemProps {
45
- id?: string;
46
- 'aria-label'?: string;
47
-
48
- wrap?: boolean;
49
- inset?: boolean;
50
- icon?: ReactNode;
51
- label?: ReactNode;
52
- fallback?: ReactNode;
53
- children?: ReactNode;
54
- }
55
-
56
- export const DescriptionItem = forwardRef<HTMLDivElement, DescriptionItemProps>(
57
- (
58
- {
59
- icon,
60
- wrap,
61
- inset,
62
- label,
63
- children,
64
- fallback,
65
- id: idProp,
66
- 'aria-label': ariaLabel,
67
- },
68
- ref,
69
- ) => {
70
- const id = useUID(idProp);
71
- const labelID = `${id}-label`;
72
- const isEmptyChildren = isEmptyReactNode(children);
73
-
74
- return (
75
- <Columns id={id} ref={ref} space="xsmall" aria-label={ariaLabel}>
76
- {!!(icon || inset) && (
77
- <Column width="content">
78
- <DescriptionItemIcon>{icon}</DescriptionItemIcon>
79
- </Column>
80
- )}
81
-
82
- <Column width="adaptive">
83
- <Inline space="xxsmall" noWrap={!wrap}>
84
- {!!label && (
85
- <TextBox as="label" id={labelID} color="secondary">
86
- {label}
87
- </TextBox>
88
- )}
89
-
90
- <TextBox
91
- as="div"
92
- noWrap={!wrap}
93
- wrapOverflow={!!wrap}
94
- aria-labelledby={label == null ? undefined : labelID}
95
- color={isEmptyChildren && label == null ? 'secondary' : 'primary'}
96
- >
97
- {isEmptyChildren ? fallback : children}
98
- </TextBox>
99
- </Inline>
100
- </Column>
101
- </Columns>
102
- );
103
- },
104
- );
@@ -1,44 +0,0 @@
1
- import { Meta } from '@storybook/react';
2
- import { FileDropZone, toBytes } from './FileDropZone';
3
-
4
- export default { title: 'Lab/FileDropZone', component: FileDropZone } as Meta;
5
-
6
- export const basic = () => (
7
- <FileDropZone
8
- onDropAccepted={(files) => {
9
- alert(`Accepted files: ${files.map((file) => file.name).join(', ')}`);
10
- }}
11
- />
12
- );
13
-
14
- export const accept = () => (
15
- <FileDropZone
16
- accept={['.jpeg', '.jpg', '.png', '.gif']}
17
- hintText="or Drag & Drop .jpeg .jpg .png .gif files"
18
- onDropAccepted={(files) => {
19
- alert(`Accepted files: ${files.map((file) => file.name).join(', ')}`);
20
- }}
21
- />
22
- );
23
-
24
- export const maxSize = () => (
25
- <FileDropZone
26
- maxSize={toBytes(20, 'mb')}
27
- hintText="or Drag & Drop files less than 20 MB"
28
- onDropAccepted={(files) => {
29
- alert(`Accepted files: ${files.map((file) => file.name).join(', ')}`);
30
- }}
31
- />
32
- );
33
-
34
- export const maxFiles = () => (
35
- <FileDropZone
36
- maxFiles={1}
37
- hintText="or Drag & Drop file"
38
- onDropAccepted={(files) => {
39
- alert(`Accepted files: ${files.map((file) => file.name).join(', ')}`);
40
- }}
41
- >
42
- Upload attachment
43
- </FileDropZone>
44
- );
@@ -1,170 +0,0 @@
1
- import { CircularProgress, SvgIcon } from '@material-ui/core';
2
- import { Error } from '@material-ui/icons';
3
- import { mdiUpload } from '@mdi/js';
4
- import { CardButton, Color, Column, Columns } from '@superdispatch/ui';
5
- import { forwardRef, ReactElement, ReactNode, Suspense } from 'react';
6
- import Dropzone, { FileRejection } from 'react-dropzone';
7
- import styled from 'styled-components';
8
-
9
- export function toBytes(input: number, unit: 'kb' | 'mb' | 'gb'): number {
10
- if (unit === 'gb') return input * (1 << 30);
11
- if (unit === 'mb') return input * (1 << 20);
12
- return input * (1 << 10);
13
- }
14
-
15
- const KILOBYTE = 1024;
16
- const BYTE_UNITS = [
17
- 'B',
18
- 'KB',
19
- 'MB',
20
- 'GB',
21
- 'TB',
22
- 'PB',
23
- 'EB',
24
- 'ZB',
25
- 'YB',
26
- ] as const;
27
- export function formatBytes(bytes: number): string {
28
- if (bytes === 0) return '0 Bytes';
29
- const unitIndex = Math.floor(Math.log(bytes) / Math.log(KILOBYTE));
30
- const unit = BYTE_UNITS[unitIndex] as string;
31
- return `${(bytes / Math.pow(KILOBYTE, unitIndex)).toFixed(2)} ${unit}`;
32
- }
33
-
34
- const StyledCardButton = styled(CardButton)<{
35
- status?: 'idle' | 'active' | 'error';
36
- }>(({ status }) => ({
37
- backgroundColor: status === 'active' ? Color.Blue50 : undefined,
38
- }));
39
-
40
- interface UploadRejectionProps {
41
- maxSize?: number;
42
- maxFiles?: number;
43
- accept?: string | string[];
44
- rejection: FileRejection;
45
- }
46
-
47
- function UploadRejection({
48
- maxSize,
49
- rejection,
50
- }: UploadRejectionProps): null | ReactElement {
51
- const [error] = rejection.errors;
52
- if (!error) return null;
53
-
54
- return (
55
- <Columns align="center">
56
- <Column width="content">
57
- <Error />
58
- </Column>
59
-
60
- <Column>
61
- {error.code === 'file-too-large'
62
- ? maxSize == null
63
- ? 'Attachment size is too large'
64
- : `Attachment size should be less than ${formatBytes(maxSize)}`
65
- : error.message}
66
- </Column>
67
- </Columns>
68
- );
69
- }
70
-
71
- export interface FileDropZoneProps {
72
- children?: ReactNode;
73
- startIcon?: ReactNode;
74
- hintText?: ReactNode;
75
- fallback?: ReactNode;
76
- disabled?: boolean;
77
-
78
- maxSize?: number;
79
- maxFiles?: number;
80
- accept?: string | string[];
81
- onDropAccepted?: (files: File[]) => void;
82
- onDropRejected?: (fileRejections: FileRejection[]) => void;
83
- }
84
-
85
- export const FileDropZone = forwardRef<HTMLButtonElement, FileDropZoneProps>(
86
- (props, ref) => {
87
- const {
88
- // CardButton
89
- disabled = false,
90
- children = 'Upload Attachments',
91
- hintText = 'or Drag & Drop files',
92
- startIcon = (
93
- <SvgIcon>
94
- <path d={mdiUpload} />
95
- </SvgIcon>
96
- ),
97
- fallback = (
98
- <CardButton
99
- ref={ref}
100
- disabled={true}
101
- startIcon={<CircularProgress size="1em" color="inherit" />}
102
- >
103
- Loading dependencies…
104
- </CardButton>
105
- ),
106
-
107
- // Dropzone
108
- accept,
109
- maxSize = Infinity,
110
- maxFiles = Infinity,
111
- onDropRejected,
112
- onDropAccepted,
113
- ...dropzoneProps
114
- } = props;
115
-
116
- return (
117
- <Suspense fallback={fallback}>
118
- <Dropzone
119
- {...dropzoneProps}
120
- accept={accept}
121
- maxSize={maxSize}
122
- maxFiles={maxFiles}
123
- disabled={disabled}
124
- onDropAccepted={(files) => {
125
- onDropAccepted?.(files);
126
- }}
127
- onDropRejected={(fileRejections) => {
128
- onDropRejected?.(fileRejections);
129
- }}
130
- >
131
- {({
132
- isDragActive,
133
- isDragReject,
134
- getRootProps,
135
- getInputProps,
136
- fileRejections: [fileRejection],
137
- }) => {
138
- return (
139
- <>
140
- <input {...getInputProps()} />
141
-
142
- <StyledCardButton
143
- {...getRootProps()}
144
- ref={ref}
145
- hint={hintText}
146
- disabled={disabled}
147
- startIcon={startIcon}
148
- status={
149
- isDragActive ? 'active' : isDragReject ? 'error' : 'idle'
150
- }
151
- error={
152
- !!fileRejection && (
153
- <UploadRejection
154
- accept={accept}
155
- maxSize={maxSize}
156
- rejection={fileRejection}
157
- />
158
- )
159
- }
160
- >
161
- {children}
162
- </StyledCardButton>
163
- </>
164
- );
165
- }}
166
- </Dropzone>
167
- </Suspense>
168
- );
169
- },
170
- );
@@ -1,37 +0,0 @@
1
- import { Meta } from '@storybook/react';
2
- import { Stack } from '@superdispatch/ui';
3
- import { Box } from '../box/Box';
4
- import { FileListItem } from './FileListItem';
5
-
6
- export default {
7
- title: 'Lab/FileListItem',
8
- component: FileListItem,
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
- <FileListItem name="Read this document.txt" />
21
- <FileListItem name="TST1208 Dispatcher Info.pdf" />
22
- <FileListItem name="TST1208 Dispatcher Info.pdf" canDelete={false} />
23
- <FileListItem
24
- name="attachment.jpg"
25
- url="https://picsum.photos/seed/picsum/1024/768"
26
- />
27
- </Stack>
28
- );
29
-
30
- export const status = () => (
31
- <Stack>
32
- <FileListItem name="TST1208 Dispatcher Info.pdf" status="idle" />
33
- <FileListItem name="TST1208 Dispatcher Info.pdf" status="loading" />
34
- <FileListItem name="TST1208 Dispatcher Info.pdf" status="success" />
35
- <FileListItem name="TST1208 Dispatcher Info.pdf" status="error" />
36
- </Stack>
37
- );
@@ -1,145 +0,0 @@
1
- import {
2
- CircularProgress,
3
- IconButton,
4
- Link,
5
- SvgIcon,
6
- SvgIconProps,
7
- Tooltip,
8
- } from '@material-ui/core';
9
- import { CheckCircle, Delete, Error, Image, Refresh } from '@material-ui/icons';
10
- import { mdiFilePdfBox, mdiTextBox } from '@mdi/js';
11
- import {
12
- Color,
13
- Column,
14
- Columns,
15
- useResponsiveValue,
16
- useUID,
17
- } from '@superdispatch/ui';
18
- import { forwardRef, memo, ReactNode, useState } from 'react';
19
- import styled from 'styled-components';
20
-
21
- const FileListItemName = styled.div`
22
- overflow: hidden;
23
- line-height: 22px;
24
- white-space: nowrap;
25
- text-overflow: ellipsis;
26
- `;
27
-
28
- const FileListItemProgress = styled(CircularProgress)`
29
- font-size: 24px;
30
-
31
- ${({ theme }) => theme.breakpoints.up('sm')} {
32
- font-size: 14px;
33
- }
34
- `;
35
-
36
- const PdfIcon = memo((props: SvgIconProps) => (
37
- <SvgIcon {...props}>
38
- <path d={mdiFilePdfBox} />
39
- </SvgIcon>
40
- ));
41
- const TextBoxIcon = memo((props: SvgIconProps) => (
42
- <SvgIcon {...props}>
43
- <path d={mdiTextBox} />
44
- </SvgIcon>
45
- ));
46
-
47
- type FileType = 'pdf' | 'image' | 'unknown';
48
- const PDF_FILE_PATTERN = /\.pdf$/;
49
- const IMAGE_FILE_PATTERN = /\.(gif|png|jpg|jpeg)$/;
50
-
51
- function getFileType(name: string): FileType {
52
- if (PDF_FILE_PATTERN.exec(name)) return 'pdf';
53
- if (IMAGE_FILE_PATTERN.exec(name)) return 'image';
54
- return 'unknown';
55
- }
56
-
57
- export interface FileListItemProps {
58
- url?: string;
59
- name: string;
60
- onRetry?: () => void;
61
- onDelete?: () => void;
62
- canDelete?: boolean;
63
- helperText?: ReactNode;
64
- status?: 'idle' | 'loading' | 'error' | 'success';
65
- }
66
- export const FileListItem = forwardRef<HTMLDivElement, FileListItemProps>(
67
- ({ url, name, status, canDelete = true, onRetry, onDelete }, ref) => {
68
- const uid = useUID();
69
- const fileType = getFileType(name);
70
- const [isHoveredState, setIsHovered] = useState(false);
71
- const isHovered = useResponsiveValue(true, isHoveredState);
72
-
73
- return (
74
- <div
75
- ref={ref}
76
- onMouseOver={() => {
77
- setIsHovered(true);
78
- }}
79
- onMouseLeave={() => {
80
- setIsHovered(false);
81
- }}
82
- aria-label="file-list-item"
83
- >
84
- <Columns align="center" space="xsmall">
85
- <Column width="content">
86
- {status === 'error' ? (
87
- <Error color="error" fontSize="small" />
88
- ) : fileType === 'pdf' ? (
89
- <PdfIcon color="action" fontSize="small" />
90
- ) : fileType === 'image' ? (
91
- <Image color="action" fontSize="small" />
92
- ) : (
93
- <TextBoxIcon color="action" fontSize="small" />
94
- )}
95
- </Column>
96
-
97
- <Column width="fluid">
98
- <FileListItemName id={uid}>
99
- {!url ? (
100
- name
101
- ) : (
102
- <Link
103
- href={url}
104
- noWrap={true}
105
- target="_blank"
106
- rel="noopener noreferrer"
107
- >
108
- {name}
109
- </Link>
110
- )}
111
- </FileListItemName>
112
- </Column>
113
-
114
- {status === 'error' && (
115
- <Column width="content">
116
- <Tooltip title="Retry">
117
- <IconButton size="small" onClick={onRetry}>
118
- <Refresh fontSize="small" />
119
- </IconButton>
120
- </Tooltip>
121
- </Column>
122
- )}
123
-
124
- <Column width="content">
125
- {status === 'loading' ? (
126
- <IconButton size="small" disabled={true}>
127
- <FileListItemProgress size="1em" />
128
- </IconButton>
129
- ) : !isHovered && status === 'success' ? (
130
- <IconButton size="small">
131
- <CheckCircle fontSize="small" htmlColor={Color.Green300} />
132
- </IconButton>
133
- ) : canDelete ? (
134
- <Tooltip title="Delete">
135
- <IconButton size="small" onClick={onDelete}>
136
- <Delete fontSize="small" />
137
- </IconButton>
138
- </Tooltip>
139
- ) : null}
140
- </Column>
141
- </Columns>
142
- </div>
143
- );
144
- },
145
- );