@groupeactual/ui-kit 0.4.33 → 0.4.34
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/package.json +7 -3
- package/src/DesignSystemProvider.tsx +61 -0
- package/src/components/Accordion/Accordion.tsx +72 -0
- package/src/components/Accordion/index.ts +1 -0
- package/src/components/Button/Button.tsx +18 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Chip/Chip.tsx +125 -0
- package/src/components/Chip/index.ts +1 -0
- package/src/components/EmbbededNotification/EmbeddedNotification.tsx +95 -0
- package/src/components/EmbbededNotification/index.ts +1 -0
- package/src/components/Form/Checkbox/Checkbox.tsx +86 -0
- package/src/components/Form/Checkbox/index.ts +1 -0
- package/src/components/Form/MultiSelect/MultiSelect.tsx +184 -0
- package/src/components/Form/MultiSelect/index.ts +1 -0
- package/src/components/Form/Select/Select.tsx +154 -0
- package/src/components/Form/Select/index.ts +1 -0
- package/src/components/Form/TextField/TextField.tsx +98 -0
- package/src/components/Form/TextField/index.ts +1 -0
- package/src/components/Icon/Icon.tsx +126 -0
- package/src/components/Icon/index.ts +1 -0
- package/src/components/Link/Link.tsx +11 -0
- package/src/components/Link/index.ts +1 -0
- package/src/components/Pagination/Pagination.tsx +116 -0
- package/src/components/Pagination/index.ts +1 -0
- package/src/components/Text/Text.tsx +26 -0
- package/src/components/Text/index.ts +1 -0
- package/src/components/Tooltip/Tooltip.tsx +33 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/components/index.ts +13 -0
- package/src/index.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groupeactual/ui-kit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.34",
|
|
4
4
|
"description": "A simple template for a custom React component library",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@babel/core": "^7.20.5",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"main": "dist/cjs/index.js",
|
|
33
33
|
"module": "dist/esm/index.js",
|
|
34
34
|
"files": [
|
|
35
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"src"
|
|
36
37
|
],
|
|
37
38
|
"types": "dist/index.d.ts",
|
|
38
39
|
"dependencies": {
|
|
@@ -47,7 +48,10 @@
|
|
|
47
48
|
"@fortawesome/pro-solid-svg-icons": "^6.3.0",
|
|
48
49
|
"@mui/base": "^5.0.0-alpha.108",
|
|
49
50
|
"@mui/material": "^5.10.16",
|
|
50
|
-
"@mui/system": "^5.10.16"
|
|
51
|
+
"@mui/system": "^5.10.16",
|
|
52
|
+
"@emotion/is-prop-valid": "^1.2.0",
|
|
53
|
+
"@emotion/react": "^11.10.5",
|
|
54
|
+
"@emotion/styled": "^11.10.5"
|
|
51
55
|
},
|
|
52
56
|
"scripts": {
|
|
53
57
|
"build": "rollup -c",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
PropsWithChildren,
|
|
3
|
+
useState,
|
|
4
|
+
createContext,
|
|
5
|
+
useContext
|
|
6
|
+
} from 'react';
|
|
7
|
+
import { useMaterialThemeTokens } from '@groupeactual/design-tokens';
|
|
8
|
+
import { createTheme, ThemeProvider } from '@mui/material';
|
|
9
|
+
|
|
10
|
+
// Deux themes pour le moment :
|
|
11
|
+
// Default = Theme backoffice Material
|
|
12
|
+
// Ep = Espace personnel
|
|
13
|
+
type Theme = 'Default' | 'Ep';
|
|
14
|
+
|
|
15
|
+
export interface DesignSystemContextValues {
|
|
16
|
+
isDarkTheme: boolean;
|
|
17
|
+
themeName: Theme;
|
|
18
|
+
toggleDarkTheme: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const DesignSystemContext = createContext<DesignSystemContextValues>({
|
|
22
|
+
isDarkTheme: false,
|
|
23
|
+
themeName: 'Default',
|
|
24
|
+
toggleDarkTheme: () => {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
name?: Theme;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const MaterialThemeProvider = ({ children }: PropsWithChildren<unknown>) => {
|
|
34
|
+
const { themeName } =
|
|
35
|
+
useContext<DesignSystemContextValues>(DesignSystemContext);
|
|
36
|
+
const { muiTokens } = useMaterialThemeTokens(themeName);
|
|
37
|
+
const theme = createTheme(muiTokens);
|
|
38
|
+
|
|
39
|
+
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const DesignSystemProvider = ({
|
|
43
|
+
children,
|
|
44
|
+
name: themeName = 'Default'
|
|
45
|
+
}: PropsWithChildren<Props>) => {
|
|
46
|
+
const [isDarkTheme, setIsDarkTheme] = useState(false);
|
|
47
|
+
|
|
48
|
+
const toggleDarkTheme = (): void => {
|
|
49
|
+
setIsDarkTheme(!isDarkTheme);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<DesignSystemContext.Provider
|
|
54
|
+
value={{ isDarkTheme, themeName, toggleDarkTheme }}
|
|
55
|
+
>
|
|
56
|
+
<MaterialThemeProvider>{children}</MaterialThemeProvider>
|
|
57
|
+
</DesignSystemContext.Provider>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default DesignSystemProvider;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
MouseEventHandler,
|
|
3
|
+
ReactNode,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState
|
|
6
|
+
} from 'react';
|
|
7
|
+
import AccordionMui, { AccordionProps } from '@mui/material/Accordion';
|
|
8
|
+
import AccordionSummary from '@mui/material/AccordionSummary';
|
|
9
|
+
import AccordionDetails from '@mui/material/AccordionDetails';
|
|
10
|
+
|
|
11
|
+
interface Props extends AccordionProps {
|
|
12
|
+
icon: ReactNode;
|
|
13
|
+
title?: string;
|
|
14
|
+
summaryHeight?: number;
|
|
15
|
+
expanded?: boolean;
|
|
16
|
+
onClick?: MouseEventHandler;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Accordion = ({
|
|
20
|
+
icon,
|
|
21
|
+
title,
|
|
22
|
+
summaryHeight,
|
|
23
|
+
expanded = false,
|
|
24
|
+
onClick,
|
|
25
|
+
children,
|
|
26
|
+
...props
|
|
27
|
+
}: Props) => {
|
|
28
|
+
const [internalExpanded, setInternalExpanded] = useState(expanded);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (expanded !== internalExpanded) {
|
|
32
|
+
setInternalExpanded(expanded);
|
|
33
|
+
}
|
|
34
|
+
}, [expanded]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<AccordionMui
|
|
38
|
+
sx={{ border: `1px solid`, borderColor: 'greyLightDefaultBorder' }}
|
|
39
|
+
expanded={internalExpanded}
|
|
40
|
+
onClick={(e) => {
|
|
41
|
+
if (!props.disabled) {
|
|
42
|
+
setInternalExpanded(!internalExpanded);
|
|
43
|
+
onClick && onClick(e);
|
|
44
|
+
}
|
|
45
|
+
}}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
<AccordionSummary
|
|
49
|
+
expandIcon={icon}
|
|
50
|
+
sx={{
|
|
51
|
+
fontWeight: 500,
|
|
52
|
+
fontSize: 18,
|
|
53
|
+
lineHeight: 21,
|
|
54
|
+
height: summaryHeight || 60
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
{title}
|
|
58
|
+
</AccordionSummary>
|
|
59
|
+
<AccordionDetails
|
|
60
|
+
sx={{
|
|
61
|
+
backgroundColor: 'greyXLight',
|
|
62
|
+
borderTop: '1px solid',
|
|
63
|
+
borderColor: 'greyLightDefaultBorder'
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</AccordionDetails>
|
|
68
|
+
</AccordionMui>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default Accordion;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Accordion';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ButtonProps } from '@mui/material/Button';
|
|
3
|
+
import { Button as ButtonMUI } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<ButtonProps, 'variant' | 'children'> {
|
|
6
|
+
variant?: 'primary' | 'secondary';
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Button = ({ variant, children, ...props }: Props) => {
|
|
11
|
+
return (
|
|
12
|
+
<ButtonMUI variant={variant as 'text'} {...props}>
|
|
13
|
+
{children}
|
|
14
|
+
</ButtonMUI>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default Button;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Button';
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { ChipProps as ChipPropsMUI } from '@mui/material/Chip';
|
|
3
|
+
import { Chip as ChipMUI, Tooltip } from '@mui/material';
|
|
4
|
+
import { useTheme } from '@mui/system';
|
|
5
|
+
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
6
|
+
|
|
7
|
+
import IconProvider from '../Icon';
|
|
8
|
+
|
|
9
|
+
interface Props
|
|
10
|
+
extends Omit<
|
|
11
|
+
ChipPropsMUI,
|
|
12
|
+
'suffix' | 'prefix' | 'onDelete' | 'onDeleteIcon'
|
|
13
|
+
> {
|
|
14
|
+
label: string;
|
|
15
|
+
size?: 'small';
|
|
16
|
+
prefixIcon?: IconDefinition;
|
|
17
|
+
suffixIcon?: IconDefinition;
|
|
18
|
+
suffixAction?: (e: any) => void;
|
|
19
|
+
suffixTooltip?: string;
|
|
20
|
+
tooltip?: string;
|
|
21
|
+
maxWidth?: number;
|
|
22
|
+
maxLength?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const Chip = ({
|
|
26
|
+
label,
|
|
27
|
+
prefixIcon,
|
|
28
|
+
suffixIcon,
|
|
29
|
+
suffixTooltip,
|
|
30
|
+
suffixAction,
|
|
31
|
+
tooltip,
|
|
32
|
+
size = 'small',
|
|
33
|
+
maxWidth = 300,
|
|
34
|
+
maxLength = 17,
|
|
35
|
+
...props
|
|
36
|
+
}: Props) => {
|
|
37
|
+
const theme = useTheme();
|
|
38
|
+
const [currentLabel, setCurrentLabel] = useState<string>(label);
|
|
39
|
+
const [isOpen, setIsOpen] = useState<boolean>(false);
|
|
40
|
+
|
|
41
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
42
|
+
|
|
43
|
+
let backgroundColor: string =
|
|
44
|
+
props.color && theme.palette[props.color]
|
|
45
|
+
? typeof theme.palette[props.color] === 'object'
|
|
46
|
+
? theme.palette[props.color]['main']
|
|
47
|
+
: theme.palette[props.color]
|
|
48
|
+
: props.color ?? 'white';
|
|
49
|
+
|
|
50
|
+
if (props.variant === 'filled') {
|
|
51
|
+
// opacity 0.08
|
|
52
|
+
backgroundColor = `${backgroundColor}14`;
|
|
53
|
+
} else {
|
|
54
|
+
backgroundColor = 'white';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const emptyOnDelete = () => {
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// use maxwidth props to set the max width of the chip based on the column width
|
|
62
|
+
// if a chip is in a table, the width should match the width of the column.
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (
|
|
65
|
+
ref?.current?.offsetWidth &&
|
|
66
|
+
ref?.current?.offsetWidth > maxWidth &&
|
|
67
|
+
`${label.substring(0, maxLength)}...` !== currentLabel
|
|
68
|
+
) {
|
|
69
|
+
setCurrentLabel(`${label.substring(0, maxLength)}...`);
|
|
70
|
+
} else if (
|
|
71
|
+
ref?.current?.offsetWidth &&
|
|
72
|
+
ref?.current?.offsetWidth <= maxWidth &&
|
|
73
|
+
label === `${label.substring(0, maxLength)}...`
|
|
74
|
+
) {
|
|
75
|
+
setCurrentLabel(label);
|
|
76
|
+
}
|
|
77
|
+
}, [ref?.current?.offsetWidth]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Tooltip title={tooltip} placement="right-start">
|
|
81
|
+
<Tooltip
|
|
82
|
+
title={suffixTooltip}
|
|
83
|
+
open={isOpen}
|
|
84
|
+
placement="bottom-end"
|
|
85
|
+
sx={{ cursor: suffixAction ? 'pointer !important' : 'default' }}
|
|
86
|
+
>
|
|
87
|
+
<ChipMUI
|
|
88
|
+
label={currentLabel}
|
|
89
|
+
ref={ref}
|
|
90
|
+
size={size}
|
|
91
|
+
sx={{
|
|
92
|
+
backgroundColor: backgroundColor,
|
|
93
|
+
'&.MuiChip-colorSecondary:hover': suffixIcon &&
|
|
94
|
+
suffixAction && {
|
|
95
|
+
backgroundColor: '#004f88 !important'
|
|
96
|
+
},
|
|
97
|
+
'&.MuiChip-deleteIconColorPrimary:hover': suffixIcon &&
|
|
98
|
+
suffixAction && {
|
|
99
|
+
backgroundColor: '#004f88 !important'
|
|
100
|
+
}
|
|
101
|
+
}}
|
|
102
|
+
avatar={
|
|
103
|
+
prefixIcon ? <IconProvider icon={prefixIcon} size="sm" /> : <></>
|
|
104
|
+
}
|
|
105
|
+
deleteIcon={
|
|
106
|
+
suffixIcon ? (
|
|
107
|
+
<IconProvider
|
|
108
|
+
icon={suffixIcon}
|
|
109
|
+
onMouseEnter={() => suffixTooltip && setIsOpen(true)}
|
|
110
|
+
onMouseLeave={() => suffixTooltip && setIsOpen(false)}
|
|
111
|
+
size="sm"
|
|
112
|
+
/>
|
|
113
|
+
) : (
|
|
114
|
+
<></>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
onDelete={suffixAction ?? emptyOnDelete}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
</Tooltip>
|
|
121
|
+
</Tooltip>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default Chip;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Chip';
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { Box, BoxProps } from '@mui/material';
|
|
3
|
+
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
4
|
+
import {
|
|
5
|
+
faCircleXmark,
|
|
6
|
+
faCircleInfo,
|
|
7
|
+
faCircleCheck,
|
|
8
|
+
faCircleExclamation
|
|
9
|
+
} from '@fortawesome/pro-solid-svg-icons';
|
|
10
|
+
|
|
11
|
+
import Text from '../Text';
|
|
12
|
+
import Icon from '../Icon/Icon';
|
|
13
|
+
|
|
14
|
+
interface Props extends BoxProps {
|
|
15
|
+
variant: 'warning' | 'error' | 'success' | 'infos';
|
|
16
|
+
title: string;
|
|
17
|
+
text?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const EmbeddedNotification = ({
|
|
21
|
+
title,
|
|
22
|
+
text,
|
|
23
|
+
variant = 'infos',
|
|
24
|
+
...props
|
|
25
|
+
}: Props) => {
|
|
26
|
+
interface EmbeddedNotifVariant {
|
|
27
|
+
color: string;
|
|
28
|
+
icon: IconDefinition;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const variantNotification: Record<string, EmbeddedNotifVariant> = {
|
|
32
|
+
warning: {
|
|
33
|
+
color: 'orangeWarning',
|
|
34
|
+
icon: faCircleExclamation
|
|
35
|
+
},
|
|
36
|
+
error: {
|
|
37
|
+
color: 'redError',
|
|
38
|
+
icon: faCircleXmark
|
|
39
|
+
},
|
|
40
|
+
success: {
|
|
41
|
+
color: 'greenSuccess',
|
|
42
|
+
icon: faCircleCheck
|
|
43
|
+
},
|
|
44
|
+
infos: {
|
|
45
|
+
color: 'blueInfo',
|
|
46
|
+
icon: faCircleInfo
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Box
|
|
52
|
+
border="1px solid"
|
|
53
|
+
borderColor={variantNotification[variant].color}
|
|
54
|
+
mt="4px"
|
|
55
|
+
p="4px"
|
|
56
|
+
borderRadius="5px"
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
<Box display="flex" alignItems="center" pb="2px">
|
|
60
|
+
<Box sx={{ pl: '12px', pr: '16px', display: 'flex' }}>
|
|
61
|
+
<Icon
|
|
62
|
+
icon={variantNotification[variant].icon}
|
|
63
|
+
color={variantNotification[variant].color}
|
|
64
|
+
/>
|
|
65
|
+
</Box>
|
|
66
|
+
<Box>
|
|
67
|
+
<Box>
|
|
68
|
+
<Text
|
|
69
|
+
align="left"
|
|
70
|
+
variant="body1Bold"
|
|
71
|
+
color={variantNotification[variant].color}
|
|
72
|
+
display="inline-block"
|
|
73
|
+
>
|
|
74
|
+
{title}
|
|
75
|
+
</Text>
|
|
76
|
+
</Box>
|
|
77
|
+
{text && (
|
|
78
|
+
<Box>
|
|
79
|
+
<Text
|
|
80
|
+
align="left"
|
|
81
|
+
variant="body1Regular"
|
|
82
|
+
color="greyDark"
|
|
83
|
+
display="inline-block"
|
|
84
|
+
>
|
|
85
|
+
{text}
|
|
86
|
+
</Text>
|
|
87
|
+
</Box>
|
|
88
|
+
)}
|
|
89
|
+
</Box>
|
|
90
|
+
</Box>
|
|
91
|
+
</Box>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default EmbeddedNotification;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './EmbeddedNotification';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import CheckboxMUI, { CheckboxProps } from '@mui/material/Checkbox';
|
|
4
|
+
import FormControl from '@mui/material/FormControl';
|
|
5
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
6
|
+
import Typography from '@mui/material/Typography';
|
|
7
|
+
import Box from '@mui/system/Box';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
name: string;
|
|
11
|
+
value: boolean;
|
|
12
|
+
label: string;
|
|
13
|
+
onChange?: (
|
|
14
|
+
field: string,
|
|
15
|
+
value: any,
|
|
16
|
+
shouldValidate?: boolean | undefined
|
|
17
|
+
) => void;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const Checkbox = ({
|
|
22
|
+
name,
|
|
23
|
+
value,
|
|
24
|
+
error,
|
|
25
|
+
label,
|
|
26
|
+
onChange,
|
|
27
|
+
...props
|
|
28
|
+
}: CheckboxProps & Props): JSX.Element => {
|
|
29
|
+
const [internalValue, setInternalValue] = useState(value);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (value !== internalValue) {
|
|
33
|
+
setInternalValue(value);
|
|
34
|
+
}
|
|
35
|
+
}, [value]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<FormControl fullWidth>
|
|
39
|
+
<FormControlLabel
|
|
40
|
+
control={
|
|
41
|
+
<Box>
|
|
42
|
+
<CheckboxMUI
|
|
43
|
+
name={name}
|
|
44
|
+
sx={{ marginTop: '-2px' }}
|
|
45
|
+
checked={internalValue}
|
|
46
|
+
color="primary"
|
|
47
|
+
onChange={(e) => {
|
|
48
|
+
setInternalValue(e.target.checked);
|
|
49
|
+
onChange && onChange(name, e.target.checked, true);
|
|
50
|
+
}}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
</Box>
|
|
54
|
+
}
|
|
55
|
+
label={
|
|
56
|
+
<Typography
|
|
57
|
+
component="span"
|
|
58
|
+
sx={{
|
|
59
|
+
fontSize: '14px',
|
|
60
|
+
marginLeft: '-3px',
|
|
61
|
+
marginTop: '2px',
|
|
62
|
+
fontWeight: 400,
|
|
63
|
+
color: error ? '#B80025' : '#000'
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
{label}
|
|
67
|
+
</Typography>
|
|
68
|
+
}
|
|
69
|
+
/>
|
|
70
|
+
{error && (
|
|
71
|
+
<Typography
|
|
72
|
+
sx={{
|
|
73
|
+
marginTop: -0.5,
|
|
74
|
+
color: '#B80025',
|
|
75
|
+
fontWeight: 400,
|
|
76
|
+
fontSize: '10px'
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{error}
|
|
80
|
+
</Typography>
|
|
81
|
+
)}
|
|
82
|
+
</FormControl>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default Checkbox;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Checkbox';
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import { FormHelperText, InputLabel, MenuItem } from '@mui/material';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import FormControl from '@mui/material/FormControl';
|
|
5
|
+
import MuiSelect, {
|
|
6
|
+
SelectChangeEvent,
|
|
7
|
+
SelectProps
|
|
8
|
+
} from '@mui/material/Select';
|
|
9
|
+
import {
|
|
10
|
+
faChevronDown,
|
|
11
|
+
faCheck,
|
|
12
|
+
faTimesCircle
|
|
13
|
+
} from '@fortawesome/pro-solid-svg-icons';
|
|
14
|
+
import Icon from '../../Icon';
|
|
15
|
+
import Chip from '../../Chip/Chip';
|
|
16
|
+
|
|
17
|
+
interface Props<T>
|
|
18
|
+
extends Omit<
|
|
19
|
+
SelectProps<T[]>,
|
|
20
|
+
'value' | 'onChange' | 'defaultValue' | 'color'
|
|
21
|
+
> {
|
|
22
|
+
label: string;
|
|
23
|
+
options: T[];
|
|
24
|
+
helperText?: string;
|
|
25
|
+
color?: 'success';
|
|
26
|
+
getOptionLabel: (option: T) => string;
|
|
27
|
+
onChange: (value: T[]) => void;
|
|
28
|
+
defaultValue?: T[];
|
|
29
|
+
width?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const MultiSelect = <T extends {}>({
|
|
33
|
+
label,
|
|
34
|
+
options,
|
|
35
|
+
color,
|
|
36
|
+
error,
|
|
37
|
+
placeholder,
|
|
38
|
+
width = 200,
|
|
39
|
+
defaultValue = [],
|
|
40
|
+
getOptionLabel,
|
|
41
|
+
helperText,
|
|
42
|
+
onChange,
|
|
43
|
+
onBlur,
|
|
44
|
+
...props
|
|
45
|
+
}: Props<T>) => {
|
|
46
|
+
const [values, setValues] = useState(defaultValue);
|
|
47
|
+
|
|
48
|
+
const handleChange = (event: SelectChangeEvent<typeof options>) => {
|
|
49
|
+
const newValue = event.target.value as T[];
|
|
50
|
+
onChange(newValue);
|
|
51
|
+
setValues(newValue);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleDeleteChip = (chipValue: T) => {
|
|
55
|
+
const newValue = values?.filter((val) => val !== chipValue);
|
|
56
|
+
setValues(newValue);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const getFormControlClassName = useMemo(() => {
|
|
60
|
+
const classNames: string[] = ['DsSelect'];
|
|
61
|
+
if (props.disabled) classNames.push('Mui-disabled');
|
|
62
|
+
if (values?.length > 0) classNames.push('Mui-filled');
|
|
63
|
+
|
|
64
|
+
return classNames;
|
|
65
|
+
}, [values, props.disabled]);
|
|
66
|
+
|
|
67
|
+
const getInputLabelClassName = useMemo(() => {
|
|
68
|
+
const classNames: string[] = [];
|
|
69
|
+
if (error) classNames.push('Mui-error');
|
|
70
|
+
if (props.disabled) classNames.push('Mui-disabled');
|
|
71
|
+
|
|
72
|
+
return classNames;
|
|
73
|
+
}, [error, props.disabled]);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Box sx={{ width }}>
|
|
77
|
+
<FormControl
|
|
78
|
+
id={label === placeholder ? 'select-mui' : 'select-ds'}
|
|
79
|
+
fullWidth
|
|
80
|
+
color={color}
|
|
81
|
+
className={getFormControlClassName.join(' ')}
|
|
82
|
+
sx={{
|
|
83
|
+
'.MuiOutlinedInput-input': {
|
|
84
|
+
marginTop: values?.length > 0 ? '-4px' : '2px'
|
|
85
|
+
}
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<InputLabel className={getInputLabelClassName.join(' ')}>
|
|
89
|
+
{label}
|
|
90
|
+
</InputLabel>
|
|
91
|
+
<MuiSelect
|
|
92
|
+
sx={{
|
|
93
|
+
color: color + '! important',
|
|
94
|
+
'& .MuiSelect-select .notranslate::after': placeholder
|
|
95
|
+
? {
|
|
96
|
+
content: `"${placeholder}"`,
|
|
97
|
+
opacity:
|
|
98
|
+
label === placeholder ? '0 !important' : '1 !important'
|
|
99
|
+
}
|
|
100
|
+
: {}
|
|
101
|
+
}}
|
|
102
|
+
multiple
|
|
103
|
+
label={label}
|
|
104
|
+
placeholder={label === placeholder ? '' : placeholder}
|
|
105
|
+
notched={
|
|
106
|
+
/* eslint-disable-next-line no-undefined */
|
|
107
|
+
label === placeholder ? undefined : true
|
|
108
|
+
}
|
|
109
|
+
value={values}
|
|
110
|
+
error={!!error}
|
|
111
|
+
onChange={handleChange}
|
|
112
|
+
onBlur={onBlur}
|
|
113
|
+
renderValue={(selected) => (
|
|
114
|
+
<Box sx={{ display: 'flex', flexWrap: 'nowrap', gap: 0.5 }}>
|
|
115
|
+
{selected?.map((selectedOption: T, i) => (
|
|
116
|
+
<Chip
|
|
117
|
+
key={i}
|
|
118
|
+
disabled={props.disabled}
|
|
119
|
+
variant="filled"
|
|
120
|
+
color="default"
|
|
121
|
+
label={getOptionLabel(selectedOption)}
|
|
122
|
+
suffixIcon={faTimesCircle}
|
|
123
|
+
suffixAction={() =>
|
|
124
|
+
!props.disabled && handleDeleteChip(selectedOption)
|
|
125
|
+
}
|
|
126
|
+
onMouseDown={(event) => {
|
|
127
|
+
event.stopPropagation();
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
))}
|
|
131
|
+
</Box>
|
|
132
|
+
)}
|
|
133
|
+
IconComponent={({ className }) => (
|
|
134
|
+
<Icon
|
|
135
|
+
className={
|
|
136
|
+
props.disabled ? 'Mui-disabled SelectIcon' : 'SelectIcon'
|
|
137
|
+
}
|
|
138
|
+
icon={color === 'success' ? faCheck : faChevronDown}
|
|
139
|
+
size={color === 'success' ? 'md' : 'sm'}
|
|
140
|
+
sx={{
|
|
141
|
+
marginRight: '12px',
|
|
142
|
+
marginTop: color === 'success' ? '2px' : '0px',
|
|
143
|
+
transform:
|
|
144
|
+
className.toString().includes('iconOpen') &&
|
|
145
|
+
color !== 'success'
|
|
146
|
+
? 'rotate(180deg)'
|
|
147
|
+
: 'none'
|
|
148
|
+
}}
|
|
149
|
+
/>
|
|
150
|
+
)}
|
|
151
|
+
MenuProps={{
|
|
152
|
+
PaperProps: {
|
|
153
|
+
style: {
|
|
154
|
+
width
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}}
|
|
158
|
+
{...props}
|
|
159
|
+
>
|
|
160
|
+
{options?.map((option, i) => (
|
|
161
|
+
<MenuItem
|
|
162
|
+
key={i}
|
|
163
|
+
value={option as any}
|
|
164
|
+
sx={{
|
|
165
|
+
fontWeight: values.indexOf(option) === -1 ? 400 : 500,
|
|
166
|
+
backgroundColor:
|
|
167
|
+
values.indexOf(option) === -1 ? '#ffffff' : '#D3E4F0'
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
{getOptionLabel(option)}
|
|
171
|
+
</MenuItem>
|
|
172
|
+
))}
|
|
173
|
+
</MuiSelect>
|
|
174
|
+
{(error || helperText) && (
|
|
175
|
+
<FormHelperText component="span" className={error ? 'Mui-error' : ''}>
|
|
176
|
+
{error || helperText}
|
|
177
|
+
</FormHelperText>
|
|
178
|
+
)}
|
|
179
|
+
</FormControl>
|
|
180
|
+
</Box>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export default MultiSelect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './MultiSelect';
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React, { BaseSyntheticEvent, useMemo, useState } from 'react';
|
|
2
|
+
import { FormHelperText, InputLabel, MenuItem } from '@mui/material';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import FormControl from '@mui/material/FormControl';
|
|
5
|
+
import MuiSelect, {
|
|
6
|
+
SelectChangeEvent,
|
|
7
|
+
SelectProps
|
|
8
|
+
} from '@mui/material/Select';
|
|
9
|
+
import { faChevronDown, faCheck } from '@fortawesome/pro-solid-svg-icons';
|
|
10
|
+
import Icon from '../../Icon';
|
|
11
|
+
|
|
12
|
+
interface Props<T>
|
|
13
|
+
extends Omit<
|
|
14
|
+
SelectProps<T>,
|
|
15
|
+
'value' | 'onChange' | 'defaultValue' | 'color'
|
|
16
|
+
> {
|
|
17
|
+
label?: string;
|
|
18
|
+
helperText?: string;
|
|
19
|
+
options: T[];
|
|
20
|
+
getOptionLabel?: (option: T) => string;
|
|
21
|
+
color?: 'success';
|
|
22
|
+
onChange: (value: T) => void;
|
|
23
|
+
defaultValue?: T | undefined;
|
|
24
|
+
width?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const Select = <T extends {}>({
|
|
28
|
+
label = '',
|
|
29
|
+
defaultValue,
|
|
30
|
+
options,
|
|
31
|
+
color,
|
|
32
|
+
error,
|
|
33
|
+
placeholder,
|
|
34
|
+
onChange,
|
|
35
|
+
getOptionLabel,
|
|
36
|
+
helperText,
|
|
37
|
+
onBlur,
|
|
38
|
+
width = 200,
|
|
39
|
+
...props
|
|
40
|
+
}: Props<T>) => {
|
|
41
|
+
const [value, setValue] = useState(defaultValue);
|
|
42
|
+
|
|
43
|
+
const handleChange = (event: SelectChangeEvent<T>) => {
|
|
44
|
+
const newValue = event.target.value as T;
|
|
45
|
+
onChange(newValue);
|
|
46
|
+
setValue(newValue);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const getFormControlClassName = useMemo(() => {
|
|
50
|
+
const classNames: string[] = ['DsSelect'];
|
|
51
|
+
if (props.disabled) classNames.push('Mui-disabled');
|
|
52
|
+
if (value) classNames.push('Mui-filled');
|
|
53
|
+
|
|
54
|
+
return classNames;
|
|
55
|
+
}, [value, props.disabled]);
|
|
56
|
+
|
|
57
|
+
const getInputLabelClassName = useMemo(() => {
|
|
58
|
+
const classNames: string[] = [];
|
|
59
|
+
if (error) classNames.push('Mui-error');
|
|
60
|
+
if (props.disabled) classNames.push('Mui-disabled');
|
|
61
|
+
|
|
62
|
+
return classNames;
|
|
63
|
+
}, [error, props.disabled]);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Box sx={{ width }}>
|
|
67
|
+
<FormControl
|
|
68
|
+
id={label === placeholder ? 'select-mui' : 'select-ds'}
|
|
69
|
+
fullWidth
|
|
70
|
+
color={color}
|
|
71
|
+
className={getFormControlClassName.join(' ')}
|
|
72
|
+
sx={{
|
|
73
|
+
'.MuiOutlinedInput-input': {
|
|
74
|
+
marginTop: value ? '0px' : '2px'
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<InputLabel className={getInputLabelClassName.join(' ')}>
|
|
79
|
+
{label}
|
|
80
|
+
</InputLabel>
|
|
81
|
+
<MuiSelect
|
|
82
|
+
sx={{
|
|
83
|
+
color: color + '! important',
|
|
84
|
+
'& .MuiSelect-select .notranslate::after': placeholder
|
|
85
|
+
? {
|
|
86
|
+
content: `"${placeholder}"`,
|
|
87
|
+
opacity:
|
|
88
|
+
label === placeholder ? '0 !important' : '1 !important'
|
|
89
|
+
}
|
|
90
|
+
: {}
|
|
91
|
+
}}
|
|
92
|
+
label={label}
|
|
93
|
+
placeholder={label === placeholder ? '' : placeholder}
|
|
94
|
+
value={value}
|
|
95
|
+
error={!!error}
|
|
96
|
+
notched={
|
|
97
|
+
/* eslint-disable-next-line no-undefined */
|
|
98
|
+
label === placeholder ? undefined : true
|
|
99
|
+
}
|
|
100
|
+
onChange={handleChange}
|
|
101
|
+
onBlur={onBlur}
|
|
102
|
+
IconComponent={({ className }) => (
|
|
103
|
+
<Icon
|
|
104
|
+
className={
|
|
105
|
+
props.disabled ? 'Mui-disabled SelectIcon' : 'SelectIcon'
|
|
106
|
+
}
|
|
107
|
+
icon={color === 'success' ? faCheck : faChevronDown}
|
|
108
|
+
size={color === 'success' ? 'md' : 'sm'}
|
|
109
|
+
sx={{
|
|
110
|
+
marginRight: '12px',
|
|
111
|
+
marginTop: color === 'success' ? '2px' : '0px',
|
|
112
|
+
transform:
|
|
113
|
+
className.toString().includes('iconOpen') &&
|
|
114
|
+
color !== 'success'
|
|
115
|
+
? 'rotate(180deg)'
|
|
116
|
+
: 'none'
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
MenuProps={{
|
|
121
|
+
PaperProps: {
|
|
122
|
+
style: {
|
|
123
|
+
width
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}}
|
|
127
|
+
{...props}
|
|
128
|
+
>
|
|
129
|
+
{options?.map((option, i) => (
|
|
130
|
+
<MenuItem
|
|
131
|
+
key={i}
|
|
132
|
+
value={option as any}
|
|
133
|
+
onMouseEnter={(e: BaseSyntheticEvent) =>
|
|
134
|
+
(e.target.style.backgroundColor = '#D3E4F0')
|
|
135
|
+
}
|
|
136
|
+
onMouseLeave={(e: BaseSyntheticEvent) =>
|
|
137
|
+
(e.target.style.backgroundColor = '#ffffff')
|
|
138
|
+
}
|
|
139
|
+
>
|
|
140
|
+
{getOptionLabel ? getOptionLabel(option) : option['label']}
|
|
141
|
+
</MenuItem>
|
|
142
|
+
))}
|
|
143
|
+
</MuiSelect>
|
|
144
|
+
{(error || helperText) && (
|
|
145
|
+
<FormHelperText component="span" className={error ? 'Mui-error' : ''}>
|
|
146
|
+
{error || helperText}
|
|
147
|
+
</FormHelperText>
|
|
148
|
+
)}
|
|
149
|
+
</FormControl>
|
|
150
|
+
</Box>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export default Select;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Select';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
FocusEventHandler,
|
|
3
|
+
ReactNode,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState
|
|
6
|
+
} from 'react';
|
|
7
|
+
|
|
8
|
+
import MuiTextField, { TextFieldProps } from '@mui/material/TextField';
|
|
9
|
+
import { InputProps as StandardInputProps } from '@mui/material/Input/Input';
|
|
10
|
+
|
|
11
|
+
interface Props extends Omit<TextFieldProps, 'error'> {
|
|
12
|
+
error?: string;
|
|
13
|
+
onBlur?: FocusEventHandler<unknown>;
|
|
14
|
+
onChange?: StandardInputProps['onChange'];
|
|
15
|
+
label: string;
|
|
16
|
+
value: string;
|
|
17
|
+
name: string;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
endAdornment?: ReactNode;
|
|
21
|
+
maxLength?: number;
|
|
22
|
+
}
|
|
23
|
+
const TextField = ({
|
|
24
|
+
name,
|
|
25
|
+
value,
|
|
26
|
+
error,
|
|
27
|
+
onBlur,
|
|
28
|
+
onChange,
|
|
29
|
+
label,
|
|
30
|
+
disabled,
|
|
31
|
+
endAdornment,
|
|
32
|
+
placeholder = '',
|
|
33
|
+
maxLength,
|
|
34
|
+
...props
|
|
35
|
+
}: Props) => {
|
|
36
|
+
const [internalValue, setInternalValue] = useState(value);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (value !== internalValue) {
|
|
39
|
+
setInternalValue(value);
|
|
40
|
+
}
|
|
41
|
+
}, [value]);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<MuiTextField
|
|
45
|
+
id={label === placeholder ? 'text-field-mui' : 'text-field-ds'}
|
|
46
|
+
className="DsTextField"
|
|
47
|
+
variant="outlined"
|
|
48
|
+
name={name}
|
|
49
|
+
label={label}
|
|
50
|
+
value={internalValue}
|
|
51
|
+
placeholder={label === placeholder ? '' : placeholder}
|
|
52
|
+
FormHelperTextProps={{ component: 'div' } as any}
|
|
53
|
+
InputLabelProps={{
|
|
54
|
+
shrink:
|
|
55
|
+
/* eslint-disable-next-line no-undefined */
|
|
56
|
+
label === placeholder ? undefined : true
|
|
57
|
+
}}
|
|
58
|
+
onClick={(e) => e.stopPropagation()}
|
|
59
|
+
onChange={(e) => {
|
|
60
|
+
setInternalValue(e.currentTarget.value);
|
|
61
|
+
if (onChange) {
|
|
62
|
+
onChange(e);
|
|
63
|
+
}
|
|
64
|
+
}}
|
|
65
|
+
onBlur={(e) => {
|
|
66
|
+
if (onBlur) {
|
|
67
|
+
onBlur(e);
|
|
68
|
+
}
|
|
69
|
+
}}
|
|
70
|
+
error={!!error}
|
|
71
|
+
disabled={disabled}
|
|
72
|
+
InputProps={{ endAdornment }}
|
|
73
|
+
inputProps={{ maxLength }}
|
|
74
|
+
{...props}
|
|
75
|
+
helperText={
|
|
76
|
+
<div style={{ display: 'table', width: '100%' }}>
|
|
77
|
+
{(error || props.helperText) && (
|
|
78
|
+
<div
|
|
79
|
+
style={{
|
|
80
|
+
display: maxLength ? 'table-cell' : '',
|
|
81
|
+
float: 'left'
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{error || props.helperText}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
{maxLength && (
|
|
88
|
+
<div style={{ display: 'table-cell', float: 'right' }}>
|
|
89
|
+
{internalValue.length}/{maxLength} caract.
|
|
90
|
+
</div>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export default TextField;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './TextField';
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React, { forwardRef, ReactElement } from 'react';
|
|
2
|
+
import SvgIcon from '@mui/material/SvgIcon';
|
|
3
|
+
import { SxProps, Theme } from '@mui/material/styles';
|
|
4
|
+
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
5
|
+
import Box, { BoxProps } from '@mui/material/Box';
|
|
6
|
+
import { useTheme } from '@mui/system';
|
|
7
|
+
|
|
8
|
+
const FontSizes = {
|
|
9
|
+
xs: 8,
|
|
10
|
+
sm: 12,
|
|
11
|
+
md: 16,
|
|
12
|
+
lg: 24,
|
|
13
|
+
xl: 32,
|
|
14
|
+
xxl: 40,
|
|
15
|
+
xxxl: 72
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
variant?: 'square' | 'none';
|
|
20
|
+
icon: IconDefinition;
|
|
21
|
+
color?: string;
|
|
22
|
+
size?: number | keyof typeof FontSizes;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface FontAwesomeSvgIconProps {
|
|
26
|
+
icon: any;
|
|
27
|
+
fontSize: number | undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// eslint-disable-next-line react/display-name
|
|
31
|
+
const FontAwesomeSvgIcon = forwardRef<SVGSVGElement, FontAwesomeSvgIconProps>(
|
|
32
|
+
({ icon, fontSize }, ref) => {
|
|
33
|
+
const {
|
|
34
|
+
icon: [width, height, , , svgPathData]
|
|
35
|
+
} = icon;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<SvgIcon
|
|
39
|
+
ref={ref}
|
|
40
|
+
viewBox={`0 0 ${width as string} ${height as string}`}
|
|
41
|
+
sx={{
|
|
42
|
+
fontSize: fontSize ?? '14px'
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
{typeof svgPathData === 'string' ? (
|
|
46
|
+
<path d={svgPathData} />
|
|
47
|
+
) : (
|
|
48
|
+
/**
|
|
49
|
+
* A multi-path Font Awesome icon seems to imply a duotune icon. The 0th path seems to
|
|
50
|
+
* be the faded element (referred to as the "secondary" path in the Font Awesome docs)
|
|
51
|
+
* of a duotone icon. 40% is the default opacity.
|
|
52
|
+
*
|
|
53
|
+
* @see https://fontawesome.com/how-to-use/on-the-web/styling/duotone-icons#changing-opacity
|
|
54
|
+
*/
|
|
55
|
+
svgPathData.map((d: string, i: number) => (
|
|
56
|
+
<path style={{ opacity: i === 0 ? 0.4 : 1 }} d={d} key={i} />
|
|
57
|
+
))
|
|
58
|
+
)}
|
|
59
|
+
</SvgIcon>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const IconProvider = ({
|
|
65
|
+
variant = 'none',
|
|
66
|
+
icon,
|
|
67
|
+
color = '#136cac', // blueClickable
|
|
68
|
+
size = 16,
|
|
69
|
+
...props
|
|
70
|
+
}: Props & BoxProps): ReactElement => {
|
|
71
|
+
const getStyles = (): SxProps<Theme> => {
|
|
72
|
+
const theme = useTheme();
|
|
73
|
+
const usedColor = theme.palette[color]
|
|
74
|
+
? theme.palette[color]
|
|
75
|
+
: !color || color?.length === 0
|
|
76
|
+
? '#136cac'
|
|
77
|
+
: color;
|
|
78
|
+
|
|
79
|
+
switch (variant) {
|
|
80
|
+
case 'square':
|
|
81
|
+
return {
|
|
82
|
+
color: usedColor,
|
|
83
|
+
backgroundColor: `${usedColor}14`,
|
|
84
|
+
borderRadius: '4px',
|
|
85
|
+
borderColor: '1px solid ' + usedColor,
|
|
86
|
+
overflow: 'visible',
|
|
87
|
+
padding: '10px',
|
|
88
|
+
width: '35px',
|
|
89
|
+
height: '35px',
|
|
90
|
+
display: 'flex',
|
|
91
|
+
justifyContent: 'center',
|
|
92
|
+
alignItems: 'center'
|
|
93
|
+
};
|
|
94
|
+
default:
|
|
95
|
+
return {
|
|
96
|
+
color: usedColor,
|
|
97
|
+
width: usedFontSize,
|
|
98
|
+
height: size,
|
|
99
|
+
display: 'inline-flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'center'
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const isKey = (key: PropertyKey): key is keyof typeof FontSizes => {
|
|
107
|
+
return key in FontSizes;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const usedFontSize: number =
|
|
111
|
+
variant === 'square'
|
|
112
|
+
? 16
|
|
113
|
+
: isKey(size)
|
|
114
|
+
? FontSizes[size]
|
|
115
|
+
: size >= 0
|
|
116
|
+
? size
|
|
117
|
+
: 16;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Box component="span" sx={getStyles()} {...props}>
|
|
121
|
+
<FontAwesomeSvgIcon icon={icon} fontSize={usedFontSize} />
|
|
122
|
+
</Box>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default IconProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Icon';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Link as LinkMui, LinkProps } from '@mui/material';
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<LinkProps, 'variant'> {
|
|
5
|
+
variant?: 'link1' | 'link2';
|
|
6
|
+
component?: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const Link = (props: Props) => <LinkMui {...(props as LinkProps)} />;
|
|
10
|
+
|
|
11
|
+
export default Link;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Link';
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Divider,
|
|
5
|
+
Pagination as MUIPagination,
|
|
6
|
+
MenuItem,
|
|
7
|
+
Select
|
|
8
|
+
} from '@mui/material';
|
|
9
|
+
|
|
10
|
+
import Text from '../Text';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
totalString: string;
|
|
14
|
+
totalPerPageString: string;
|
|
15
|
+
totalPages: number;
|
|
16
|
+
limitsPerPage: number[];
|
|
17
|
+
setLimit?: (limit: number) => void;
|
|
18
|
+
setPage?: (page: number) => void;
|
|
19
|
+
page?: number;
|
|
20
|
+
limit?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const Pagination = ({
|
|
24
|
+
totalString,
|
|
25
|
+
totalPerPageString,
|
|
26
|
+
totalPages,
|
|
27
|
+
limitsPerPage,
|
|
28
|
+
setLimit,
|
|
29
|
+
setPage,
|
|
30
|
+
page = 1,
|
|
31
|
+
limit
|
|
32
|
+
}: Props) => {
|
|
33
|
+
const [internalPage, setInternalPage] = useState<number>(page);
|
|
34
|
+
const [internalLimit, setInternalLimit] = useState<number>(
|
|
35
|
+
limit ?? limitsPerPage[0]
|
|
36
|
+
);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (page !== internalPage) {
|
|
39
|
+
setInternalPage(page);
|
|
40
|
+
}
|
|
41
|
+
}, [page]);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (limit && limit !== internalLimit) {
|
|
45
|
+
setInternalLimit(limit);
|
|
46
|
+
}
|
|
47
|
+
}, [limit]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<Box display="flex">
|
|
52
|
+
<Text color="greyXDark" variant="body1Bold" pt="10px" pr="16px">
|
|
53
|
+
{totalString}
|
|
54
|
+
</Text>
|
|
55
|
+
{totalPages > 1 && (
|
|
56
|
+
<>
|
|
57
|
+
<Divider
|
|
58
|
+
orientation="vertical"
|
|
59
|
+
sx={{ marginRight: '16px', color: 'greyXLight' }}
|
|
60
|
+
/>
|
|
61
|
+
<Select
|
|
62
|
+
sx={{
|
|
63
|
+
// @TODO replace by Select from ui-kit when ready
|
|
64
|
+
height: '32px',
|
|
65
|
+
width: '75px',
|
|
66
|
+
fontSize: '14px',
|
|
67
|
+
fontWeight: 400
|
|
68
|
+
}}
|
|
69
|
+
labelId="select-label"
|
|
70
|
+
id="dac-select-label"
|
|
71
|
+
value={internalLimit}
|
|
72
|
+
onChange={(event) => {
|
|
73
|
+
setInternalLimit(Number(event?.target?.value));
|
|
74
|
+
setLimit && setLimit(Number(event?.target?.value));
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{limitsPerPage.map((limit, id) => (
|
|
78
|
+
<MenuItem key={id} value={limit}>
|
|
79
|
+
{limit}
|
|
80
|
+
</MenuItem>
|
|
81
|
+
))}
|
|
82
|
+
</Select>
|
|
83
|
+
<Text
|
|
84
|
+
color="greyXDark"
|
|
85
|
+
variant="body1Regular"
|
|
86
|
+
pt="6px"
|
|
87
|
+
pl="6px"
|
|
88
|
+
pr="16px"
|
|
89
|
+
>
|
|
90
|
+
{totalPerPageString}
|
|
91
|
+
</Text>
|
|
92
|
+
<Divider
|
|
93
|
+
orientation="vertical"
|
|
94
|
+
sx={{ marginRight: '16px', color: 'greyXLight' }}
|
|
95
|
+
/>
|
|
96
|
+
</>
|
|
97
|
+
)}
|
|
98
|
+
</Box>
|
|
99
|
+
{totalPages > 1 && (
|
|
100
|
+
<Box display="flex" pr="4px">
|
|
101
|
+
<MUIPagination
|
|
102
|
+
variant="outlined"
|
|
103
|
+
shape="rounded"
|
|
104
|
+
count={totalPages}
|
|
105
|
+
page={internalPage}
|
|
106
|
+
onChange={(event: React.ChangeEvent<unknown>, value: number) => {
|
|
107
|
+
setInternalPage(value);
|
|
108
|
+
setPage && setPage(value);
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
</Box>
|
|
112
|
+
)}
|
|
113
|
+
</>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
export default Pagination;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Pagination';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Typography, TypographyProps } from '@mui/material';
|
|
3
|
+
|
|
4
|
+
interface Props extends Omit<TypographyProps, 'variant'> {
|
|
5
|
+
variant?:
|
|
6
|
+
| 'bigNumber'
|
|
7
|
+
| 'body1Regular'
|
|
8
|
+
| 'body1Medium'
|
|
9
|
+
| 'body1Bold'
|
|
10
|
+
| 'body2Regular'
|
|
11
|
+
| 'body2Medium'
|
|
12
|
+
| 'body2Bold'
|
|
13
|
+
| 'caption'
|
|
14
|
+
| 'buttonNotif'
|
|
15
|
+
| 'header1'
|
|
16
|
+
| 'header2'
|
|
17
|
+
| 'header3'
|
|
18
|
+
| 'header4';
|
|
19
|
+
component?: any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Text = (props: Props) => (
|
|
23
|
+
<Typography color="greyXDark" {...(props as TypographyProps)} />
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export default Text;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Text';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { IconButton, Tooltip as TooltipMui, TooltipProps } from '@mui/material';
|
|
3
|
+
import Text from '../Text/Text';
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<TooltipProps, 'icon' | 'children'> {
|
|
6
|
+
title: string;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Tooltip = ({ title, children, ...props }: Props) => (
|
|
11
|
+
<TooltipMui
|
|
12
|
+
title={
|
|
13
|
+
<Text variant="body2Regular" color="white">
|
|
14
|
+
{title}
|
|
15
|
+
</Text>
|
|
16
|
+
}
|
|
17
|
+
{...props}
|
|
18
|
+
className="DsTooltip-root"
|
|
19
|
+
>
|
|
20
|
+
<IconButton
|
|
21
|
+
sx={{
|
|
22
|
+
padding: 0,
|
|
23
|
+
'& .MuiBox-root': {
|
|
24
|
+
width: 0
|
|
25
|
+
}
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</IconButton>
|
|
30
|
+
</TooltipMui>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export default Tooltip;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Tooltip';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { default as Text } from './Text';
|
|
2
|
+
export { default as Link } from './Link';
|
|
3
|
+
export { default as Button } from './Button';
|
|
4
|
+
export { default as TextField } from './Form/TextField';
|
|
5
|
+
export { default as Select } from './Form/Select';
|
|
6
|
+
export { default as MultiSelect } from './Form/MultiSelect';
|
|
7
|
+
export { default as Checkbox } from './Form/Checkbox';
|
|
8
|
+
export { default as Accordion } from './Accordion';
|
|
9
|
+
export { default as IconProvider } from './Icon/Icon';
|
|
10
|
+
export { default as Pagination } from './Pagination';
|
|
11
|
+
export { default as Chip } from './Chip/Chip';
|
|
12
|
+
export { default as EmbeddedNotification } from './EmbbededNotification/EmbeddedNotification';
|
|
13
|
+
export { default as Tooltip } from './Tooltip/Tooltip';
|