@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groupeactual/ui-kit",
3
- "version": "0.4.33",
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';
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './components';
2
+ export {
3
+ default as DesignSystemProvider,
4
+ DesignSystemContext,
5
+ type DesignSystemContextValues
6
+ } from './DesignSystemProvider';