@centreon/ui 26.3.19 → 26.5.1
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 +3 -3
- package/src/Form/Inputs/ConnectedAutocomplete.tsx +23 -14
- package/src/Form/Inputs/models.ts +1 -0
- package/src/Graph/common/timeSeries/index.ts +3 -1
- package/src/InputField/Text/index.tsx +5 -4
- package/src/Listing/Row/Row.tsx +3 -43
- package/src/Listing/index.tsx +2 -9
- package/src/api/buildListingDecoder.ts +39 -6
- package/src/api/buildListingEndpoint/index.ts +24 -20
- package/src/api/buildListingEndpoint/models.ts +4 -2
- package/src/api/useMutationQuery/index.ts +3 -1
- package/src/queryParameters/index.ts +15 -3
- package/src/utils/useMemoComponent.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centreon/ui",
|
|
3
|
-
"version": "26.
|
|
3
|
+
"version": "26.5.1",
|
|
4
4
|
"description": "Centreon UI Components",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"update:deps": "pnpx npm-check-updates -i --format group",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"url": "git+https://github.com/centreon/centreon.git"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
|
-
"
|
|
32
|
-
"
|
|
31
|
+
"react",
|
|
32
|
+
"centreon"
|
|
33
33
|
],
|
|
34
34
|
"author": {
|
|
35
35
|
"name": "centreon@centreon.com"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FormikValues, useFormikContext } from 'formik';
|
|
2
2
|
import { equals, isEmpty, path, propEq, reject, split } from 'ramda';
|
|
3
3
|
import { useCallback, useMemo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useMemoComponent
|
|
10
10
|
} from '../..';
|
|
11
11
|
import MultiConnectedAutocompleteField from '../../InputField/Select/Autocomplete/Connected/Multi';
|
|
12
|
-
import {
|
|
12
|
+
import { InputPropsWithoutGroup, InputType } from './models';
|
|
13
13
|
|
|
14
14
|
const defaultFilterKey = 'name';
|
|
15
15
|
|
|
@@ -42,10 +42,26 @@ const ConnectedAutocomplete = ({
|
|
|
42
42
|
|
|
43
43
|
const isMultiple = equals(type, InputType.MultiConnectedAutocomplete);
|
|
44
44
|
|
|
45
|
-
const getEndpoint = (parameters): string =>
|
|
46
|
-
|
|
45
|
+
const getEndpoint = (parameters): string => {
|
|
46
|
+
const nameQueryParameters =
|
|
47
|
+
connectedAutocomplete?.useNewAPIFormat && parameters?.search
|
|
48
|
+
? [
|
|
49
|
+
{
|
|
50
|
+
name: 'name[lk]',
|
|
51
|
+
value: parameters.search.conditions[0].values.$lk.slice(1, -1)
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
: [];
|
|
55
|
+
|
|
56
|
+
return buildListingEndpoint({
|
|
57
|
+
apiFormat: connectedAutocomplete?.useNewAPIFormat
|
|
58
|
+
? 'JSON-LD'
|
|
59
|
+
: 'Standard',
|
|
47
60
|
baseEndpoint: connectedAutocomplete?.endpoint,
|
|
48
|
-
customQueryParameters:
|
|
61
|
+
customQueryParameters: [
|
|
62
|
+
...(connectedAutocomplete?.customQueryParameters || []),
|
|
63
|
+
...nameQueryParameters
|
|
64
|
+
],
|
|
49
65
|
parameters: {
|
|
50
66
|
...parameters,
|
|
51
67
|
search: {
|
|
@@ -58,6 +74,7 @@ const ConnectedAutocomplete = ({
|
|
|
58
74
|
sort: { [filterKey]: 'ASC' }
|
|
59
75
|
}
|
|
60
76
|
});
|
|
77
|
+
};
|
|
61
78
|
|
|
62
79
|
const fieldNamePath = split('.', fieldName);
|
|
63
80
|
|
|
@@ -79,15 +96,7 @@ const ConnectedAutocomplete = ({
|
|
|
79
96
|
setFieldTouched(fieldName, true, false);
|
|
80
97
|
setFieldValue(fieldName, value);
|
|
81
98
|
},
|
|
82
|
-
[
|
|
83
|
-
fieldName,
|
|
84
|
-
change,
|
|
85
|
-
setFieldTouched,
|
|
86
|
-
setFieldValue,
|
|
87
|
-
setTouched,
|
|
88
|
-
setValues,
|
|
89
|
-
values
|
|
90
|
-
]
|
|
99
|
+
[fieldName, touched, additionalMemoProps]
|
|
91
100
|
);
|
|
92
101
|
|
|
93
102
|
const blur = (): void => setFieldTouched(fieldName, true);
|
|
@@ -56,6 +56,7 @@ export interface InputProps {
|
|
|
56
56
|
options?: Array<string>;
|
|
57
57
|
};
|
|
58
58
|
connectedAutocomplete?: {
|
|
59
|
+
useNewAPIFormat?: boolean;
|
|
59
60
|
additionalConditionParameters: Array<ConditionsSearchParameter>;
|
|
60
61
|
customQueryParameters: Array<QueryParameter>;
|
|
61
62
|
chipColor?: string;
|
|
@@ -136,7 +136,9 @@ const toLine = ({
|
|
|
136
136
|
...defaultDsData,
|
|
137
137
|
...(ds_data || {}),
|
|
138
138
|
ds_color_area:
|
|
139
|
-
ds_data?.ds_color_area ??
|
|
139
|
+
ds_data?.ds_color_area ??
|
|
140
|
+
ds_data?.ds_color_line ??
|
|
141
|
+
defaultDsData.ds_color_line
|
|
140
142
|
};
|
|
141
143
|
|
|
142
144
|
return {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Box,
|
|
3
3
|
InputAdornment,
|
|
4
|
-
|
|
4
|
+
InputProps,
|
|
5
5
|
TextField as MuiTextField,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
TextFieldProps,
|
|
7
|
+
TextFieldSlotsAndSlotProps,
|
|
8
|
+
Theme,
|
|
9
9
|
Tooltip,
|
|
10
10
|
Typography
|
|
11
11
|
} from '@mui/material';
|
|
@@ -170,6 +170,7 @@ const TextField = forwardRef(
|
|
|
170
170
|
ref={ref}
|
|
171
171
|
size={size || 'small'}
|
|
172
172
|
{...getValueProps()}
|
|
173
|
+
autoComplete="off"
|
|
173
174
|
className={classes.textField}
|
|
174
175
|
required={required}
|
|
175
176
|
slotProps={{
|
package/src/Listing/Row/Row.tsx
CHANGED
|
@@ -2,12 +2,10 @@ import { TableRow, type TableRowProps, useTheme } from '@mui/material';
|
|
|
2
2
|
|
|
3
3
|
import type { ListingVariant } from '@centreon/ui-context';
|
|
4
4
|
|
|
5
|
-
import { equals,
|
|
5
|
+
import { equals, lt, not, pluck } from 'ramda';
|
|
6
6
|
import { memo, useCallback, useEffect, useRef } from 'react';
|
|
7
7
|
|
|
8
|
-
import LoadingSkeleton from '../../LoadingSkeleton';
|
|
9
8
|
import { useViewportIntersection } from '../../utils/useViewportIntersection';
|
|
10
|
-
import { performanceRowsLimit } from '../index';
|
|
11
9
|
import type { Column, ColumnConfiguration, RowColorCondition } from '../models';
|
|
12
10
|
|
|
13
11
|
type Props = {
|
|
@@ -39,33 +37,8 @@ const Row = memo<RowProps>(
|
|
|
39
37
|
tabIndex,
|
|
40
38
|
onMouseOver,
|
|
41
39
|
onFocus,
|
|
42
|
-
onClick
|
|
43
|
-
isInViewport,
|
|
44
|
-
visibleColumns,
|
|
45
|
-
checkable,
|
|
46
|
-
limit
|
|
40
|
+
onClick
|
|
47
41
|
}: RowProps): JSX.Element => {
|
|
48
|
-
if (not(isInViewport) && gte(limit, performanceRowsLimit)) {
|
|
49
|
-
return (
|
|
50
|
-
<div className="contents">
|
|
51
|
-
{checkable && (
|
|
52
|
-
<div className="p-1">
|
|
53
|
-
<div>
|
|
54
|
-
<LoadingSkeleton className="w-full" />
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
)}
|
|
58
|
-
{visibleColumns.map(({ id }) => (
|
|
59
|
-
<div className="p-1" key={`loading_${id}`}>
|
|
60
|
-
<div>
|
|
61
|
-
<LoadingSkeleton className="w-full" />
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
))}
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
42
|
return (
|
|
70
43
|
<TableRow
|
|
71
44
|
className="cursor-pointer contents w-full"
|
|
@@ -83,7 +56,6 @@ const Row = memo<RowProps>(
|
|
|
83
56
|
const {
|
|
84
57
|
row: previousRow,
|
|
85
58
|
rowColorConditions: previousRowColorConditions,
|
|
86
|
-
isInViewport: prevIsInViewport,
|
|
87
59
|
visibleColumns: previousVisibleColumns,
|
|
88
60
|
isShiftKeyDown: prevIsShiftKeyDown,
|
|
89
61
|
shiftKeyDownRowPivot: prevShiftKeyDownRowPivot,
|
|
@@ -117,18 +89,6 @@ const Row = memo<RowProps>(
|
|
|
117
89
|
return false;
|
|
118
90
|
}
|
|
119
91
|
|
|
120
|
-
const isNoLongerInViewport = not(prevIsInViewport) && not(nextIsInViewport);
|
|
121
|
-
|
|
122
|
-
if (isNoLongerInViewport && gte(nextLimit, performanceRowsLimit)) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const isBackInViewport = not(prevIsInViewport) && nextIsInViewport;
|
|
127
|
-
|
|
128
|
-
if (isBackInViewport && gte(nextLimit, performanceRowsLimit)) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
92
|
const previousRowConditions = previousRowColorConditions?.map(
|
|
133
93
|
({ condition }) => condition(previousRow)
|
|
134
94
|
);
|
|
@@ -185,7 +145,7 @@ const IntersectionRow = ({ isHovered, ...rest }: Props): JSX.Element => {
|
|
|
185
145
|
|
|
186
146
|
useEffect(() => {
|
|
187
147
|
setElement(getFirstCellElement() as HTMLDivElement);
|
|
188
|
-
}, [getFirstCellElement
|
|
148
|
+
}, [getFirstCellElement()]);
|
|
189
149
|
|
|
190
150
|
return (
|
|
191
151
|
<div className="contents w-full" data-is-hovered={isHovered} ref={rowRef}>
|
package/src/Listing/index.tsx
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
filter,
|
|
11
11
|
findIndex,
|
|
12
12
|
gt,
|
|
13
|
-
gte,
|
|
14
13
|
identity,
|
|
15
14
|
includes,
|
|
16
15
|
isNil,
|
|
@@ -143,8 +142,6 @@ const defaultColumnConfiguration = {
|
|
|
143
142
|
sortable: false
|
|
144
143
|
};
|
|
145
144
|
|
|
146
|
-
export const performanceRowsLimit = 60;
|
|
147
|
-
|
|
148
145
|
const Listing = <
|
|
149
146
|
TRow extends {
|
|
150
147
|
id: RowId;
|
|
@@ -598,7 +595,7 @@ const Listing = <
|
|
|
598
595
|
component="div"
|
|
599
596
|
onMouseLeave={clearHoveredRow}
|
|
600
597
|
>
|
|
601
|
-
{rowsToDisplay.map((row
|
|
598
|
+
{rowsToDisplay.map((row) => {
|
|
602
599
|
const isRowSelected = isSelected(row);
|
|
603
600
|
const isSubItem = allSubItemIds.includes(
|
|
604
601
|
getSubItemRowId(row)
|
|
@@ -617,11 +614,7 @@ const Listing = <
|
|
|
617
614
|
isHovered={isRowHovered}
|
|
618
615
|
isSelected={isRowSelected}
|
|
619
616
|
isShiftKeyDown={isShiftKeyDown}
|
|
620
|
-
key={
|
|
621
|
-
gte(limit, performanceRowsLimit)
|
|
622
|
-
? `row_${index}`
|
|
623
|
-
: getRowId(row)
|
|
624
|
-
}
|
|
617
|
+
key={getRowId(row)}
|
|
625
618
|
lastSelectionIndex={lastSelectionIndex}
|
|
626
619
|
limit={limit}
|
|
627
620
|
listingVariant={listingVariant}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { equals } from 'ramda';
|
|
1
2
|
import { JsonDecoder } from 'ts.data.json';
|
|
2
3
|
|
|
3
|
-
import
|
|
4
|
+
import { Listing, ListingMeta } from './models';
|
|
4
5
|
|
|
5
6
|
const metaDecoder = JsonDecoder.object<ListingMeta>(
|
|
6
7
|
{
|
|
@@ -15,13 +16,34 @@ interface ListingDecoderOptions<TEntity> {
|
|
|
15
16
|
entityDecoder: JsonDecoder.Decoder<TEntity>;
|
|
16
17
|
entityDecoderName: string;
|
|
17
18
|
listingDecoderName: string;
|
|
19
|
+
apiFormat?: 'Standard' | 'JSON-LD';
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
entityDecoder
|
|
22
|
-
entityDecoderName,
|
|
23
|
-
listingDecoderName
|
|
24
|
-
|
|
22
|
+
const jsonLdListingDecoder = <TEntity>(
|
|
23
|
+
entityDecoder: JsonDecoder.Decoder<TEntity>,
|
|
24
|
+
entityDecoderName: string,
|
|
25
|
+
listingDecoderName: string
|
|
26
|
+
): JsonDecoder.Decoder<Listing<TEntity>> =>
|
|
27
|
+
JsonDecoder.object(
|
|
28
|
+
{
|
|
29
|
+
member: JsonDecoder.array(entityDecoder, entityDecoderName),
|
|
30
|
+
totalItems: JsonDecoder.number
|
|
31
|
+
},
|
|
32
|
+
listingDecoderName
|
|
33
|
+
).map((data) => ({
|
|
34
|
+
meta: {
|
|
35
|
+
limit: data.member.length,
|
|
36
|
+
page: 1,
|
|
37
|
+
total: data.totalItems
|
|
38
|
+
},
|
|
39
|
+
result: data.member
|
|
40
|
+
})) as JsonDecoder.Decoder<Listing<TEntity>>;
|
|
41
|
+
|
|
42
|
+
const standardListingDecoder = <TEntity>(
|
|
43
|
+
entityDecoder: JsonDecoder.Decoder<TEntity>,
|
|
44
|
+
entityDecoderName: string,
|
|
45
|
+
listingDecoderName: string
|
|
46
|
+
): JsonDecoder.Decoder<Listing<TEntity>> =>
|
|
25
47
|
JsonDecoder.object<Listing<TEntity>>(
|
|
26
48
|
{
|
|
27
49
|
meta: metaDecoder,
|
|
@@ -30,4 +52,15 @@ const buildListingDecoder = <TEntity>({
|
|
|
30
52
|
listingDecoderName
|
|
31
53
|
);
|
|
32
54
|
|
|
55
|
+
const buildListingDecoder = <TEntity>({
|
|
56
|
+
entityDecoder,
|
|
57
|
+
entityDecoderName,
|
|
58
|
+
listingDecoderName,
|
|
59
|
+
apiFormat = 'Standard'
|
|
60
|
+
}: ListingDecoderOptions<TEntity>): JsonDecoder.Decoder<Listing<TEntity>> => {
|
|
61
|
+
return (
|
|
62
|
+
equals(apiFormat, 'JSON-LD') ? jsonLdListingDecoder : standardListingDecoder
|
|
63
|
+
)(entityDecoder, entityDecoderName, listingDecoderName);
|
|
64
|
+
};
|
|
65
|
+
|
|
33
66
|
export default buildListingDecoder;
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
+
import { equals, keys, values } from 'ramda';
|
|
2
|
+
|
|
1
3
|
import toRawQueryParameters from '../../queryParameters';
|
|
2
|
-
import
|
|
4
|
+
import { QueryParameter } from '../../queryParameters/models';
|
|
3
5
|
import { getSearchQueryParameterValue } from './getSearchQueryParameterValue';
|
|
4
|
-
import
|
|
6
|
+
import { BuildListingEndpointParameters, Parameters } from './models';
|
|
5
7
|
|
|
6
8
|
const getQueryParameters = ({
|
|
7
9
|
sort,
|
|
8
10
|
page,
|
|
9
11
|
limit,
|
|
10
12
|
search,
|
|
11
|
-
customQueryParameters = []
|
|
13
|
+
customQueryParameters = [],
|
|
14
|
+
apiFormat
|
|
12
15
|
}: Parameters): Array<QueryParameter> => {
|
|
16
|
+
if (equals(apiFormat, 'JSON-LD')) {
|
|
17
|
+
return [
|
|
18
|
+
{ name: 'page', value: page },
|
|
19
|
+
{ name: 'itemsPerPage', value: limit },
|
|
20
|
+
{ name: `sort[${keys(sort || {})[0]}]`, value: values(sort || {})[0] },
|
|
21
|
+
...customQueryParameters
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
return [
|
|
14
26
|
{ name: 'page', value: page },
|
|
15
27
|
{ name: 'limit', value: limit },
|
|
@@ -22,33 +34,25 @@ const getQueryParameters = ({
|
|
|
22
34
|
];
|
|
23
35
|
};
|
|
24
36
|
|
|
25
|
-
const buildEndpoint = ({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return `${baseEndpoint}
|
|
37
|
+
const buildEndpoint = ({
|
|
38
|
+
baseEndpoint,
|
|
39
|
+
queryParameters,
|
|
40
|
+
apiFormat
|
|
41
|
+
}): string => {
|
|
42
|
+
return `${baseEndpoint}?${toRawQueryParameters({ apiFormat, queryParameters })}`;
|
|
31
43
|
};
|
|
32
44
|
|
|
33
45
|
const buildListingEndpoint = ({
|
|
34
46
|
baseEndpoint,
|
|
35
47
|
parameters,
|
|
36
48
|
customQueryParameters,
|
|
37
|
-
|
|
49
|
+
apiFormat = 'Standard'
|
|
38
50
|
}: BuildListingEndpointParameters): string => {
|
|
39
|
-
if (isCustomEndpoint) {
|
|
40
|
-
return buildCustomEndpoint({
|
|
41
|
-
baseEndpoint,
|
|
42
|
-
queryParameters: [
|
|
43
|
-
...getQueryParameters({ ...parameters, customQueryParameters })
|
|
44
|
-
]
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
51
|
return buildEndpoint({
|
|
52
|
+
apiFormat,
|
|
49
53
|
baseEndpoint,
|
|
50
54
|
queryParameters: [
|
|
51
|
-
...getQueryParameters({ ...parameters, customQueryParameters })
|
|
55
|
+
...getQueryParameters({ ...parameters, apiFormat, customQueryParameters })
|
|
52
56
|
]
|
|
53
57
|
});
|
|
54
58
|
};
|
|
@@ -3,8 +3,8 @@ import type { QueryParameter } from '../../queryParameters/models';
|
|
|
3
3
|
export interface BuildListingEndpointParameters {
|
|
4
4
|
baseEndpoint?: string;
|
|
5
5
|
customQueryParameters?: Array<QueryParameter>;
|
|
6
|
-
parameters: Parameters
|
|
7
|
-
|
|
6
|
+
parameters: Omit<Parameters, 'apiFormat' | 'customQueryParameters'>;
|
|
7
|
+
apiFormat?: 'Standard' | 'JSON-LD';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export interface SearchMatch {
|
|
@@ -18,7 +18,9 @@ export interface Parameters {
|
|
|
18
18
|
page?: number;
|
|
19
19
|
search?: SearchParameter;
|
|
20
20
|
sort?: SortQueryParameterValue;
|
|
21
|
+
apiFormat: 'Standard' | 'JSON-LD';
|
|
21
22
|
}
|
|
23
|
+
|
|
22
24
|
export interface SearchParameter {
|
|
23
25
|
conditions?: Array<ConditionsSearchParameter>;
|
|
24
26
|
lists?: Array<ListsSearchParameter>;
|
|
@@ -114,7 +114,9 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
114
114
|
defaultFailureMessage,
|
|
115
115
|
endpoint: getEndpoint(_meta as TMeta),
|
|
116
116
|
headers: new Headers({
|
|
117
|
-
'Content-Type':
|
|
117
|
+
'Content-Type': equals(method, Method.PATCH)
|
|
118
|
+
? 'application/merge-patch+json'
|
|
119
|
+
: 'application/json',
|
|
118
120
|
...fetchHeaders
|
|
119
121
|
}),
|
|
120
122
|
isMutation: true,
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { isEmpty, isNil } from 'ramda';
|
|
1
|
+
import { equals, isEmpty, isNil } from 'ramda';
|
|
2
2
|
|
|
3
3
|
import type { QueryParameter } from './models';
|
|
4
4
|
|
|
5
|
+
interface ToRawQueryParametersProps {
|
|
6
|
+
queryParameters: Array<QueryParameter>;
|
|
7
|
+
apiFormat: 'Standard' | 'JSON-LD';
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
const toRawQueryParameter = ({ name, value }): string => {
|
|
6
|
-
return `${name}=${encodeURIComponent(
|
|
11
|
+
return `${name}=${encodeURIComponent(value)}`;
|
|
7
12
|
};
|
|
8
13
|
|
|
9
|
-
const toRawQueryParameters = (
|
|
14
|
+
const toRawQueryParameters = ({
|
|
15
|
+
queryParameters,
|
|
16
|
+
apiFormat
|
|
17
|
+
}: ToRawQueryParametersProps): string =>
|
|
10
18
|
queryParameters
|
|
11
19
|
.filter(({ value }) => !isNil(value) && !isEmpty(value))
|
|
20
|
+
.map(({ name, value }) => ({
|
|
21
|
+
name,
|
|
22
|
+
value: equals(apiFormat, 'JSON-LD') ? value : JSON.stringify(value)
|
|
23
|
+
}))
|
|
12
24
|
.map(toRawQueryParameter)
|
|
13
25
|
.join('&');
|
|
14
26
|
|
|
@@ -21,7 +21,7 @@ interface MemoComponent {
|
|
|
21
21
|
export const useMemoComponent = ({
|
|
22
22
|
Component,
|
|
23
23
|
memoProps
|
|
24
|
-
}: MemoComponent):
|
|
25
|
-
useMemo(() => Component,
|
|
24
|
+
}: MemoComponent): ReactElement =>
|
|
25
|
+
useMemo(() => Component, useDeepCompare(memoProps));
|
|
26
26
|
|
|
27
27
|
export default useMemoComponent;
|