@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.
@@ -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);