@zesty-io/material 0.1.1 → 0.1.2
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/es/ConfirmDialog/ConfirmDialog.stories.d.ts +6 -0
- package/es/ConfirmDialog/ConfirmDialog.stories.js +24 -0
- package/es/ConfirmDialog/index.d.ts +18 -0
- package/es/ConfirmDialog/index.js +11 -0
- package/es/FieldTypeOneToMany/FieldTypeOneToMany.stories.js +6 -7
- package/es/FieldTypeOneToMany/index.d.ts +25 -7
- package/es/FieldTypeOneToMany/index.js +6 -7
- package/es/index.d.ts +1 -1
- package/es/index.js +1 -1
- package/es/utils/virtualization.d.ts +0 -1
- package/es/utils/virtualization.js +0 -11
- package/package.json +1 -1
- package/src/{ConfirmModal/ConfirmModal.stories.tsx → ConfirmDialog/ConfirmDialog.stories.tsx} +0 -0
- package/src/{ConfirmModal → ConfirmDialog}/index.tsx +0 -0
- package/src/FieldTypeOneToMany/FieldTypeOneToMany.stories.tsx +11 -11
- package/src/FieldTypeOneToMany/index.tsx +54 -29
- package/src/FieldTypeOneToOne/index.tsx +0 -1
- package/src/index.ts +1 -1
- package/src/utils/virtualization.tsx +0 -18
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Story, Meta } from '@storybook/react/types-6-0';
|
|
2
|
+
import { ConfirmDialogProps } from '.';
|
|
3
|
+
declare const _default: Meta<import("@storybook/react/types-6-0").Args>;
|
|
4
|
+
export default _default;
|
|
5
|
+
export declare const Default: Story<ConfirmDialogProps>;
|
|
6
|
+
export declare const CustomChildren: Story<ConfirmDialogProps>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Button } from '@mui/material';
|
|
4
|
+
import ConfirmDialog from '.';
|
|
5
|
+
export default {
|
|
6
|
+
title: 'ConfirmDialog',
|
|
7
|
+
component: ConfirmDialog,
|
|
8
|
+
argType: {},
|
|
9
|
+
};
|
|
10
|
+
const Template = (args) => {
|
|
11
|
+
const [open, setOpen] = useState(false);
|
|
12
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "contained", onClick: () => setOpen(true), children: "Click me to open" }), _jsx(ConfirmDialog, { ...args, open: open, callback: (confirmed) => setOpen(false) })] }));
|
|
13
|
+
};
|
|
14
|
+
export const Default = Template.bind({});
|
|
15
|
+
Default.args = {
|
|
16
|
+
title: 'Confirm modal title',
|
|
17
|
+
content: 'Confirm modal content',
|
|
18
|
+
};
|
|
19
|
+
export const CustomChildren = Template.bind({});
|
|
20
|
+
CustomChildren.args = {
|
|
21
|
+
title: 'Confirm modal title',
|
|
22
|
+
content: 'Confirm modal content',
|
|
23
|
+
children: _jsxs(_Fragment, { children: [_jsx(Button, { color: "error", variant: "contained", children: "Custom 1" }), _jsx(Button, { color: "success", variant: "contained", children: "Custom 2" })] })
|
|
24
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { DialogProps } from '@mui/material/Dialog';
|
|
3
|
+
export interface ConfirmDialogProps extends Omit<DialogProps, 'title'> {
|
|
4
|
+
/**
|
|
5
|
+
* Title of confirm dialog
|
|
6
|
+
*/
|
|
7
|
+
title: string | ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Content of confirm dialog
|
|
10
|
+
*/
|
|
11
|
+
content: string | ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Callback of confirm dialog
|
|
14
|
+
*/
|
|
15
|
+
callback: (confirmed: boolean) => void;
|
|
16
|
+
}
|
|
17
|
+
declare const ConfirmDialog: ({ title, content, onClose, children, callback, ...props }: ConfirmDialogProps) => JSX.Element;
|
|
18
|
+
export default ConfirmDialog;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Dialog from '@mui/material/Dialog';
|
|
3
|
+
import DialogActions from '@mui/material/DialogActions';
|
|
4
|
+
import DialogContent from '@mui/material/DialogContent';
|
|
5
|
+
import DialogContentText from '@mui/material/DialogContentText';
|
|
6
|
+
import DialogTitle from '@mui/material/DialogTitle';
|
|
7
|
+
import { Button } from '@mui/material';
|
|
8
|
+
const ConfirmDialog = ({ title, content, onClose, children, callback, ...props }) => {
|
|
9
|
+
return (_jsxs(Dialog, { ...props, children: [_jsx(DialogTitle, { children: title }), _jsx(DialogContent, { children: _jsx(DialogContentText, { children: content }) }), children ? _jsx(DialogActions, { children: children }) : (_jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", onClick: () => callback(false), children: "No" }), _jsx(Button, { variant: "contained", onClick: () => callback(true), autoFocus: true, children: "Yes" })] }))] }));
|
|
10
|
+
};
|
|
11
|
+
export default ConfirmDialog;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import FieldTypeOneToMany from '.';
|
|
4
|
-
import { randomOptions } from '../utils/virtualization';
|
|
5
4
|
export default {
|
|
6
5
|
title: 'FieldTypeOneToMany',
|
|
7
6
|
component: FieldTypeOneToMany,
|
|
@@ -11,8 +10,10 @@ const Template = (args) => {
|
|
|
11
10
|
const [value, setValue] = useState([]);
|
|
12
11
|
const [options, setOptions] = useState([]);
|
|
13
12
|
const handleOnOpen = async () => {
|
|
13
|
+
const largeArr = new Array(1000).fill(null);
|
|
14
14
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
15
|
-
|
|
15
|
+
const data = largeArr.map((_, idx) => ({ component: _jsx("div", { children: `Test ${idx}` }), value: String(Math.random()), inputLabel: `Test ${idx}` }));
|
|
16
|
+
setOptions(data);
|
|
16
17
|
};
|
|
17
18
|
const handleOnChange = (e, values) => {
|
|
18
19
|
setValue(values);
|
|
@@ -21,9 +22,7 @@ const Template = (args) => {
|
|
|
21
22
|
};
|
|
22
23
|
export const Default = Template.bind({});
|
|
23
24
|
Default.args = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
helperText: 'OneToOne helperText',
|
|
28
|
-
}
|
|
25
|
+
placeholder: 'Placeholder Text...',
|
|
26
|
+
label: 'OneToMany label',
|
|
27
|
+
helperText: 'OneToMany helperText',
|
|
29
28
|
};
|
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
import { AutocompleteProps
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { AutocompleteProps } from '@mui/material';
|
|
3
3
|
export interface FieldTypeOneToManyProps extends Omit<AutocompleteProps<any, false, false, false>, 'onOpen' | 'renderInput'> {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
label?: string;
|
|
5
|
+
helperText?: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
error?: boolean;
|
|
8
|
+
required?: boolean;
|
|
8
9
|
/**
|
|
9
10
|
* Callback to be fired upon opening the dropdown
|
|
10
11
|
*/
|
|
11
12
|
onOpen: () => Promise<any>;
|
|
13
|
+
/**
|
|
14
|
+
* Structure for option
|
|
15
|
+
*/
|
|
16
|
+
options: {
|
|
17
|
+
/**
|
|
18
|
+
* Component to be rendered in the dropdown
|
|
19
|
+
*/
|
|
20
|
+
component: ReactNode | string;
|
|
21
|
+
/**
|
|
22
|
+
* Value of option
|
|
23
|
+
*/
|
|
24
|
+
value: string;
|
|
25
|
+
/**
|
|
26
|
+
* Label that should display in the input when selected
|
|
27
|
+
*/
|
|
28
|
+
inputLabel: string;
|
|
29
|
+
}[];
|
|
12
30
|
}
|
|
13
|
-
declare const FieldTypeOneToMany: ({
|
|
31
|
+
declare const FieldTypeOneToMany: ({ label, helperText, placeholder, error, onOpen, options, required, ...props }: FieldTypeOneToManyProps) => JSX.Element;
|
|
14
32
|
export default FieldTypeOneToMany;
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { Popper, styled, TextField } from '@mui/material';
|
|
3
|
+
import { FormControl, FormLabel, Popper, styled, TextField } from '@mui/material';
|
|
4
4
|
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
|
|
5
5
|
import { ListboxComponent } from '../utils/virtualization';
|
|
6
|
-
const FieldTypeOneToMany = ({
|
|
6
|
+
const FieldTypeOneToMany = ({ label, helperText, placeholder, error, onOpen, options, required, ...props }) => {
|
|
7
7
|
const [loaded, setLoaded] = useState(false);
|
|
8
|
-
const [loading, setLoading] = useState(
|
|
8
|
+
const [loading, setLoading] = useState(false);
|
|
9
9
|
const handleOpen = () => {
|
|
10
10
|
if (!loaded && onOpen) {
|
|
11
11
|
onOpen().then(() => {
|
|
12
12
|
setLoading(false);
|
|
13
13
|
});
|
|
14
|
+
setLoading(true);
|
|
14
15
|
setLoaded(true);
|
|
15
16
|
}
|
|
16
17
|
};
|
|
17
|
-
return (_jsx(Autocomplete, { onOpen: handleOpen, loading: loading, fullWidth: true, multiple: true, disableListWrap: true, PopperComponent: StyledPopper, ListboxComponent: ListboxComponent, renderInput: (params) => (_jsx(TextField, { ...params,
|
|
18
|
-
shrink: true,
|
|
19
|
-
} })), renderOption: (props, option) => [props, option], ...props }));
|
|
18
|
+
return (_jsxs(FormControl, { fullWidth: true, required: required, children: [_jsx(FormLabel, { children: label }), _jsx(Autocomplete, { onOpen: handleOpen, loading: loading, fullWidth: true, multiple: true, disableListWrap: true, disableClearable: true, disablePortal: true, size: 'small', PopperComponent: StyledPopper, ListboxComponent: ListboxComponent, renderInput: (params) => (_jsx(TextField, { ...params, helperText: helperText, error: error, placeholder: placeholder })), options: loading ? [] : options, getOptionLabel: (option) => option.inputLabel, renderOption: (props, option) => [props, option.component], ...props })] }));
|
|
20
19
|
};
|
|
21
20
|
export default FieldTypeOneToMany;
|
|
22
21
|
const StyledPopper = styled(Popper)({
|
package/es/index.d.ts
CHANGED
|
@@ -10,4 +10,4 @@ export { default as FieldTypeDropdown } from './FieldTypeDropdown';
|
|
|
10
10
|
export { default as FieldTypeOneToOne } from './FieldTypeOneToOne';
|
|
11
11
|
export { default as FieldTypeOneToMany } from './FieldTypeOneToMany';
|
|
12
12
|
export { default as CopyButton } from './CopyButton';
|
|
13
|
-
export { default as
|
|
13
|
+
export { default as ConfirmDialog } from './ConfirmDialog';
|
package/es/index.js
CHANGED
|
@@ -10,4 +10,4 @@ export { default as FieldTypeDropdown } from './FieldTypeDropdown';
|
|
|
10
10
|
export { default as FieldTypeOneToOne } from './FieldTypeOneToOne';
|
|
11
11
|
export { default as FieldTypeOneToMany } from './FieldTypeOneToMany';
|
|
12
12
|
export { default as CopyButton } from './CopyButton';
|
|
13
|
-
export { default as
|
|
13
|
+
export { default as ConfirmDialog } from './ConfirmDialog';
|
|
@@ -61,14 +61,3 @@ export const ListboxComponent = React.forwardRef(function ListboxComponent(props
|
|
|
61
61
|
const gridRef = useResetCache(itemCount);
|
|
62
62
|
return (_jsx("div", { ref: ref, children: _jsx(OuterElementContext.Provider, { value: other, children: _jsx(VariableSizeList, { itemData: itemData, height: getHeight() + 2 * LISTBOX_PADDING, width: "100%", ref: gridRef, outerElementType: OuterElementType, innerElementType: "ul", itemSize: (index) => getChildSize(itemData[index]), overscanCount: 5, itemCount: itemCount, children: renderRow }) }) }));
|
|
63
63
|
});
|
|
64
|
-
function random(length) {
|
|
65
|
-
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
66
|
-
let result = '';
|
|
67
|
-
for (let i = 0; i < length; i += 1) {
|
|
68
|
-
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
69
|
-
}
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
export const randomOptions = Array.from(new Array(10000))
|
|
73
|
-
.map(() => random(10 + Math.ceil(Math.random() * 20)))
|
|
74
|
-
.sort((a, b) => a.toUpperCase().localeCompare(b.toUpperCase()));
|
package/package.json
CHANGED
package/src/{ConfirmModal/ConfirmModal.stories.tsx → ConfirmDialog/ConfirmDialog.stories.tsx}
RENAMED
|
File without changes
|
|
File without changes
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { SyntheticEvent, useState } from 'react';
|
|
1
|
+
import { ReactNode, SyntheticEvent, useState } from 'react';
|
|
2
2
|
import { Story, Meta } from '@storybook/react/types-6-0';
|
|
3
3
|
import FieldTypeOneToMany, { FieldTypeOneToManyProps } from '.';
|
|
4
|
-
import { randomOptions } from '../utils/virtualization';
|
|
5
4
|
|
|
6
5
|
export default {
|
|
7
6
|
title: 'FieldTypeOneToMany',
|
|
@@ -10,16 +9,19 @@ export default {
|
|
|
10
9
|
} as Meta;
|
|
11
10
|
|
|
12
11
|
const Template: Story<FieldTypeOneToManyProps> = (args) => {
|
|
13
|
-
const [value, setValue] = useState<string[]>([]);
|
|
12
|
+
const [value, setValue] = useState<{component: string | ReactNode, value: string, inputLabel: string}[]>([]);
|
|
13
|
+
|
|
14
|
+
const [options, setOptions] = useState<{component: string | ReactNode, value: string, inputLabel: string}[]>([]);
|
|
14
15
|
|
|
15
|
-
const [options, setOptions] = useState<string[]>([]);
|
|
16
16
|
|
|
17
17
|
const handleOnOpen = async () => {
|
|
18
|
+
const largeArr = new Array(1000).fill(null);
|
|
18
19
|
await new Promise((resolve) => setTimeout(resolve, 3000))
|
|
19
|
-
|
|
20
|
+
const data = largeArr.map((_, idx) => ({component: <div>{`Test ${idx}`}</div>, value: String(Math.random()), inputLabel: `Test ${idx}`}));
|
|
21
|
+
setOptions(data);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
const handleOnChange = (e: SyntheticEvent<Element, Event>, values: string[]) => {
|
|
24
|
+
const handleOnChange = (e: SyntheticEvent<Element, Event>, values: {component: string | ReactNode, value: string, inputLabel: string}[]) => {
|
|
23
25
|
setValue(values);
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -36,11 +38,9 @@ const Template: Story<FieldTypeOneToManyProps> = (args) => {
|
|
|
36
38
|
|
|
37
39
|
export const Default = Template.bind({});
|
|
38
40
|
Default.args = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
helperText: 'OneToOne helperText',
|
|
43
|
-
}
|
|
41
|
+
placeholder: 'Placeholder Text...',
|
|
42
|
+
label: 'OneToMany label',
|
|
43
|
+
helperText: 'OneToMany helperText',
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
|
|
@@ -1,54 +1,79 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { AutocompleteProps, Popper, styled, TextField, TextFieldProps } from '@mui/material';
|
|
1
|
+
import { ReactNode, useState } from 'react';
|
|
2
|
+
import { AutocompleteProps, FormControl, FormLabel, Popper, styled, TextField, TextFieldProps } from '@mui/material';
|
|
3
3
|
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
|
|
4
4
|
import { ListboxComponent } from '../utils/virtualization';
|
|
5
5
|
|
|
6
6
|
export interface FieldTypeOneToManyProps extends Omit<AutocompleteProps<any, false, false, false>, 'onOpen' | 'renderInput'> {
|
|
7
|
+
label?: string;
|
|
8
|
+
helperText?: string;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
error?: boolean;
|
|
11
|
+
required?: boolean;
|
|
7
12
|
/**
|
|
8
|
-
*
|
|
13
|
+
* Callback to be fired upon opening the dropdown
|
|
9
14
|
*/
|
|
10
|
-
|
|
15
|
+
onOpen: () => Promise<any>;
|
|
11
16
|
/**
|
|
12
|
-
*
|
|
17
|
+
* Structure for option
|
|
13
18
|
*/
|
|
14
|
-
|
|
19
|
+
options: {
|
|
20
|
+
/**
|
|
21
|
+
* Component to be rendered in the dropdown
|
|
22
|
+
*/
|
|
23
|
+
component: ReactNode | string;
|
|
24
|
+
/**
|
|
25
|
+
* Value of option
|
|
26
|
+
*/
|
|
27
|
+
value: string;
|
|
28
|
+
/**
|
|
29
|
+
* Label that should display in the input when selected
|
|
30
|
+
*/
|
|
31
|
+
inputLabel: string;
|
|
32
|
+
}[]
|
|
15
33
|
}
|
|
16
34
|
|
|
17
|
-
const FieldTypeOneToMany = ({
|
|
35
|
+
const FieldTypeOneToMany = ({label, helperText, placeholder, error, onOpen, options, required, ...props }: FieldTypeOneToManyProps) => {
|
|
18
36
|
const [loaded, setLoaded] = useState(false);
|
|
19
|
-
const [loading, setLoading] = useState(
|
|
37
|
+
const [loading, setLoading] = useState(false);
|
|
20
38
|
|
|
21
39
|
const handleOpen = () => {
|
|
22
40
|
if (!loaded && onOpen) {
|
|
23
41
|
onOpen().then(() => {
|
|
24
42
|
setLoading(false);
|
|
25
43
|
});
|
|
26
|
-
|
|
44
|
+
setLoading(true);
|
|
27
45
|
setLoaded(true);
|
|
28
46
|
}
|
|
29
47
|
};
|
|
30
48
|
|
|
31
49
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
<FormControl fullWidth required={required}>
|
|
51
|
+
<FormLabel>{label}</FormLabel>
|
|
52
|
+
<Autocomplete
|
|
53
|
+
onOpen={handleOpen}
|
|
54
|
+
loading={loading}
|
|
55
|
+
fullWidth
|
|
56
|
+
multiple
|
|
57
|
+
disableListWrap
|
|
58
|
+
disableClearable
|
|
59
|
+
disablePortal
|
|
60
|
+
size='small'
|
|
61
|
+
PopperComponent={StyledPopper}
|
|
62
|
+
ListboxComponent={ListboxComponent}
|
|
63
|
+
renderInput={(params) => (
|
|
64
|
+
<TextField
|
|
65
|
+
{...params}
|
|
66
|
+
helperText={helperText}
|
|
67
|
+
error={error}
|
|
68
|
+
placeholder={placeholder}
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
options={loading ? [] : options}
|
|
72
|
+
getOptionLabel={(option) => option.inputLabel}
|
|
73
|
+
renderOption={(props, option) => [props, option.component]}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
</FormControl>
|
|
52
77
|
);
|
|
53
78
|
};
|
|
54
79
|
|
|
@@ -77,7 +77,6 @@ const FieldTypeOneToOne = ({label, helperText, placeholder, error, onOpen, optio
|
|
|
77
77
|
options={loading ? [] : options}
|
|
78
78
|
getOptionLabel={(option) => option.inputLabel}
|
|
79
79
|
renderOption={(props, option) => [props, option.component]}
|
|
80
|
-
// getOptionLabel={(option) => option.label}
|
|
81
80
|
{...props}
|
|
82
81
|
/>
|
|
83
82
|
</FormControl>
|
package/src/index.ts
CHANGED
|
@@ -10,4 +10,4 @@ export { default as FieldTypeDropdown } from './FieldTypeDropdown';
|
|
|
10
10
|
export { default as FieldTypeOneToOne} from './FieldTypeOneToOne';
|
|
11
11
|
export { default as FieldTypeOneToMany} from './FieldTypeOneToMany';
|
|
12
12
|
export { default as CopyButton } from './CopyButton';
|
|
13
|
-
export { default as
|
|
13
|
+
export { default as ConfirmDialog } from './ConfirmDialog';
|
|
@@ -105,21 +105,3 @@ export const ListboxComponent = React.forwardRef<
|
|
|
105
105
|
</div>
|
|
106
106
|
);
|
|
107
107
|
});
|
|
108
|
-
|
|
109
|
-
function random(length: number) {
|
|
110
|
-
const characters =
|
|
111
|
-
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
112
|
-
let result = '';
|
|
113
|
-
|
|
114
|
-
for (let i = 0; i < length; i += 1) {
|
|
115
|
-
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
export const randomOptions = Array.from(new Array(10000))
|
|
123
|
-
.map(() => random(10 + Math.ceil(Math.random() * 20)))
|
|
124
|
-
.sort((a: string, b: string) => a.toUpperCase().localeCompare(b.toUpperCase()));
|
|
125
|
-
|