@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.
- package/LICENSE +21 -0
- package/{pkg/dist-node → dist-node}/index.js +4 -3
- package/dist-node/index.js.map +1 -0
- package/{pkg/dist-src → dist-src}/navbar/NavbarAccordion.js +5 -4
- package/{pkg/dist-web → dist-web}/index.js +4 -3
- package/dist-web/index.js.map +1 -0
- package/package.json +13 -39
- package/.babelrc.js +0 -5
- package/.turbo/turbo-version.log +0 -28
- package/pkg/README.md +0 -10
- package/pkg/dist-node/index.js.map +0 -1
- package/pkg/dist-web/index.js.map +0 -1
- package/pkg/package.json +0 -34
- package/playroom.ts +0 -23
- package/src/alert/Alert.stories.tsx +0 -105
- package/src/alert/Alert.tsx +0 -108
- package/src/banner/Banner.stories.tsx +0 -64
- package/src/banner/Banner.tsx +0 -120
- package/src/box/Box.stories.tsx +0 -20
- package/src/box/Box.tsx +0 -252
- package/src/button/Button.stories.tsx +0 -717
- package/src/button/Button.tsx +0 -460
- package/src/button-area/ButtonArea.stories.tsx +0 -65
- package/src/button-area/ButtonArea.tsx +0 -88
- package/src/container/Container.tsx +0 -48
- package/src/description-item/DescriptionItem.stories.tsx +0 -163
- package/src/description-item/DescriptionItem.tsx +0 -104
- package/src/file-drop-zone/FileDropZone.stories.tsx +0 -44
- package/src/file-drop-zone/FileDropZone.tsx +0 -170
- package/src/file-list-item/FileListItem.stories.tsx +0 -37
- package/src/file-list-item/FileListItem.tsx +0 -145
- package/src/file-list-item/__tests__/FileListItem.spec.tsx +0 -339
- package/src/index.spec.ts +0 -43
- package/src/index.ts +0 -28
- package/src/linked-text/LinkeText.stories.tsx +0 -42
- package/src/linked-text/LinkedText.tsx +0 -47
- package/src/multiline-text/MultilineText.stories.tsx +0 -30
- package/src/multiline-text/MultilineText.ts +0 -16
- package/src/navbar/Navbar.stories.tsx +0 -135
- package/src/navbar/Navbar.tsx +0 -111
- package/src/navbar/NavbarAccordion.tsx +0 -171
- package/src/navbar/NavbarAvatar.tsx +0 -51
- package/src/navbar/NavbarBottomBar.tsx +0 -135
- package/src/navbar/NavbarContext.tsx +0 -23
- package/src/navbar/NavbarItem.tsx +0 -119
- package/src/navbar/NavbarList.tsx +0 -225
- package/src/navbar/NavbarMenu.tsx +0 -102
- package/src/sidebar/Sidebar.stories.tsx +0 -363
- package/src/sidebar/Sidebar.tsx +0 -73
- package/src/sidebar/SidebarBackButton.tsx +0 -33
- package/src/sidebar/SidebarContainer.tsx +0 -114
- package/src/sidebar/SidebarContent.tsx +0 -119
- package/src/sidebar/SidebarDivider.tsx +0 -15
- package/src/sidebar/SidebarMenuItem.tsx +0 -211
- package/src/sidebar/SidebarMenuItemAction.tsx +0 -27
- package/src/sidebar/SidebarMenuItemAvatar.tsx +0 -59
- package/src/sidebar/SidebarMenuItemContext.tsx +0 -33
- package/src/sidebar/SidebarSubheader.tsx +0 -38
- package/src/styled.d.ts +0 -12
- package/src/text-box/TextBox.stories.tsx +0 -108
- package/src/text-box/TextBox.tsx +0 -229
- package/src/utils/RuleNormalizer.ts +0 -24
- package/src/utils/mergeStyles.ts +0 -28
- package/tsconfig.json +0 -19
- /package/{pkg/dist-src → dist-src}/alert/Alert.js +0 -0
- /package/{pkg/dist-src → dist-src}/banner/Banner.js +0 -0
- /package/{pkg/dist-src → dist-src}/box/Box.js +0 -0
- /package/{pkg/dist-src → dist-src}/button/Button.js +0 -0
- /package/{pkg/dist-src → dist-src}/button-area/ButtonArea.js +0 -0
- /package/{pkg/dist-src → dist-src}/container/Container.js +0 -0
- /package/{pkg/dist-src → dist-src}/description-item/DescriptionItem.js +0 -0
- /package/{pkg/dist-src → dist-src}/file-drop-zone/FileDropZone.js +0 -0
- /package/{pkg/dist-src → dist-src}/file-list-item/FileListItem.js +0 -0
- /package/{pkg/dist-src → dist-src}/index.js +0 -0
- /package/{pkg/dist-src → dist-src}/linked-text/LinkedText.js +0 -0
- /package/{pkg/dist-src → dist-src}/multiline-text/MultilineText.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/Navbar.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/NavbarAvatar.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/NavbarBottomBar.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/NavbarContext.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/NavbarItem.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/NavbarList.js +0 -0
- /package/{pkg/dist-src → dist-src}/navbar/NavbarMenu.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/Sidebar.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarBackButton.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarContainer.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarContent.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarDivider.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItem.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItemAction.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItemAvatar.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarMenuItemContext.js +0 -0
- /package/{pkg/dist-src → dist-src}/sidebar/SidebarSubheader.js +0 -0
- /package/{pkg/dist-src → dist-src}/text-box/TextBox.js +0 -0
- /package/{pkg/dist-src → dist-src}/utils/RuleNormalizer.js +0 -0
- /package/{pkg/dist-src → dist-src}/utils/mergeStyles.js +0 -0
- /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
|
-
);
|