@luminati-io/uikit 1.4.0 → 1.5.0
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/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/assets/icons/account.svg +1 -8
- package/assets/icons/adapt.svg +1 -4
- package/assets/icons/add.svg +1 -3
- package/assets/icons/add_circle.svg +1 -5
- package/assets/icons/add_funds.svg +1 -7
- package/assets/icons/attach.svg +1 -3
- package/assets/icons/book_help.svg +1 -4
- package/assets/icons/building.svg +1 -3
- package/assets/icons/calendar.svg +1 -13
- package/assets/icons/calendar_graph.svg +1 -14
- package/assets/icons/call.svg +1 -5
- package/assets/icons/camera.svg +1 -5
- package/assets/icons/cart.svg +1 -3
- package/assets/icons/close.svg +1 -3
- package/assets/icons/close_circle.svg +1 -3
- package/assets/icons/close_small.svg +1 -5
- package/assets/icons/collapse.svg +1 -4
- package/assets/icons/column.svg +1 -22
- package/assets/icons/columns.svg +1 -10
- package/assets/icons/connect.svg +1 -3
- package/assets/icons/contact_support.svg +1 -3
- package/assets/icons/copy.svg +1 -3
- package/assets/icons/copy_small.svg +1 -3
- package/assets/icons/customize.svg +1 -6
- package/dist/uikit.umd.js +1 -1
- package/package.json +1 -1
- package/src/icon.js +1 -1
- package/src/icon_button.js +25 -21
- package/src/index.js +6 -0
- package/src/input/index.js +6 -0
- package/src/input/toggle.js +105 -0
- package/src/layout/flex.js +19 -1
- package/src/link/index.js +6 -0
- package/src/link/link.js +84 -0
- package/src/tooltip.js +5 -2
- package/src/utils.js +9 -5
- package/webpack.common.js +2 -4
- package/webpack.shared.js +11 -0
package/package.json
CHANGED
package/src/icon.js
CHANGED
package/src/icon_button.js
CHANGED
|
@@ -5,16 +5,24 @@ import React from 'react';
|
|
|
5
5
|
import styled, {css} from 'styled-components';
|
|
6
6
|
import PT from 'prop-types';
|
|
7
7
|
import utils from './utils';
|
|
8
|
-
import Icon
|
|
8
|
+
import Icon from './icon';
|
|
9
|
+
import Tooltip from './tooltip';
|
|
9
10
|
|
|
10
|
-
const {theme, toPixel, getCommonProps, iconNames} = utils;
|
|
11
|
+
const {theme, toPixel, getCommonProps, iconNames, tooltipPlacements} = utils;
|
|
11
12
|
|
|
12
13
|
const IconButton = React.forwardRef((props, ref)=>{
|
|
13
|
-
const {variant
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const {variant, disabled, icon, tooltip, tooltipPlacement} = props;
|
|
15
|
+
return <Tooltip tooltip={tooltip} placement={tooltipPlacement}>
|
|
16
|
+
<StyledIconButton
|
|
17
|
+
ref={ref}
|
|
18
|
+
{...getCommonProps(props)}
|
|
19
|
+
variant={variant}
|
|
20
|
+
disabled={disabled}
|
|
21
|
+
data-testid="icon_button"
|
|
22
|
+
>
|
|
23
|
+
<Icon name={icon} size="sm" />
|
|
24
|
+
</StyledIconButton>
|
|
25
|
+
</Tooltip>;
|
|
18
26
|
});
|
|
19
27
|
IconButton.displayName = 'IconButton';
|
|
20
28
|
IconButton.defaultProps = {variant: 'icon-button'};
|
|
@@ -22,6 +30,8 @@ IconButton.propTypes = {
|
|
|
22
30
|
variant: PT.oneOf(['icon', 'icon-button']),
|
|
23
31
|
icon: PT.oneOf(iconNames).isRequired,
|
|
24
32
|
disabled: PT.bool,
|
|
33
|
+
tooltip: PT.node,
|
|
34
|
+
tooltipPlacement: PT.oneOf(tooltipPlacements),
|
|
25
35
|
};
|
|
26
36
|
|
|
27
37
|
const StyledIconButton = styled.button`
|
|
@@ -34,36 +44,30 @@ const StyledIconButton = styled.button`
|
|
|
34
44
|
${props=>{
|
|
35
45
|
return css`
|
|
36
46
|
background-color: ${theme.color.white};
|
|
37
|
-
border: ${props
|
|
47
|
+
border: ${props.variant=='icon'?'0 none;':
|
|
38
48
|
`1px solid ${theme.color.gray_6}`};
|
|
39
|
-
${
|
|
40
|
-
color: ${theme.color.gray_9};
|
|
41
|
-
}
|
|
49
|
+
[data-icon] { color: ${theme.color.gray_9}; }
|
|
42
50
|
`;
|
|
43
51
|
}}
|
|
44
52
|
&:hover:not(:disabled) {
|
|
45
53
|
background-color: ${theme.color.gray_2};
|
|
46
|
-
border: ${props=>props
|
|
54
|
+
border: ${props=>props.variant=='icon'?'0 none;':
|
|
47
55
|
`1px solid ${theme.color.gray_8}`};
|
|
48
56
|
}
|
|
49
57
|
&:active:not(:disabled) {
|
|
50
58
|
background-color: ${theme.color.blue_4};
|
|
51
|
-
border: ${props=>props
|
|
59
|
+
border: ${props=>props.variant=='icon'?'0 none;':
|
|
52
60
|
`1px solid ${theme.color.blue_4}`};
|
|
53
|
-
${
|
|
54
|
-
color: ${theme.color.blue_11};
|
|
55
|
-
}
|
|
61
|
+
[data-icon] { color: ${theme.color.blue_11}; }
|
|
56
62
|
}
|
|
57
63
|
&:disabled {
|
|
58
|
-
border: ${props=>props
|
|
64
|
+
border: ${props=>props.variant=='icon'?'0 none;':
|
|
59
65
|
`1px solid ${theme.color.gray_7}`};
|
|
60
|
-
${
|
|
61
|
-
color: ${theme.color.gray_7};
|
|
62
|
-
}
|
|
66
|
+
[data-icon] { color: ${theme.color.gray_7}; }
|
|
63
67
|
cursor: not-allowed;
|
|
64
68
|
}
|
|
65
69
|
${props=>{
|
|
66
|
-
let w = props
|
|
70
|
+
let w = props.variant=='icon'?24:36;
|
|
67
71
|
return css`
|
|
68
72
|
width: ${toPixel(w)};
|
|
69
73
|
height: ${toPixel(w)};
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// LICENSE_CODE ZON
|
|
2
2
|
'use strict'; /*jslint react:true*/
|
|
3
3
|
import {Button} from './button';
|
|
4
|
+
import Input from './input';
|
|
4
5
|
import Typography from './Typography';
|
|
5
6
|
import InfoChip from './infochip';
|
|
6
7
|
import Layout from './layout';
|
|
8
|
+
import {Link} from './link';
|
|
7
9
|
import {Progress, ProgressBar} from './progress';
|
|
8
10
|
import Icon from './icon';
|
|
9
11
|
import IconButton from './icon_button';
|
|
@@ -11,8 +13,10 @@ import Tooltip, {withTooltip} from './tooltip';
|
|
|
11
13
|
|
|
12
14
|
export {
|
|
13
15
|
Layout,
|
|
16
|
+
Link,
|
|
14
17
|
Typography,
|
|
15
18
|
Button,
|
|
19
|
+
Input,
|
|
16
20
|
InfoChip,
|
|
17
21
|
Progress,
|
|
18
22
|
ProgressBar,
|
|
@@ -24,8 +28,10 @@ export {
|
|
|
24
28
|
|
|
25
29
|
const UIKit = {
|
|
26
30
|
Layout,
|
|
31
|
+
Link,
|
|
27
32
|
Typography,
|
|
28
33
|
Button,
|
|
34
|
+
Input,
|
|
29
35
|
InfoChip,
|
|
30
36
|
Progress,
|
|
31
37
|
ProgressBar,
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// LICENSE_CODE ZON
|
|
2
|
+
'use strict'; /*jslint react:true*/
|
|
3
|
+
|
|
4
|
+
import React, {useCallback} from 'react';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import PT from 'prop-types';
|
|
7
|
+
import {theme, toPixel, getCommonProps} from '../utils';
|
|
8
|
+
import typography from '../Typography';
|
|
9
|
+
|
|
10
|
+
const {Label} = typography;
|
|
11
|
+
|
|
12
|
+
const Toggle = props=>{
|
|
13
|
+
const {value, label, disabled, size, onChange} = props;
|
|
14
|
+
const handleChange = useCallback(evt=>{
|
|
15
|
+
const {checked} = evt.target;
|
|
16
|
+
onChange?.(checked, evt);
|
|
17
|
+
}, [onChange]);
|
|
18
|
+
const color = disabled?value?'gray-11':'gray-9':
|
|
19
|
+
value?'gray-11-50':'gray-11';
|
|
20
|
+
return <ToggleLabel {...getCommonProps(props)} $size={size}>
|
|
21
|
+
<ToggleInput $size={size} checked={value} onChange={handleChange}
|
|
22
|
+
disabled={disabled} />
|
|
23
|
+
{label&&<Label variant="sm" tag="span" color={color} no_wrap>
|
|
24
|
+
{label}
|
|
25
|
+
</Label>}
|
|
26
|
+
</ToggleLabel>;
|
|
27
|
+
};
|
|
28
|
+
Toggle.displayName = 'Toggle';
|
|
29
|
+
Toggle.defaultProps = {size: 'sm'};
|
|
30
|
+
Toggle.propTypes = {
|
|
31
|
+
value: PT.bool,
|
|
32
|
+
label: PT.string,
|
|
33
|
+
disabled: PT.bool,
|
|
34
|
+
size: PT.oneOf(['xs', 'sm']),
|
|
35
|
+
onChange: PT.func,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const ToggleLabel = styled.label`
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 4px;
|
|
43
|
+
margin: 0;
|
|
44
|
+
`;
|
|
45
|
+
ToggleLabel.displayName = 'ToggleLabel';
|
|
46
|
+
|
|
47
|
+
const ToggleInput = styled.input.attrs({type: 'checkbox'})`
|
|
48
|
+
&& {
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
width: ${props=>toPixel(props.$size=='xs'?32:44)};
|
|
51
|
+
height: ${props=>toPixel(props.$size=='xs'?16:20)};
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
margin: 0;
|
|
55
|
+
padding: 0 1px;
|
|
56
|
+
vertical-align: top;
|
|
57
|
+
background-color: ${theme.color.gray_5};
|
|
58
|
+
border: 1px solid ${theme.color.gray_7};
|
|
59
|
+
border-radius: 25px;
|
|
60
|
+
outline: none;
|
|
61
|
+
outline-offset: 0;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
appearance: none;
|
|
64
|
+
transition: all 0.3s cubic-bezier(0.2, 0.85, 0.32, 1.2);
|
|
65
|
+
&:hover {
|
|
66
|
+
background-color: ${theme.color.gray_7};
|
|
67
|
+
border-color: ${theme.color.gray_8};
|
|
68
|
+
}
|
|
69
|
+
&:focus {
|
|
70
|
+
outline: none;
|
|
71
|
+
outline-offset: 0;
|
|
72
|
+
background-color: ${theme.color.gray_9};
|
|
73
|
+
border-color: ${theme.color.gray_9};
|
|
74
|
+
}
|
|
75
|
+
&:checked {
|
|
76
|
+
background-color: ${theme.color.blue_11};
|
|
77
|
+
border-color: ${theme.color.blue_11};
|
|
78
|
+
&:hover {
|
|
79
|
+
background-color: ${theme.color.blue_10};
|
|
80
|
+
border-color: ${theme.color.blue_10};
|
|
81
|
+
}
|
|
82
|
+
&:focus {
|
|
83
|
+
background-color: ${theme.color.blue_11};
|
|
84
|
+
border-color: ${theme.color.blue_11};
|
|
85
|
+
outline: 3px solid ${theme.color.blue_5};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
&::after {
|
|
90
|
+
content: "";
|
|
91
|
+
display: block;
|
|
92
|
+
width: ${props=>toPixel(props.$size=='xs'?12:16)};
|
|
93
|
+
height: ${props=>toPixel(props.$size=='xs'?12:16)};
|
|
94
|
+
border-radius: 50%;
|
|
95
|
+
background-color: ${theme.color.white};
|
|
96
|
+
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
|
97
|
+
transition: all 0.3s cubic-bezier(0.2, 0.85, 0.32, 1.2);
|
|
98
|
+
}
|
|
99
|
+
&:checked::after {
|
|
100
|
+
transform: translateX(${props=>toPixel(props.$size=='xs'?15:23)});
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
ToggleInput.displayName = 'ToggleInput';
|
|
104
|
+
|
|
105
|
+
export default Toggle;
|
package/src/layout/flex.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// LICENSE_CODE ZON
|
|
2
2
|
'use strict'; /*jslint react:true*/
|
|
3
3
|
|
|
4
|
+
import React from 'react';
|
|
4
5
|
import styled from 'styled-components';
|
|
6
|
+
import PT from 'prop-types';
|
|
5
7
|
|
|
6
8
|
import Box from './box';
|
|
7
9
|
|
|
8
|
-
const
|
|
10
|
+
const StyledFlex = styled(Box)`
|
|
9
11
|
display: ${props=>props.inline ? 'inline-flex' : 'flex'};
|
|
10
12
|
align-items: ${props=>props.align_items};
|
|
11
13
|
align-content: ${props=>props.align_content};
|
|
@@ -17,7 +19,23 @@ const Flex = styled(Box)`
|
|
|
17
19
|
row-gap: ${props=>props.row_gap};
|
|
18
20
|
column-gap: ${props=>props.column_gap};
|
|
19
21
|
`;
|
|
22
|
+
const Flex = props=>{
|
|
23
|
+
return <StyledFlex {...props}/>;
|
|
24
|
+
};
|
|
20
25
|
Flex.displayName = 'Flex';
|
|
21
26
|
Flex.defaultProps = {flex_direction: 'row'};
|
|
27
|
+
Flex.propTypes = {
|
|
28
|
+
inline: PT.bool,
|
|
29
|
+
align_items: PT.oneOf(['flex-start', 'center']),
|
|
30
|
+
align_content: PT.string,
|
|
31
|
+
justify_items: PT.string,
|
|
32
|
+
justify_content: PT.string,
|
|
33
|
+
flex_wrap: PT.string,
|
|
34
|
+
flex_direction: PT.oneOf(['row', 'row-reverse', 'column',
|
|
35
|
+
'column-reverse']),
|
|
36
|
+
gap: PT.string,
|
|
37
|
+
row_gap: PT.string,
|
|
38
|
+
column_gap: PT.string,
|
|
39
|
+
};
|
|
22
40
|
|
|
23
41
|
export default Flex;
|
package/src/link/link.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// LICENSE_CODE ZON
|
|
2
|
+
'use strict'; /*jslint react:true*/
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import PT from 'prop-types';
|
|
7
|
+
import {
|
|
8
|
+
theme,
|
|
9
|
+
iconNames,
|
|
10
|
+
toPixel,
|
|
11
|
+
getCommonProps,
|
|
12
|
+
getIconProps,
|
|
13
|
+
getLinkProps,
|
|
14
|
+
} from '../utils';
|
|
15
|
+
import typography from '../Typography';
|
|
16
|
+
import Icon from '../icon';
|
|
17
|
+
|
|
18
|
+
const {Typography, Label} = typography;
|
|
19
|
+
|
|
20
|
+
const Link = React.forwardRef((props, ref)=>{
|
|
21
|
+
const {text, variant, size, icon} = props;
|
|
22
|
+
const {isLeft, isRight, ...iconProps} = getIconProps('Link', props);
|
|
23
|
+
const labelVariant = {lg: 'lg', sm: 'xs'}[size]||'base';
|
|
24
|
+
return <StyledLink
|
|
25
|
+
ref={ref}
|
|
26
|
+
{...getCommonProps(props)}
|
|
27
|
+
{...getLinkProps(props)}
|
|
28
|
+
variant={variant}
|
|
29
|
+
size={size}
|
|
30
|
+
noIcon={!icon}
|
|
31
|
+
>
|
|
32
|
+
{isLeft&&<Icon {...iconProps}/>}
|
|
33
|
+
<Label variant={labelVariant} no_wrap>{text}</Label>
|
|
34
|
+
{isRight&&<Icon {...iconProps}/>}
|
|
35
|
+
</StyledLink>;
|
|
36
|
+
});
|
|
37
|
+
Link.displayName = 'Link';
|
|
38
|
+
Link.defaultProps = {
|
|
39
|
+
variant: 'primary',
|
|
40
|
+
size: 'md',
|
|
41
|
+
iconPlacement: 'left',
|
|
42
|
+
};
|
|
43
|
+
Link.propTypes = {
|
|
44
|
+
text: PT.string.isRequired,
|
|
45
|
+
variant: PT.oneOf(['primary', 'secondary']),
|
|
46
|
+
size: PT.oneOf(['sm', 'md', 'lg']),
|
|
47
|
+
icon: PT.oneOf(iconNames),
|
|
48
|
+
iconPlacement: PT.oneOf(['left', 'right']),
|
|
49
|
+
href: PT.string,
|
|
50
|
+
newTab: PT.bool,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const StyledLink = styled.a`
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
text-decoration: none;
|
|
58
|
+
background: 0 none;
|
|
59
|
+
border: 0 none;
|
|
60
|
+
padding: 0;
|
|
61
|
+
gap: ${props=>toPixel(props.size=='lg'?8:4)};
|
|
62
|
+
${Typography} {
|
|
63
|
+
color: ${props=>props.variant=='primary'?
|
|
64
|
+
theme.color.blue_11:theme.color.gray_11_50};
|
|
65
|
+
text-decoration: ${props=>props.variant=='secondary'&&
|
|
66
|
+
props.noIcon?'underline':'none'};
|
|
67
|
+
}
|
|
68
|
+
[data-icon] {
|
|
69
|
+
color: ${props=>props.variant=='primary'?
|
|
70
|
+
theme.color.blue_11:theme.color.gray_11_50};
|
|
71
|
+
}
|
|
72
|
+
&:hover {
|
|
73
|
+
${Typography} {
|
|
74
|
+
color: ${theme.color.blue_11};
|
|
75
|
+
text-decoration: ${props=>props.variant=='secondary'&&
|
|
76
|
+
props.noIcon?'underline':'none'};
|
|
77
|
+
}
|
|
78
|
+
[data-icon] { color: ${theme.color.blue_11}; }
|
|
79
|
+
}
|
|
80
|
+
&:hover, :visited, :focus { text-decoration: none; }
|
|
81
|
+
`;
|
|
82
|
+
StyledLink.displayName = 'StyledLink';
|
|
83
|
+
|
|
84
|
+
export default Link;
|
package/src/tooltip.js
CHANGED
|
@@ -5,7 +5,7 @@ import React, {useCallback, useState} from 'react';
|
|
|
5
5
|
import {usePopper} from 'react-popper';
|
|
6
6
|
import styled from 'styled-components';
|
|
7
7
|
import PT from 'prop-types';
|
|
8
|
-
import {theme} from './utils';
|
|
8
|
+
import {theme, tooltipPlacements} from './utils';
|
|
9
9
|
import typography from './Typography';
|
|
10
10
|
|
|
11
11
|
const {Label} = typography;
|
|
@@ -26,6 +26,8 @@ const Tooltip = props=>{
|
|
|
26
26
|
{name: 'offset', options: {offset: [0, 8]}},
|
|
27
27
|
],
|
|
28
28
|
});
|
|
29
|
+
if (!tooltip)
|
|
30
|
+
return children;
|
|
29
31
|
return <>
|
|
30
32
|
<StyledTooltipReference ref={setReferenceElement}
|
|
31
33
|
onMouseEnter={show} onMouseLeave={hide}>
|
|
@@ -39,9 +41,10 @@ const Tooltip = props=>{
|
|
|
39
41
|
</StyledTooltipBody>
|
|
40
42
|
</>;
|
|
41
43
|
};
|
|
44
|
+
Tooltip.defaultProps = {placement: 'top'};
|
|
42
45
|
Tooltip.propTypes = {
|
|
43
46
|
tooltip: PT.node,
|
|
44
|
-
placement: PT.oneOf(
|
|
47
|
+
placement: PT.oneOf(tooltipPlacements),
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
export const withTooltip = (Comp, tooltipProps)=>function WithTooltip(props){
|
package/src/utils.js
CHANGED
|
@@ -149,10 +149,8 @@ export const getIconProps = (comp, opt)=>{
|
|
|
149
149
|
};
|
|
150
150
|
|
|
151
151
|
export const getLinkProps = opt=>{
|
|
152
|
-
const {as,
|
|
153
|
-
let p = {as,
|
|
154
|
-
if (href)
|
|
155
|
-
p = {...p, as: 'a', href};
|
|
152
|
+
const {as, href, newTab} = opt;
|
|
153
|
+
let p = {as, href};
|
|
156
154
|
if (newTab)
|
|
157
155
|
p = {...p, target: '_blank', rel: 'noopener noreferrer'};
|
|
158
156
|
return p;
|
|
@@ -262,5 +260,11 @@ export const toButtonSize = size=>{
|
|
|
262
260
|
return css`height: ${toPixel(w)};`;
|
|
263
261
|
};
|
|
264
262
|
|
|
263
|
+
export const tooltipPlacements = (()=>{
|
|
264
|
+
const positions = ['auto', 'top', 'right', 'bottom', 'left'];
|
|
265
|
+
const variants = ['', '-start', '-end'];
|
|
266
|
+
return positions.flatMap(p=>variants.map(v=>p+v));
|
|
267
|
+
})();
|
|
268
|
+
|
|
265
269
|
export default {theme, toPixel, getCommonProps, getTypographyProps, fromTheme,
|
|
266
|
-
colorNames, iconNames};
|
|
270
|
+
colorNames, iconNames, tooltipPlacements};
|
package/webpack.common.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict'; /*jslint node:true*/
|
|
3
3
|
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const {svgRule} = require('./webpack.shared.js');
|
|
5
6
|
|
|
6
7
|
const alias = {
|
|
7
8
|
'@icons': path.resolve(__dirname, 'assets/icons'),
|
|
@@ -15,10 +16,7 @@ const common = {
|
|
|
15
16
|
exclude: /node_modules/,
|
|
16
17
|
use: ['babel-loader'],
|
|
17
18
|
},
|
|
18
|
-
|
|
19
|
-
test: /\.svg$/,
|
|
20
|
-
loader: 'svg-sprite-loader',
|
|
21
|
-
}
|
|
19
|
+
svgRule,
|
|
22
20
|
]
|
|
23
21
|
},
|
|
24
22
|
resolve: {alias},
|