@100mslive/roomkit-react 0.3.14-alpha.0 → 0.3.14-alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/Diagnostics/AudioTest.d.ts +2 -0
- package/dist/Diagnostics/ConnectivityTest.d.ts +7 -0
- package/dist/Diagnostics/Diagnostics.d.ts +2 -0
- package/dist/Diagnostics/VideoTest.d.ts +2 -0
- package/dist/Diagnostics/components.d.ts +16 -0
- package/dist/Diagnostics/hms.d.ts +9 -0
- package/dist/Diagnostics/index.d.ts +1 -0
- package/dist/{HLSView-7LHIA6HH.css → HLSView-772PCEEZ.css} +3 -3
- package/dist/{HLSView-7LHIA6HH.css.map → HLSView-772PCEEZ.css.map} +1 -1
- package/dist/{HLSView-USRUP6VG.js → HLSView-VSU7IPCJ.js} +2 -2
- package/dist/Prebuilt/App.d.ts +3 -1
- package/dist/Stats/index.d.ts +1 -0
- package/dist/{chunk-DYDYPNYY.js → chunk-VE34B77C.js} +12334 -1483
- package/dist/chunk-VE34B77C.js.map +7 -0
- package/dist/index.cjs.css +2 -2
- package/dist/index.cjs.css.map +1 -1
- package/dist/index.cjs.js +16767 -5828
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/dist/meta.cjs.json +896 -190
- package/dist/meta.esbuild.json +908 -198
- package/package.json +7 -7
- package/src/Diagnostics/AudioTest.tsx +153 -0
- package/src/Diagnostics/ConnectivityTest.tsx +355 -0
- package/src/Diagnostics/DeviceSelector.jsx +71 -0
- package/src/Diagnostics/Diagnostics.tsx +112 -0
- package/src/Diagnostics/VideoTest.tsx +60 -0
- package/src/Diagnostics/components.tsx +84 -0
- package/src/Diagnostics/hms.ts +9 -0
- package/src/Diagnostics/index.ts +1 -0
- package/src/Prebuilt/App.tsx +12 -1
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +16 -2
- package/src/Prebuilt/components/StatsForNerds.jsx +1 -13
- package/src/Stats/index.tsx +1 -0
- package/src/index.ts +1 -0
- package/dist/chunk-DYDYPNYY.js.map +0 -7
- /package/dist/{HLSView-USRUP6VG.js.map → HLSView-VSU7IPCJ.js.map} +0 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
import React, { useContext } from 'react';
|
2
|
+
import { HMSRoomProvider } from '@100mslive/react-sdk';
|
3
|
+
import { ConnectivityIcon, GlobeIcon, MicOnIcon, VideoOnIcon } from '@100mslive/react-icons';
|
4
|
+
import { DiagnosticsContext, DiagnosticsSteps } from './components';
|
5
|
+
import { Box, Flex } from '../Layout';
|
6
|
+
import { Text } from '../Text';
|
7
|
+
import { HMSThemeProvider } from '../Theme';
|
8
|
+
import { AudioTest } from './AudioTest';
|
9
|
+
import { ConnectivityTest } from './ConnectivityTest';
|
10
|
+
import { hmsActions, hmsNotifications, hmsStats, hmsStore } from './hms';
|
11
|
+
import { VideoTest } from './VideoTest';
|
12
|
+
|
13
|
+
const DiagnosticsStepIcon: Record<string, React.ReactNode> = {
|
14
|
+
video: <VideoOnIcon width="2rem" height="2rem" />,
|
15
|
+
audio: <MicOnIcon width="2rem" height="2rem" />,
|
16
|
+
browser: <GlobeIcon width="2rem" height="2rem" />,
|
17
|
+
connectivity: <ConnectivityIcon width="2rem" height="2rem" />,
|
18
|
+
};
|
19
|
+
|
20
|
+
const Container = ({ children }: { children: React.ReactNode }) => (
|
21
|
+
<Box
|
22
|
+
css={{
|
23
|
+
px: '155px',
|
24
|
+
py: '160px',
|
25
|
+
bg: '$background_dim',
|
26
|
+
lineHeight: '1.5',
|
27
|
+
'-webkit-text-size-adjust': '100%',
|
28
|
+
position: 'relative',
|
29
|
+
minHeight: '100vh',
|
30
|
+
'@lg': {
|
31
|
+
p: '$12',
|
32
|
+
},
|
33
|
+
}}
|
34
|
+
>
|
35
|
+
{children}
|
36
|
+
</Box>
|
37
|
+
);
|
38
|
+
|
39
|
+
const DiagnosticsStepTest = () => {
|
40
|
+
const { activeStep } = useContext(DiagnosticsContext);
|
41
|
+
|
42
|
+
let TestComponent = () => <></>;
|
43
|
+
|
44
|
+
if (activeStep === 'audio') {
|
45
|
+
TestComponent = AudioTest;
|
46
|
+
} else if (activeStep === 'video') {
|
47
|
+
TestComponent = VideoTest;
|
48
|
+
} else if (activeStep === 'connectivity') {
|
49
|
+
TestComponent = ConnectivityTest;
|
50
|
+
}
|
51
|
+
|
52
|
+
return <TestComponent key={activeStep} />;
|
53
|
+
};
|
54
|
+
|
55
|
+
const DiagnosticsStepHeader = () => {
|
56
|
+
const { activeStep } = useContext(DiagnosticsContext);
|
57
|
+
return (
|
58
|
+
<Flex css={{ py: '$8', px: '$10', alignItems: 'center', borderBottom: '1px solid $border_default' }}>
|
59
|
+
<Text css={{ c: '$primary_bright', mt: '$xs' }}>{DiagnosticsStepIcon[activeStep]}</Text>
|
60
|
+
<Text css={{ fontSize: '$h6', ml: '$9' }}>{DiagnosticsSteps[activeStep]}</Text>
|
61
|
+
</Flex>
|
62
|
+
);
|
63
|
+
};
|
64
|
+
|
65
|
+
const DiagnosticsStep = () => {
|
66
|
+
return (
|
67
|
+
<Box css={{ border: '1px solid $border_default', r: '$1', w: '75%', '@lg': { w: '100%' } }}>
|
68
|
+
<DiagnosticsStepHeader />
|
69
|
+
<DiagnosticsStepTest />
|
70
|
+
</Box>
|
71
|
+
);
|
72
|
+
};
|
73
|
+
|
74
|
+
const DiagnosticsStepsList = () => {
|
75
|
+
const { activeStep } = useContext(DiagnosticsContext);
|
76
|
+
|
77
|
+
return (
|
78
|
+
<Box css={{ w: '25%', '@lg': { display: 'none' } }}>
|
79
|
+
<ul>
|
80
|
+
{Object.keys(DiagnosticsSteps).map(key => (
|
81
|
+
<li key={key}>
|
82
|
+
<Text variant="md" css={{ mb: '$10', c: activeStep === key ? '$on_primary_high' : '$on_primary_low' }}>
|
83
|
+
{DiagnosticsSteps[key]}
|
84
|
+
</Text>
|
85
|
+
</li>
|
86
|
+
))}
|
87
|
+
</ul>
|
88
|
+
</Box>
|
89
|
+
);
|
90
|
+
};
|
91
|
+
|
92
|
+
export const Diagnostics = () => {
|
93
|
+
const [activeStep, setActiveStep] = React.useState(Object.keys(DiagnosticsSteps)[0]);
|
94
|
+
return (
|
95
|
+
<HMSRoomProvider store={hmsStore} actions={hmsActions} notifications={hmsNotifications} stats={hmsStats}>
|
96
|
+
<HMSThemeProvider themeType="default">
|
97
|
+
<DiagnosticsContext.Provider value={{ activeStep, setActiveStep }}>
|
98
|
+
<Container>
|
99
|
+
<Text variant="h4">Pre-call Test</Text>
|
100
|
+
<Text variant="md" css={{ c: '$on_primary_medium' }}>
|
101
|
+
Make sure your devices and network are good to go, let's get started.
|
102
|
+
</Text>
|
103
|
+
<Flex css={{ direction: 'column', mt: '$12', justifyItems: 'center' }}>
|
104
|
+
<DiagnosticsStepsList />
|
105
|
+
<DiagnosticsStep />
|
106
|
+
</Flex>
|
107
|
+
</Container>
|
108
|
+
</DiagnosticsContext.Provider>
|
109
|
+
</HMSThemeProvider>
|
110
|
+
</HMSRoomProvider>
|
111
|
+
);
|
112
|
+
};
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
2
|
+
import { selectDevices, selectLocalMediaSettings, selectLocalVideoTrackID, useHMSStore } from '@100mslive/react-sdk';
|
3
|
+
import { VideoOnIcon } from '@100mslive/react-icons';
|
4
|
+
import { TestContainer, TestFooter } from './components';
|
5
|
+
import { Flex } from '../Layout';
|
6
|
+
import { Text } from '../Text';
|
7
|
+
import { Video } from '../Video';
|
8
|
+
import { StyledVideoTile } from '../VideoTile';
|
9
|
+
// @ts-ignore: No implicit any
|
10
|
+
import { DeviceSelector } from './DeviceSelector';
|
11
|
+
import { hmsDiagnostics } from './hms';
|
12
|
+
|
13
|
+
export const VideoTest = () => {
|
14
|
+
const allDevices = useHMSStore(selectDevices);
|
15
|
+
const { videoInput } = allDevices;
|
16
|
+
const trackID = useHMSStore(selectLocalVideoTrackID);
|
17
|
+
const sdkSelectedDevices = useHMSStore(selectLocalMediaSettings);
|
18
|
+
const [error, setError] = useState<Error | undefined>();
|
19
|
+
|
20
|
+
useEffect(() => {
|
21
|
+
hmsDiagnostics.startCameraCheck().catch(err => setError(err));
|
22
|
+
}, []);
|
23
|
+
|
24
|
+
return (
|
25
|
+
<>
|
26
|
+
<TestContainer css={{ display: 'flex', '@lg': { flexDirection: 'column', alignItems: 'center' } }}>
|
27
|
+
{trackID && (
|
28
|
+
<StyledVideoTile.Container
|
29
|
+
css={{
|
30
|
+
w: '90%',
|
31
|
+
height: '$48',
|
32
|
+
mr: '$10',
|
33
|
+
'@lg': { mr: 0, mb: '$10' },
|
34
|
+
}}
|
35
|
+
>
|
36
|
+
<Video mirror={true} trackId={trackID} />
|
37
|
+
</StyledVideoTile.Container>
|
38
|
+
)}
|
39
|
+
<Flex direction="column" css={{ w: '100%' }}>
|
40
|
+
<Text variant="body2" css={{ c: '$on_primary_medium', mb: '$10' }}>
|
41
|
+
Move in front of your camera to make sure it's working. If you don't see your video, try changing the
|
42
|
+
selected camera. If the camera isn't part of your computer, check your settings to make sure your system
|
43
|
+
recognizes it.
|
44
|
+
</Text>
|
45
|
+
<DeviceSelector
|
46
|
+
title="Video"
|
47
|
+
devices={videoInput || []}
|
48
|
+
icon={<VideoOnIcon />}
|
49
|
+
selection={sdkSelectedDevices.videoInputDeviceId}
|
50
|
+
onChange={async (deviceId: string) => {
|
51
|
+
hmsDiagnostics.stopCameraCheck();
|
52
|
+
hmsDiagnostics.startCameraCheck(deviceId);
|
53
|
+
}}
|
54
|
+
/>
|
55
|
+
</Flex>
|
56
|
+
</TestContainer>
|
57
|
+
<TestFooter error={error} ctaText="Does your video look good?" />
|
58
|
+
</>
|
59
|
+
);
|
60
|
+
};
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import React, { useContext } from 'react';
|
2
|
+
import { Button } from '../Button';
|
3
|
+
import { Box, Flex } from '../Layout';
|
4
|
+
import { Text } from '../Text';
|
5
|
+
import { CSS } from '../Theme';
|
6
|
+
import { hmsDiagnostics } from './hms';
|
7
|
+
|
8
|
+
export const DiagnosticsSteps: Record<string, string> = {
|
9
|
+
video: 'Test Video',
|
10
|
+
audio: 'Test Audio',
|
11
|
+
// browser: 'Browser Support',
|
12
|
+
connectivity: 'Connection Quality',
|
13
|
+
};
|
14
|
+
|
15
|
+
export const DiagnosticsContext = React.createContext<{
|
16
|
+
activeStep: string;
|
17
|
+
setActiveStep: React.Dispatch<React.SetStateAction<string>>;
|
18
|
+
}>({
|
19
|
+
activeStep: 'video',
|
20
|
+
setActiveStep: () => {
|
21
|
+
return;
|
22
|
+
},
|
23
|
+
});
|
24
|
+
|
25
|
+
export const TestContainer = ({ css, children }: { css?: CSS; children: React.ReactNode }) => {
|
26
|
+
return <Box css={{ p: '$10', ...css }}>{children}</Box>;
|
27
|
+
};
|
28
|
+
|
29
|
+
export const TestFooter = ({
|
30
|
+
error,
|
31
|
+
ctaText,
|
32
|
+
children,
|
33
|
+
}: {
|
34
|
+
ctaText?: string;
|
35
|
+
error?: Error;
|
36
|
+
children?: React.ReactNode;
|
37
|
+
}) => {
|
38
|
+
const { activeStep, setActiveStep } = useContext(DiagnosticsContext);
|
39
|
+
|
40
|
+
const onNextStep = () => {
|
41
|
+
if (activeStep === 'audio') {
|
42
|
+
hmsDiagnostics.stopMicCheck();
|
43
|
+
} else if (activeStep === 'video') {
|
44
|
+
hmsDiagnostics.stopCameraCheck();
|
45
|
+
} else if (activeStep === 'connectivity') {
|
46
|
+
hmsDiagnostics.stopConnectivityCheck();
|
47
|
+
}
|
48
|
+
|
49
|
+
const keys = Object.keys(DiagnosticsSteps);
|
50
|
+
setActiveStep(step => keys[keys.indexOf(step) + 1]);
|
51
|
+
};
|
52
|
+
|
53
|
+
return (
|
54
|
+
<Flex
|
55
|
+
css={{
|
56
|
+
py: '$8',
|
57
|
+
px: '$10',
|
58
|
+
justifyContent: 'space-between',
|
59
|
+
alignItems: 'center',
|
60
|
+
borderTop: '1px solid $border_default',
|
61
|
+
fontSize: '$sm',
|
62
|
+
lineHeight: '$sm',
|
63
|
+
'@lg': { flexDirection: 'column', gap: '$8' },
|
64
|
+
}}
|
65
|
+
>
|
66
|
+
<Box>{error && <Text css={{ c: '$alert_error_default' }}>Error: {error.message}</Text>}</Box>
|
67
|
+
{children ? (
|
68
|
+
children
|
69
|
+
) : (
|
70
|
+
<Flex align="center" css={{ gap: '$8', '@lg': { flexDirection: 'column' } }}>
|
71
|
+
<Text css={{ c: '$on_primary_medium' }}>{ctaText}</Text>
|
72
|
+
<Flex align="center" gap="4">
|
73
|
+
<Button onClick={onNextStep} variant="standard" outlined={true}>
|
74
|
+
Skip
|
75
|
+
</Button>
|
76
|
+
<Button disabled={!!error} onClick={onNextStep}>
|
77
|
+
Yes
|
78
|
+
</Button>
|
79
|
+
</Flex>
|
80
|
+
</Flex>
|
81
|
+
)}
|
82
|
+
</Flex>
|
83
|
+
);
|
84
|
+
};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
// @ts-check
|
2
|
+
import { HMSReactiveStore } from '@100mslive/hms-video-store';
|
3
|
+
|
4
|
+
const hms = new HMSReactiveStore();
|
5
|
+
export const hmsStore = hms.getStore();
|
6
|
+
export const hmsActions = hms.getActions();
|
7
|
+
export const hmsNotifications = hms.getNotifications();
|
8
|
+
export const hmsStats = hms.getStats();
|
9
|
+
export const hmsDiagnostics = hms.getDiagnosticsSDK();
|
@@ -0,0 +1 @@
|
|
1
|
+
export { Diagnostics } from './Diagnostics';
|
package/src/Prebuilt/App.tsx
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
import React, { MutableRefObject, useEffect, useRef } from 'react';
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
HMSDiagnosticsInterface,
|
4
|
+
HMSStatsStoreWrapper,
|
5
|
+
HMSStoreWrapper,
|
6
|
+
IHMSNotifications,
|
7
|
+
} from '@100mslive/hms-video-store';
|
3
8
|
import { Layout, Logo, Screens, Theme, Typography } from '@100mslive/types-prebuilt';
|
4
9
|
import { match } from 'ts-pattern';
|
5
10
|
import {
|
@@ -65,6 +70,7 @@ export type HMSPrebuiltProps = {
|
|
65
70
|
options?: HMSPrebuiltOptions;
|
66
71
|
screens?: Screens;
|
67
72
|
authToken?: string;
|
73
|
+
leaveOnUnload?: boolean;
|
68
74
|
onLeave?: () => void;
|
69
75
|
onJoin?: () => void;
|
70
76
|
/**
|
@@ -79,6 +85,7 @@ export type HMSPrebuiltRefType = {
|
|
79
85
|
hmsStore: HMSStoreWrapper;
|
80
86
|
hmsStats: HMSStatsStoreWrapper;
|
81
87
|
hmsNotifications: IHMSNotifications;
|
88
|
+
hmsDiagnostics: HMSDiagnosticsInterface;
|
82
89
|
};
|
83
90
|
|
84
91
|
export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps>(
|
@@ -92,6 +99,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
|
|
92
99
|
themes,
|
93
100
|
options: { userName = '', userId = '', endpoints } = {},
|
94
101
|
screens,
|
102
|
+
leaveOnUnload = true,
|
95
103
|
onLeave,
|
96
104
|
onJoin,
|
97
105
|
},
|
@@ -107,6 +115,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
|
|
107
115
|
const hmsActions = hms.getActions();
|
108
116
|
const hmsNotifications = hms.getNotifications();
|
109
117
|
const hmsStats = hms.getStats();
|
118
|
+
const hmsDiagnostics = hms.getDiagnosticsSDK();
|
110
119
|
hms.triggerOnSubscribe();
|
111
120
|
|
112
121
|
reactiveStore.current = {
|
@@ -114,6 +123,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
|
|
114
123
|
hmsStats,
|
115
124
|
hmsStore,
|
116
125
|
hmsNotifications,
|
126
|
+
hmsDiagnostics,
|
117
127
|
};
|
118
128
|
}, []);
|
119
129
|
|
@@ -188,6 +198,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
|
|
188
198
|
store={reactiveStore.current?.hmsStore}
|
189
199
|
notifications={reactiveStore.current?.hmsNotifications}
|
190
200
|
stats={reactiveStore.current?.hmsStats}
|
201
|
+
leaveOnUnload={leaveOnUnload}
|
191
202
|
>
|
192
203
|
<RoomLayoutProvider roomLayoutEndpoint={roomLayoutEndpoint} overrideLayout={overrideLayout}>
|
193
204
|
<RoomLayoutContext.Consumer>
|
@@ -22,6 +22,8 @@ import { useEmojiPickerStyles } from './useEmojiPickerStyles';
|
|
22
22
|
import { useDefaultChatSelection, useLandscapeHLSStream, useMobileHLSStream } from '../../common/hooks';
|
23
23
|
import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants';
|
24
24
|
|
25
|
+
const CHAT_MESSAGE_LIMIT = 2000;
|
26
|
+
|
25
27
|
const TextArea = styled('textarea', {
|
26
28
|
width: '100%',
|
27
29
|
bg: 'transparent',
|
@@ -90,6 +92,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
90
92
|
const selection = selectedPeer.name || selectedRole || defaultSelection;
|
91
93
|
const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true });
|
92
94
|
const isMwebHLSStream = useMobileHLSStream();
|
95
|
+
const [messageLengthExceeded, setMessageLengthExceeded] = useState(false);
|
93
96
|
const isLandscapeHLSStream = useLandscapeHLSStream();
|
94
97
|
|
95
98
|
useEffect(() => {
|
@@ -146,6 +149,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
146
149
|
if (messageElement) {
|
147
150
|
messageElement.value = draftMessage;
|
148
151
|
updateInputHeight();
|
152
|
+
setMessageLengthExceeded(draftMessage.length > CHAT_MESSAGE_LIMIT);
|
149
153
|
}
|
150
154
|
}, [draftMessage]);
|
151
155
|
|
@@ -230,6 +234,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
230
234
|
>
|
231
235
|
{children}
|
232
236
|
<TextArea
|
237
|
+
maxLength={CHAT_MESSAGE_LIMIT + 10}
|
233
238
|
css={{
|
234
239
|
c: '$on_surface_high',
|
235
240
|
'&:valid ~ .send-msg': { color: '$on_surface_high' },
|
@@ -243,7 +248,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
243
248
|
autoFocus={!(isMobile || isLandscapeHLSStream)}
|
244
249
|
onKeyPress={async event => {
|
245
250
|
if (event.key === 'Enter') {
|
246
|
-
if (!event.shiftKey) {
|
251
|
+
if (!event.shiftKey && !messageLengthExceeded) {
|
247
252
|
event.preventDefault();
|
248
253
|
await sendMessage();
|
249
254
|
}
|
@@ -251,7 +256,10 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
251
256
|
}}
|
252
257
|
autoComplete="off"
|
253
258
|
aria-autocomplete="none"
|
254
|
-
onChange={
|
259
|
+
onChange={e => {
|
260
|
+
updateInputHeight();
|
261
|
+
setMessageLengthExceeded(e.target.value.length > CHAT_MESSAGE_LIMIT);
|
262
|
+
}}
|
255
263
|
onBlur={resetInputHeight}
|
256
264
|
onPaste={e => e.stopPropagation()}
|
257
265
|
onCut={e => e.stopPropagation()}
|
@@ -269,6 +277,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
269
277
|
<BaseIconButton
|
270
278
|
className="send-msg"
|
271
279
|
onClick={sendMessage}
|
280
|
+
disabled={messageLengthExceeded}
|
272
281
|
css={{
|
273
282
|
ml: 'auto',
|
274
283
|
height: 'max-content',
|
@@ -295,6 +304,11 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
|
|
295
304
|
)}
|
296
305
|
</Flex>
|
297
306
|
)}
|
307
|
+
{messageLengthExceeded && (
|
308
|
+
<Text variant="xs" css={{ color: '$alert_error_default', fontWeight: '$semiBold', mt: '$1', ml: '$7' }}>
|
309
|
+
Message cannot exceed 2000 characters
|
310
|
+
</Text>
|
311
|
+
)}
|
298
312
|
</Box>
|
299
313
|
);
|
300
314
|
};
|
@@ -13,6 +13,7 @@ import { Dropdown } from '../../Dropdown';
|
|
13
13
|
import { Label } from '../../Label';
|
14
14
|
import { Box, Flex } from '../../Layout';
|
15
15
|
import { Dialog } from '../../Modal';
|
16
|
+
import { formatBytes } from '../../Stats';
|
16
17
|
import { Switch } from '../../Switch';
|
17
18
|
import { Text } from '../../Text';
|
18
19
|
import { DialogDropdownTrigger } from '../primitives/DropdownTrigger';
|
@@ -245,16 +246,3 @@ const StatsRow = React.memo(({ label, value }) => (
|
|
245
246
|
</Text>
|
246
247
|
</Box>
|
247
248
|
));
|
248
|
-
|
249
|
-
const formatBytes = (bytes, unit = 'B', decimals = 2) => {
|
250
|
-
if (bytes === undefined) return '-';
|
251
|
-
if (bytes === 0) return '0 ' + unit;
|
252
|
-
|
253
|
-
const k = 1024;
|
254
|
-
const dm = decimals < 0 ? 0 : decimals;
|
255
|
-
const sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'].map(size => size + unit);
|
256
|
-
|
257
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
258
|
-
|
259
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
260
|
-
};
|
package/src/Stats/index.tsx
CHANGED
package/src/index.ts
CHANGED