@bit.rhplus/ag-grid 0.0.81 → 0.0.83
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/BulkEdit/BulkEditDatePicker.jsx +305 -305
- package/BulkEdit/BulkEditInput.jsx +81 -81
- package/BulkEdit/BulkEditPopover.jsx +310 -310
- package/BulkEdit/BulkEditSelect.jsx +106 -106
- package/Editors/DatePickerEditor.jsx +75 -0
- package/Editors/index.jsx +1 -0
- package/Renderers/ImageRenderer.jsx +103 -103
- package/Renderers/LinkRenderer.jsx +133 -133
- package/Renderers/ObjectRenderer.jsx +140 -140
- package/Renderers/SelectCellRenderer.jsx +25 -0
- package/Renderers/StateRenderer.jsx +148 -148
- package/Renderers/index.jsx +1 -0
- package/dist/BulkEdit/BulkEditDatePicker.js +35 -35
- package/dist/Editors/DatePickerEditor.d.ts +9 -0
- package/dist/Editors/DatePickerEditor.js +55 -0
- package/dist/Editors/DatePickerEditor.js.map +1 -0
- package/dist/Editors/index.d.ts +1 -0
- package/dist/Editors/index.js +2 -0
- package/dist/Editors/index.js.map +1 -0
- package/dist/Renderers/SelectCellRenderer.d.ts +4 -0
- package/dist/Renderers/SelectCellRenderer.js +18 -0
- package/dist/Renderers/SelectCellRenderer.js.map +1 -0
- package/dist/Renderers/index.d.ts +1 -0
- package/dist/Renderers/index.js +2 -0
- package/dist/Renderers/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/index.jsx +5 -1
- package/package.json +5 -5
- /package/dist/{preview-1768486452196.js → preview-1768824266519.js} +0 -0
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
import React, { useRef, useEffect } from 'react';
|
|
3
|
-
import { createMemoComparison } from '@bit.rhplus/react-memo';
|
|
4
|
-
import { Select, Button, Space } from 'antd';
|
|
5
|
-
import * as LucideIcons from 'lucide-react';
|
|
6
|
-
|
|
7
|
-
const BulkEditSelect = ({
|
|
8
|
-
value,
|
|
9
|
-
onChange,
|
|
10
|
-
onSubmit,
|
|
11
|
-
onCancel,
|
|
12
|
-
loading,
|
|
13
|
-
options = [],
|
|
14
|
-
placeholder = 'Vyberte hodnotu...',
|
|
15
|
-
allowClear = true,
|
|
16
|
-
fieldLabel = 'value',
|
|
17
|
-
fieldValue = 'id',
|
|
18
|
-
fieldIcon = null,
|
|
19
|
-
showSearch = false,
|
|
20
|
-
}) => {
|
|
21
|
-
const selectRef = useRef(null);
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (selectRef.current) {
|
|
25
|
-
setTimeout(() => {
|
|
26
|
-
selectRef.current?.focus();
|
|
27
|
-
}, 100);
|
|
28
|
-
}
|
|
29
|
-
}, []);
|
|
30
|
-
|
|
31
|
-
const handleKeyDown = (e) => {
|
|
32
|
-
if (e.key === 'Enter' && !loading) {
|
|
33
|
-
e.preventDefault();
|
|
34
|
-
onSubmit(value);
|
|
35
|
-
} else if (e.key === 'Escape') {
|
|
36
|
-
e.preventDefault();
|
|
37
|
-
onCancel();
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<>
|
|
43
|
-
<div style={{ marginBottom: '12px' }} onClick={(e) => e.stopPropagation()}>
|
|
44
|
-
<Select
|
|
45
|
-
ref={selectRef}
|
|
46
|
-
style={{ width: '100%' }}
|
|
47
|
-
value={value}
|
|
48
|
-
onChange={onChange}
|
|
49
|
-
placeholder={placeholder}
|
|
50
|
-
disabled={loading}
|
|
51
|
-
allowClear={allowClear}
|
|
52
|
-
showSearch={showSearch}
|
|
53
|
-
filterOption={showSearch ? (input, option) => {
|
|
54
|
-
// Najdi původní item podle value pro filtrování
|
|
55
|
-
const originalItem = options.find(item => item[fieldValue] === option.value);
|
|
56
|
-
const searchText = originalItem?.[fieldLabel] || '';
|
|
57
|
-
return searchText.toLowerCase().includes(input.toLowerCase());
|
|
58
|
-
} : false}
|
|
59
|
-
options={options.map(item => {
|
|
60
|
-
const iconName = fieldIcon ? item[fieldIcon] : null;
|
|
61
|
-
const LucideIcon = iconName ? LucideIcons[iconName] : null;
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
label: (
|
|
65
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
66
|
-
{LucideIcon && <LucideIcon size={16} />}
|
|
67
|
-
<span>{item[fieldLabel]}</span>
|
|
68
|
-
</div>
|
|
69
|
-
),
|
|
70
|
-
value: item[fieldValue],
|
|
71
|
-
searchText: item[fieldLabel] // Pro lepší filtrování
|
|
72
|
-
};
|
|
73
|
-
})}
|
|
74
|
-
onKeyDown={handleKeyDown}
|
|
75
|
-
onClick={(e) => e.stopPropagation()}
|
|
76
|
-
getPopupContainer={(trigger) => trigger.parentNode}
|
|
77
|
-
/>
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
<Space style={{ width: '100%', justifyContent: 'flex-end' }}>
|
|
81
|
-
<Button size="small" onClick={onCancel} disabled={loading}>
|
|
82
|
-
Zrušit
|
|
83
|
-
</Button>
|
|
84
|
-
<Button
|
|
85
|
-
type="primary"
|
|
86
|
-
size="small"
|
|
87
|
-
onClick={() => onSubmit(value)}
|
|
88
|
-
loading={loading}
|
|
89
|
-
>
|
|
90
|
-
Použít
|
|
91
|
-
</Button>
|
|
92
|
-
</Space>
|
|
93
|
-
</>
|
|
94
|
-
);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// React.memo optimalizace
|
|
98
|
-
// ✅ PERFORMANCE FIX: Ignore callback references
|
|
99
|
-
const arePropsEqual = createMemoComparison(
|
|
100
|
-
['value', 'loading', 'options', 'placeholder', 'allowClear'], // Kritické props
|
|
101
|
-
['onChange', 'onSubmit', 'onCancel'], // Ignorované callbacky
|
|
102
|
-
false,
|
|
103
|
-
'BulkEditSelect'
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
export default React.memo(BulkEditSelect, arePropsEqual);
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import React, { useRef, useEffect } from 'react';
|
|
3
|
+
import { createMemoComparison } from '@bit.rhplus/react-memo';
|
|
4
|
+
import { Select, Button, Space } from 'antd';
|
|
5
|
+
import * as LucideIcons from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
const BulkEditSelect = ({
|
|
8
|
+
value,
|
|
9
|
+
onChange,
|
|
10
|
+
onSubmit,
|
|
11
|
+
onCancel,
|
|
12
|
+
loading,
|
|
13
|
+
options = [],
|
|
14
|
+
placeholder = 'Vyberte hodnotu...',
|
|
15
|
+
allowClear = true,
|
|
16
|
+
fieldLabel = 'value',
|
|
17
|
+
fieldValue = 'id',
|
|
18
|
+
fieldIcon = null,
|
|
19
|
+
showSearch = false,
|
|
20
|
+
}) => {
|
|
21
|
+
const selectRef = useRef(null);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (selectRef.current) {
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
selectRef.current?.focus();
|
|
27
|
+
}, 100);
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const handleKeyDown = (e) => {
|
|
32
|
+
if (e.key === 'Enter' && !loading) {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
onSubmit(value);
|
|
35
|
+
} else if (e.key === 'Escape') {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
onCancel();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
<div style={{ marginBottom: '12px' }} onClick={(e) => e.stopPropagation()}>
|
|
44
|
+
<Select
|
|
45
|
+
ref={selectRef}
|
|
46
|
+
style={{ width: '100%' }}
|
|
47
|
+
value={value}
|
|
48
|
+
onChange={onChange}
|
|
49
|
+
placeholder={placeholder}
|
|
50
|
+
disabled={loading}
|
|
51
|
+
allowClear={allowClear}
|
|
52
|
+
showSearch={showSearch}
|
|
53
|
+
filterOption={showSearch ? (input, option) => {
|
|
54
|
+
// Najdi původní item podle value pro filtrování
|
|
55
|
+
const originalItem = options.find(item => item[fieldValue] === option.value);
|
|
56
|
+
const searchText = originalItem?.[fieldLabel] || '';
|
|
57
|
+
return searchText.toLowerCase().includes(input.toLowerCase());
|
|
58
|
+
} : false}
|
|
59
|
+
options={options.map(item => {
|
|
60
|
+
const iconName = fieldIcon ? item[fieldIcon] : null;
|
|
61
|
+
const LucideIcon = iconName ? LucideIcons[iconName] : null;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
label: (
|
|
65
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
66
|
+
{LucideIcon && <LucideIcon size={16} />}
|
|
67
|
+
<span>{item[fieldLabel]}</span>
|
|
68
|
+
</div>
|
|
69
|
+
),
|
|
70
|
+
value: item[fieldValue],
|
|
71
|
+
searchText: item[fieldLabel] // Pro lepší filtrování
|
|
72
|
+
};
|
|
73
|
+
})}
|
|
74
|
+
onKeyDown={handleKeyDown}
|
|
75
|
+
onClick={(e) => e.stopPropagation()}
|
|
76
|
+
getPopupContainer={(trigger) => trigger.parentNode}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<Space style={{ width: '100%', justifyContent: 'flex-end' }}>
|
|
81
|
+
<Button size="small" onClick={onCancel} disabled={loading}>
|
|
82
|
+
Zrušit
|
|
83
|
+
</Button>
|
|
84
|
+
<Button
|
|
85
|
+
type="primary"
|
|
86
|
+
size="small"
|
|
87
|
+
onClick={() => onSubmit(value)}
|
|
88
|
+
loading={loading}
|
|
89
|
+
>
|
|
90
|
+
Použít
|
|
91
|
+
</Button>
|
|
92
|
+
</Space>
|
|
93
|
+
</>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// React.memo optimalizace
|
|
98
|
+
// ✅ PERFORMANCE FIX: Ignore callback references
|
|
99
|
+
const arePropsEqual = createMemoComparison(
|
|
100
|
+
['value', 'loading', 'options', 'placeholder', 'allowClear'], // Kritické props
|
|
101
|
+
['onChange', 'onSubmit', 'onCancel'], // Ignorované callbacky
|
|
102
|
+
false,
|
|
103
|
+
'BulkEditSelect'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
export default React.memo(BulkEditSelect, arePropsEqual);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import { default as DatePicker } from 'antd/es/date-picker';
|
|
4
|
+
import { useGridCellEditor } from 'ag-grid-react';
|
|
5
|
+
import dayjs from 'dayjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* DatePickerEditor - AG Grid cell editor s Ant Design DatePicker
|
|
9
|
+
* Automaticky se otevře při vstupu do editačního módu
|
|
10
|
+
* Podporuje formátování data a validaci
|
|
11
|
+
* Správně ukládá hodnotu při zavření pickeru nebo přechodu na jinou buňku
|
|
12
|
+
*/
|
|
13
|
+
const DatePickerEditor = React.forwardRef(({ value, initialValue, onValueChange, stopEditing, format = 'DD.MM.YYYY' }, ref) => {
|
|
14
|
+
const [dateValue, setDateValue] = useState(value ? dayjs(value) : null);
|
|
15
|
+
const [open, setOpen] = useState(false);
|
|
16
|
+
|
|
17
|
+
// Expose getValue method to AG Grid - nutné pro správné uložení hodnoty
|
|
18
|
+
React.useImperativeHandle(ref, () => ({
|
|
19
|
+
getValue: () => {
|
|
20
|
+
return dateValue ? dateValue.toISOString() : null;
|
|
21
|
+
}
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// AG-Grid callback - zrušit editaci pokud se hodnota nezměnila
|
|
25
|
+
useGridCellEditor({
|
|
26
|
+
isCancelAfterEnd: () => {
|
|
27
|
+
return !dateValue || (initialValue && dateValue.isSame(dayjs(initialValue)));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Auto-open picker při mount
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setOpen(true);
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const handleChange = (newDate) => {
|
|
37
|
+
setDateValue(newDate);
|
|
38
|
+
if (onValueChange) {
|
|
39
|
+
onValueChange(newDate ? newDate.toISOString() : null);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleOk = () => {
|
|
44
|
+
stopEditing();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleOpenChange = (newOpen) => {
|
|
48
|
+
setOpen(newOpen);
|
|
49
|
+
// Když se picker zavře (uživatel klikl mimo), ukončíme editaci
|
|
50
|
+
if (!newOpen) {
|
|
51
|
+
// Malé zpoždění aby se hodnota stihla uložit
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
stopEditing();
|
|
54
|
+
}, 0);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<DatePicker
|
|
60
|
+
value={dateValue}
|
|
61
|
+
onChange={handleChange}
|
|
62
|
+
onOk={handleOk}
|
|
63
|
+
format={format}
|
|
64
|
+
style={{ width: '100%' }}
|
|
65
|
+
open={open}
|
|
66
|
+
onOpenChange={handleOpenChange}
|
|
67
|
+
autoFocus
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
DatePickerEditor.displayName = 'DatePickerEditor';
|
|
73
|
+
|
|
74
|
+
export default DatePickerEditor;
|
|
75
|
+
/* eslint-enable */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default as DatePickerEditor} from './DatePickerEditor';
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { createMemoComparison } from '@bit.rhplus/react-memo';
|
|
4
|
-
|
|
5
|
-
function ImageRenderer(props) {
|
|
6
|
-
const { data, value, colDef: { imageRendererParams = {} } = {} } = props;
|
|
7
|
-
|
|
8
|
-
if (!imageRendererParams) return null;
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
cellAlign = 'center',
|
|
12
|
-
visibleGetter,
|
|
13
|
-
showOnGroup = false,
|
|
14
|
-
imageUrl,
|
|
15
|
-
fallbackText = true,
|
|
16
|
-
alt,
|
|
17
|
-
imageSize = 24,
|
|
18
|
-
imageStyle,
|
|
19
|
-
} = imageRendererParams;
|
|
20
|
-
|
|
21
|
-
const [hasError, setHasError] = React.useState(false);
|
|
22
|
-
|
|
23
|
-
const visibleResult = React.useMemo(() =>
|
|
24
|
-
visibleGetter ? visibleGetter(data) : true,
|
|
25
|
-
[visibleGetter, data]
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
const computedImageUrl = React.useMemo(() => {
|
|
29
|
-
if (!imageUrl) return null;
|
|
30
|
-
|
|
31
|
-
if (typeof imageUrl === 'function') {
|
|
32
|
-
return imageUrl({ data, value, props });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return imageUrl;
|
|
36
|
-
}, [imageUrl, data, value, props]);
|
|
37
|
-
|
|
38
|
-
const handleImageError = React.useCallback(() => {
|
|
39
|
-
setHasError(true);
|
|
40
|
-
}, []);
|
|
41
|
-
|
|
42
|
-
React.useEffect(() => {
|
|
43
|
-
setHasError(false);
|
|
44
|
-
}, [computedImageUrl]);
|
|
45
|
-
|
|
46
|
-
const showCondition = React.useMemo(() => {
|
|
47
|
-
const newItem = (data && data._rh_plus_ag_grid_new_item) || false;
|
|
48
|
-
return !newItem && (showOnGroup || !!data) && visibleResult;
|
|
49
|
-
}, [data, showOnGroup, visibleResult]);
|
|
50
|
-
|
|
51
|
-
const containerStyle = React.useMemo(() => ({
|
|
52
|
-
width: '100%',
|
|
53
|
-
display: 'flex',
|
|
54
|
-
justifyContent: cellAlign || 'center',
|
|
55
|
-
alignItems: 'center',
|
|
56
|
-
height: '100%',
|
|
57
|
-
}), [cellAlign]);
|
|
58
|
-
|
|
59
|
-
const finalImageStyle = React.useMemo(() => ({
|
|
60
|
-
width: imageSize,
|
|
61
|
-
height: imageSize,
|
|
62
|
-
objectFit: 'contain',
|
|
63
|
-
...(typeof imageStyle === 'function' ? imageStyle({ data, value, props }) : imageStyle || {}),
|
|
64
|
-
}), [imageSize, imageStyle, data, value, props]);
|
|
65
|
-
|
|
66
|
-
if (!showCondition) return null;
|
|
67
|
-
|
|
68
|
-
if (hasError || !computedImageUrl) {
|
|
69
|
-
if (fallbackText && value) {
|
|
70
|
-
return (
|
|
71
|
-
<span style={containerStyle}>
|
|
72
|
-
{value}
|
|
73
|
-
</span>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<span style={containerStyle}>
|
|
81
|
-
<img
|
|
82
|
-
src={computedImageUrl}
|
|
83
|
-
alt={alt || value || 'Image'}
|
|
84
|
-
style={finalImageStyle}
|
|
85
|
-
onError={handleImageError}
|
|
86
|
-
loading="lazy"
|
|
87
|
-
/>
|
|
88
|
-
</span>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
ImageRenderer.displayName = 'ImageRenderer';
|
|
93
|
-
|
|
94
|
-
// React.memo optimalizace pro AG-Grid renderer
|
|
95
|
-
// ✅ PERFORMANCE FIX: colDef NESMÍ být v kritických props!
|
|
96
|
-
const arePropsEqual = createMemoComparison(
|
|
97
|
-
['value', 'data'],
|
|
98
|
-
['colDef'],
|
|
99
|
-
false,
|
|
100
|
-
'ImageRenderer'
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
export default React.memo(ImageRenderer, arePropsEqual);
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { createMemoComparison } from '@bit.rhplus/react-memo';
|
|
4
|
+
|
|
5
|
+
function ImageRenderer(props) {
|
|
6
|
+
const { data, value, colDef: { imageRendererParams = {} } = {} } = props;
|
|
7
|
+
|
|
8
|
+
if (!imageRendererParams) return null;
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
cellAlign = 'center',
|
|
12
|
+
visibleGetter,
|
|
13
|
+
showOnGroup = false,
|
|
14
|
+
imageUrl,
|
|
15
|
+
fallbackText = true,
|
|
16
|
+
alt,
|
|
17
|
+
imageSize = 24,
|
|
18
|
+
imageStyle,
|
|
19
|
+
} = imageRendererParams;
|
|
20
|
+
|
|
21
|
+
const [hasError, setHasError] = React.useState(false);
|
|
22
|
+
|
|
23
|
+
const visibleResult = React.useMemo(() =>
|
|
24
|
+
visibleGetter ? visibleGetter(data) : true,
|
|
25
|
+
[visibleGetter, data]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const computedImageUrl = React.useMemo(() => {
|
|
29
|
+
if (!imageUrl) return null;
|
|
30
|
+
|
|
31
|
+
if (typeof imageUrl === 'function') {
|
|
32
|
+
return imageUrl({ data, value, props });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return imageUrl;
|
|
36
|
+
}, [imageUrl, data, value, props]);
|
|
37
|
+
|
|
38
|
+
const handleImageError = React.useCallback(() => {
|
|
39
|
+
setHasError(true);
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
setHasError(false);
|
|
44
|
+
}, [computedImageUrl]);
|
|
45
|
+
|
|
46
|
+
const showCondition = React.useMemo(() => {
|
|
47
|
+
const newItem = (data && data._rh_plus_ag_grid_new_item) || false;
|
|
48
|
+
return !newItem && (showOnGroup || !!data) && visibleResult;
|
|
49
|
+
}, [data, showOnGroup, visibleResult]);
|
|
50
|
+
|
|
51
|
+
const containerStyle = React.useMemo(() => ({
|
|
52
|
+
width: '100%',
|
|
53
|
+
display: 'flex',
|
|
54
|
+
justifyContent: cellAlign || 'center',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
height: '100%',
|
|
57
|
+
}), [cellAlign]);
|
|
58
|
+
|
|
59
|
+
const finalImageStyle = React.useMemo(() => ({
|
|
60
|
+
width: imageSize,
|
|
61
|
+
height: imageSize,
|
|
62
|
+
objectFit: 'contain',
|
|
63
|
+
...(typeof imageStyle === 'function' ? imageStyle({ data, value, props }) : imageStyle || {}),
|
|
64
|
+
}), [imageSize, imageStyle, data, value, props]);
|
|
65
|
+
|
|
66
|
+
if (!showCondition) return null;
|
|
67
|
+
|
|
68
|
+
if (hasError || !computedImageUrl) {
|
|
69
|
+
if (fallbackText && value) {
|
|
70
|
+
return (
|
|
71
|
+
<span style={containerStyle}>
|
|
72
|
+
{value}
|
|
73
|
+
</span>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<span style={containerStyle}>
|
|
81
|
+
<img
|
|
82
|
+
src={computedImageUrl}
|
|
83
|
+
alt={alt || value || 'Image'}
|
|
84
|
+
style={finalImageStyle}
|
|
85
|
+
onError={handleImageError}
|
|
86
|
+
loading="lazy"
|
|
87
|
+
/>
|
|
88
|
+
</span>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ImageRenderer.displayName = 'ImageRenderer';
|
|
93
|
+
|
|
94
|
+
// React.memo optimalizace pro AG-Grid renderer
|
|
95
|
+
// ✅ PERFORMANCE FIX: colDef NESMÍ být v kritických props!
|
|
96
|
+
const arePropsEqual = createMemoComparison(
|
|
97
|
+
['value', 'data'],
|
|
98
|
+
['colDef'],
|
|
99
|
+
false,
|
|
100
|
+
'ImageRenderer'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export default React.memo(ImageRenderer, arePropsEqual);
|