@bit.rhplus/ui.f7.detail-item 0.0.2 → 0.0.3
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/components/Category.jsx +167 -0
- package/components/Contact.jsx +105 -0
- package/components/Custom.jsx +209 -4
- package/components/Download.jsx +32 -7
- package/components/Grid.jsx +230 -0
- package/components/InputNumber.jsx +17 -2
- package/components/InputText.jsx +17 -2
- package/components/List.jsx +305 -0
- package/components/Select.jsx +199 -0
- package/components/Switch.jsx +12 -1
- package/components/Text.jsx +36 -3
- package/components/index.jsx +6 -1
- package/dist/components/Category.d.ts +13 -0
- package/dist/components/Category.js +86 -0
- package/dist/components/Category.js.map +1 -0
- package/dist/components/Contact.d.ts +10 -0
- package/dist/components/Contact.js +60 -0
- package/dist/components/Contact.js.map +1 -0
- package/dist/components/Custom.d.ts +19 -2
- package/dist/components/Custom.js +114 -5
- package/dist/components/Custom.js.map +1 -1
- package/dist/components/Download.d.ts +5 -1
- package/dist/components/Download.js +19 -3
- package/dist/components/Download.js.map +1 -1
- package/dist/components/Grid.d.ts +19 -0
- package/dist/components/Grid.js +144 -0
- package/dist/components/Grid.js.map +1 -0
- package/dist/components/InputNumber.d.ts +3 -1
- package/dist/components/InputNumber.js +16 -2
- package/dist/components/InputNumber.js.map +1 -1
- package/dist/components/InputText.d.ts +3 -1
- package/dist/components/InputText.js +16 -2
- package/dist/components/InputText.js.map +1 -1
- package/dist/components/List.d.ts +20 -0
- package/dist/components/List.js +173 -0
- package/dist/components/List.js.map +1 -0
- package/dist/components/Select.d.ts +13 -0
- package/dist/components/Select.js +89 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/Switch.d.ts +3 -1
- package/dist/components/Switch.js +11 -2
- package/dist/components/Switch.js.map +1 -1
- package/dist/components/Text.d.ts +8 -1
- package/dist/components/Text.js +25 -3
- package/dist/components/Text.js.map +1 -1
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.js +5 -0
- package/dist/components/index.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +18 -11
- package/dist/index.js.map +1 -1
- package/index.jsx +109 -62
- package/package.json +6 -3
- /package/dist/{preview-1756999926762.js → preview-1757077532569.js} +0 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
Link,
|
|
5
|
+
Icon,
|
|
6
|
+
Popup,
|
|
7
|
+
Navbar,
|
|
8
|
+
NavLeft,
|
|
9
|
+
NavTitle,
|
|
10
|
+
NavTitleLarge,
|
|
11
|
+
NavRight,
|
|
12
|
+
Page,
|
|
13
|
+
Block,
|
|
14
|
+
List,
|
|
15
|
+
ListInput,
|
|
16
|
+
Button
|
|
17
|
+
} from 'framework7-react';
|
|
18
|
+
import { Table } from 'lucide-react';
|
|
19
|
+
import AgGrid from '@bit.rhplus/ui.grid';
|
|
20
|
+
import SaveButton from '@bit.rhplus/ui.f7.save-button';
|
|
21
|
+
|
|
22
|
+
// Grid komponenta s modální editací - zobrazuje AG Grid pro výběr položky
|
|
23
|
+
export const Grid = ({
|
|
24
|
+
children,
|
|
25
|
+
value,
|
|
26
|
+
onSave,
|
|
27
|
+
onChange, // Přidáno pro Form.Item kompatibilitu
|
|
28
|
+
title = 'Editace výběru',
|
|
29
|
+
placeholder = 'Vyberte položku',
|
|
30
|
+
color = '#6887d3',
|
|
31
|
+
size = 16,
|
|
32
|
+
lucideIcon, // Lucide React ikona (např. Table)
|
|
33
|
+
icon, // Jakákoliv React komponenta ikony
|
|
34
|
+
rowData = [], // Array dat pro grid
|
|
35
|
+
columnDefs = [], // Definice sloupců pro AG Grid
|
|
36
|
+
gridOptions = {}, // Dodatečné možnosti pro AG Grid
|
|
37
|
+
onRowDoubleClicked, // Callback pro double click na řádek
|
|
38
|
+
gridHeight = '400px', // Výška gridu
|
|
39
|
+
selectionMode = 'single', // 'single' nebo 'multiple'
|
|
40
|
+
displayField = 'name' // Pole, které se zobrazí v detail-item při výběru
|
|
41
|
+
}) => {
|
|
42
|
+
const [popupOpened, setPopupOpened] = useState(false);
|
|
43
|
+
// Inicializuj selectedRows pouze jednou při mount
|
|
44
|
+
const initialSelectedRows = useMemo(() => {
|
|
45
|
+
return value ? (Array.isArray(value) ? value : [value]) : [];
|
|
46
|
+
}, []); // Prázdné dependency array - pouze při mount
|
|
47
|
+
|
|
48
|
+
const [selectedRows, setSelectedRows] = useState(initialSelectedRows);
|
|
49
|
+
|
|
50
|
+
const linkStyle = {
|
|
51
|
+
color,
|
|
52
|
+
cursor: 'pointer',
|
|
53
|
+
display: 'flex',
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
gap: '6px'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handleCancel = () => {
|
|
59
|
+
// Při zrušení resetuj na původní hodnoty
|
|
60
|
+
const originalValue = value ? (Array.isArray(value) ? value : [value]) : [];
|
|
61
|
+
setSelectedRows(originalValue);
|
|
62
|
+
setPopupOpened(false);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleOpenPopup = () => {
|
|
66
|
+
// Při otevření nastav aktuální hodnotu
|
|
67
|
+
const currentValue = value ? (Array.isArray(value) ? value : [value]) : [];
|
|
68
|
+
setSelectedRows(currentValue);
|
|
69
|
+
setPopupOpened(true);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleRowSelection = (event) => {
|
|
73
|
+
console.log('Grid selection changed - event:', event);
|
|
74
|
+
|
|
75
|
+
if (event && event.api) {
|
|
76
|
+
const selectedNodes = event.api.getSelectedNodes();
|
|
77
|
+
const selectedData = selectedNodes.map(node => node.data);
|
|
78
|
+
|
|
79
|
+
console.log('Grid selection changed:', {
|
|
80
|
+
selectedNodes: selectedNodes.length,
|
|
81
|
+
selectedData,
|
|
82
|
+
previousSelectedRows: selectedRows
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Pro oba režimy pouze aktualizujeme stav - uložení bude přes Save button
|
|
86
|
+
setSelectedRows(selectedData);
|
|
87
|
+
} else {
|
|
88
|
+
console.log('Grid API not available in event');
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleSaveSelection = () => {
|
|
93
|
+
// Uložení vybraných položek pro oba režimy
|
|
94
|
+
let result;
|
|
95
|
+
|
|
96
|
+
if (selectionMode === 'single') {
|
|
97
|
+
result = selectedRows.length > 0 ? selectedRows[0] : null;
|
|
98
|
+
} else {
|
|
99
|
+
result = selectedRows.length === 1 ? selectedRows[0] : selectedRows;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Debug pouze při uložení
|
|
103
|
+
console.log('Saving Grid selection:', { selectedRows, result, selectionMode, hasOnChange: !!onChange, hasOnSave: !!onSave });
|
|
104
|
+
|
|
105
|
+
if (onChange) {
|
|
106
|
+
onChange(result);
|
|
107
|
+
console.log('Grid - onChange called with:', result);
|
|
108
|
+
} else if (onSave) {
|
|
109
|
+
onSave(result);
|
|
110
|
+
console.log('Grid - onSave called with:', result);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setPopupOpened(false);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// Určí jakou ikonu použít - priorita: icon > lucideIcon > výchozí Table
|
|
118
|
+
const renderIcon = () => {
|
|
119
|
+
if (icon) {
|
|
120
|
+
return React.cloneElement(icon, { size, color, ...icon.props });
|
|
121
|
+
}
|
|
122
|
+
if (lucideIcon) {
|
|
123
|
+
const LucideIcon = lucideIcon;
|
|
124
|
+
return <LucideIcon size={size} color={color} />;
|
|
125
|
+
}
|
|
126
|
+
return <Table size={size} color={color} />;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
// Kombinované grid options
|
|
131
|
+
const combinedGridOptions = {
|
|
132
|
+
rowSelection: selectionMode,
|
|
133
|
+
onSelectionChanged: handleRowSelection,
|
|
134
|
+
onRowDoubleClicked: onRowDoubleClicked,
|
|
135
|
+
suppressRowClickSelection: false, // Umožní výběr kliknutím na řádek pro oba režimy
|
|
136
|
+
...gridOptions
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
<Link onClick={handleOpenPopup} className="link" style={linkStyle}>
|
|
142
|
+
{renderIcon()}
|
|
143
|
+
{(() => {
|
|
144
|
+
// Použij value prop pro zobrazení (value obsahuje uloženou hodnotu)
|
|
145
|
+
const currentValue = value ? (Array.isArray(value) ? value : [value]) : [];
|
|
146
|
+
|
|
147
|
+
if (currentValue.length === 1) {
|
|
148
|
+
// Pokud je vybraná jedna položka, zobraz hodnotu z displayField
|
|
149
|
+
const item = currentValue[0];
|
|
150
|
+
return item[displayField] || item.name || item.title || item.id || 'Vybraná položka';
|
|
151
|
+
} else if (currentValue.length > 1) {
|
|
152
|
+
return `${currentValue.length} vybraných položek`;
|
|
153
|
+
}
|
|
154
|
+
return placeholder;
|
|
155
|
+
})()}
|
|
156
|
+
</Link>
|
|
157
|
+
|
|
158
|
+
<Popup
|
|
159
|
+
opened={popupOpened}
|
|
160
|
+
onPopupClosed={() => setPopupOpened(false)}
|
|
161
|
+
animate
|
|
162
|
+
backdrop
|
|
163
|
+
push={false}
|
|
164
|
+
className="f7-parallax grid-popup"
|
|
165
|
+
style={{
|
|
166
|
+
'--f7-popup-tablet-width': '90vw',
|
|
167
|
+
'--f7-popup-tablet-height': '90vh'
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
<div className="view view-init">
|
|
171
|
+
<div className="page page-with-navbar-large">
|
|
172
|
+
<Navbar large className="navbar-transparent">
|
|
173
|
+
<NavLeft>
|
|
174
|
+
<Link onClick={handleCancel}>
|
|
175
|
+
<Icon f7="arrow_left" style={{ fontWeight: 'bold' }} />
|
|
176
|
+
</Link>
|
|
177
|
+
</NavLeft>
|
|
178
|
+
<NavTitle>Výběr</NavTitle>
|
|
179
|
+
<NavTitleLarge>Vyberte položky</NavTitleLarge>
|
|
180
|
+
</Navbar>
|
|
181
|
+
|
|
182
|
+
<div className="page-content">
|
|
183
|
+
{/* AG Grid */}
|
|
184
|
+
<Block style={{
|
|
185
|
+
flex: 1,
|
|
186
|
+
display: 'flex',
|
|
187
|
+
flexDirection: 'column',
|
|
188
|
+
height: 'calc(100vh - 230px)', // Více prostoru pro SaveButton
|
|
189
|
+
padding: '0'
|
|
190
|
+
}}>
|
|
191
|
+
<div
|
|
192
|
+
className="ag-theme-quartz"
|
|
193
|
+
style={{
|
|
194
|
+
height: '100%',
|
|
195
|
+
width: '100%',
|
|
196
|
+
flex: 1,
|
|
197
|
+
'--ag-background-color': 'transparent',
|
|
198
|
+
'--ag-odd-row-background-color': 'transparent'
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
<AgGrid
|
|
202
|
+
columnDefs={columnDefs}
|
|
203
|
+
rowData={rowData}
|
|
204
|
+
gridOptions={{
|
|
205
|
+
...combinedGridOptions,
|
|
206
|
+
popupParent: document.body, // Popup menu se zobrazí v body, ne v popup
|
|
207
|
+
rowHeight: 48, // Explicitní výška řádku
|
|
208
|
+
headerHeight: 56 // Explicitní výška hlavičky
|
|
209
|
+
}}
|
|
210
|
+
height="100%"
|
|
211
|
+
// appearance="ag-theme-quartz"
|
|
212
|
+
defaultColDef={{
|
|
213
|
+
sortable: true,
|
|
214
|
+
filter: true,
|
|
215
|
+
resizable: true,
|
|
216
|
+
minWidth: 100, // Minimální šířka sloupců pro mobil
|
|
217
|
+
...gridOptions.defaultColDef
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
</Block>
|
|
222
|
+
|
|
223
|
+
<SaveButton onClick={handleSaveSelection} variant="black" />
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</Popup>
|
|
228
|
+
</>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ListInput,
|
|
16
16
|
Button
|
|
17
17
|
} from 'framework7-react';
|
|
18
|
+
import { Hash } from 'lucide-react';
|
|
18
19
|
import SaveButton from '@bit.rhplus/ui.f7.save-button';
|
|
19
20
|
|
|
20
21
|
// InputNumber komponenta s modální editací číselných hodnot kompatibilní s Ant Design Form
|
|
@@ -29,7 +30,9 @@ export const InputNumber = ({
|
|
|
29
30
|
size = 16,
|
|
30
31
|
min,
|
|
31
32
|
max,
|
|
32
|
-
step = 1
|
|
33
|
+
step = 1,
|
|
34
|
+
lucideIcon, // Lucide React ikona
|
|
35
|
+
icon // Jakákoliv React komponenta ikony
|
|
33
36
|
}) => {
|
|
34
37
|
const [popupOpened, setPopupOpened] = useState(false);
|
|
35
38
|
const [inputValue, setInputValue] = useState(value || children || '');
|
|
@@ -83,10 +86,22 @@ export const InputNumber = ({
|
|
|
83
86
|
}
|
|
84
87
|
};
|
|
85
88
|
|
|
89
|
+
// Určí jakou ikonu použít - priorita: icon > lucideIcon > výchozí Hash
|
|
90
|
+
const renderIcon = () => {
|
|
91
|
+
if (icon) {
|
|
92
|
+
return React.cloneElement(icon, { size, color, ...icon.props });
|
|
93
|
+
}
|
|
94
|
+
if (lucideIcon) {
|
|
95
|
+
const LucideIcon = lucideIcon;
|
|
96
|
+
return <LucideIcon size={size} color={color} />;
|
|
97
|
+
}
|
|
98
|
+
return <Hash size={size} color={color} />;
|
|
99
|
+
};
|
|
100
|
+
|
|
86
101
|
return (
|
|
87
102
|
<>
|
|
88
103
|
<Link onClick={() => setPopupOpened(true)} className="link" style={linkStyle}>
|
|
89
|
-
|
|
104
|
+
{renderIcon()}
|
|
90
105
|
{(value !== null && value !== undefined && value !== '') ? (value || children) : placeholder}
|
|
91
106
|
</Link>
|
|
92
107
|
|
package/components/InputText.jsx
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ListInput,
|
|
16
16
|
Button
|
|
17
17
|
} from 'framework7-react';
|
|
18
|
+
import { Pencil } from 'lucide-react';
|
|
18
19
|
import SaveButton from '@bit.rhplus/ui.f7.save-button';
|
|
19
20
|
|
|
20
21
|
// Input komponenta s modální editací kompatibilní s Ant Design Form
|
|
@@ -26,7 +27,9 @@ export const InputText = ({
|
|
|
26
27
|
title = 'Editace',
|
|
27
28
|
placeholder = 'Zadejte hodnotu',
|
|
28
29
|
color = '#6887d3',
|
|
29
|
-
size = 16
|
|
30
|
+
size = 16,
|
|
31
|
+
lucideIcon, // Lucide React ikona (např. Pencil, Edit3)
|
|
32
|
+
icon // Jakákoliv React komponenta ikony
|
|
30
33
|
}) => {
|
|
31
34
|
const [popupOpened, setPopupOpened] = useState(false);
|
|
32
35
|
const [inputValue, setInputValue] = useState(value || children || '');
|
|
@@ -61,10 +64,22 @@ export const InputText = ({
|
|
|
61
64
|
setPopupOpened(false);
|
|
62
65
|
};
|
|
63
66
|
|
|
67
|
+
// Určí jakou ikonu použít - priorita: icon > lucideIcon > výchozí Pencil
|
|
68
|
+
const renderIcon = () => {
|
|
69
|
+
if (icon) {
|
|
70
|
+
return React.cloneElement(icon, { size, color, ...icon.props });
|
|
71
|
+
}
|
|
72
|
+
if (lucideIcon) {
|
|
73
|
+
const LucideIcon = lucideIcon;
|
|
74
|
+
return <LucideIcon size={size} color={color} />;
|
|
75
|
+
}
|
|
76
|
+
return <Pencil size={size} color={color} />;
|
|
77
|
+
};
|
|
78
|
+
|
|
64
79
|
return (
|
|
65
80
|
<>
|
|
66
81
|
<Link onClick={() => setPopupOpened(true)} className="link" style={linkStyle}>
|
|
67
|
-
|
|
82
|
+
{renderIcon()}
|
|
68
83
|
{(value !== null && value !== undefined && value !== '') ? (value || children) : placeholder}
|
|
69
84
|
</Link>
|
|
70
85
|
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
Link,
|
|
5
|
+
Icon,
|
|
6
|
+
Popup,
|
|
7
|
+
Navbar,
|
|
8
|
+
NavLeft,
|
|
9
|
+
NavTitle,
|
|
10
|
+
NavTitleLarge,
|
|
11
|
+
NavRight,
|
|
12
|
+
Page,
|
|
13
|
+
Block,
|
|
14
|
+
List as F7List,
|
|
15
|
+
ListItem,
|
|
16
|
+
Card,
|
|
17
|
+
Button
|
|
18
|
+
} from 'framework7-react';
|
|
19
|
+
import { List as ListIcon } from 'lucide-react';
|
|
20
|
+
import SaveButton from '@bit.rhplus/ui.f7.save-button';
|
|
21
|
+
|
|
22
|
+
// List komponenta s modální editací - zobrazuje seznam jako ListItem komponenty
|
|
23
|
+
export const List = ({
|
|
24
|
+
children,
|
|
25
|
+
value,
|
|
26
|
+
onSave,
|
|
27
|
+
onChange, // Přidáno pro Form.Item kompatibilitu
|
|
28
|
+
title = 'Editace výběru',
|
|
29
|
+
placeholder = 'Vyberte položku',
|
|
30
|
+
color = '#6887d3',
|
|
31
|
+
size = 16,
|
|
32
|
+
lucideIcon, // Lucide React ikona (např. List)
|
|
33
|
+
icon, // Jakákoliv React komponenta ikony
|
|
34
|
+
options = [], // Array možností pro výběr
|
|
35
|
+
selectionMode = 'single', // 'single' nebo 'multiple'
|
|
36
|
+
displayField = 'name', // Pole, které se zobrazí v detail-item při výběru
|
|
37
|
+
titleField = 'name', // Pole pro nadpis v ListItem
|
|
38
|
+
subtitleField = 'description', // Pole pro popis v ListItem
|
|
39
|
+
iconField = 'icon', // Pole pro ikonu v ListItem
|
|
40
|
+
colorField = 'color', // Pole pro barvu v ListItem
|
|
41
|
+
ItemRenderer // Custom komponenta pro renderování jednotlivých položek
|
|
42
|
+
}) => {
|
|
43
|
+
const [popupOpened, setPopupOpened] = useState(false);
|
|
44
|
+
// Inicializuj selectedItems pouze jednou při mount
|
|
45
|
+
const initialSelectedItems = useMemo(() => {
|
|
46
|
+
return value ? (Array.isArray(value) ? value : [value]) : [];
|
|
47
|
+
}, []); // Prázdné dependency array - pouze při mount
|
|
48
|
+
|
|
49
|
+
const [selectedItems, setSelectedItems] = useState(initialSelectedItems);
|
|
50
|
+
|
|
51
|
+
const linkStyle = {
|
|
52
|
+
color,
|
|
53
|
+
cursor: 'pointer',
|
|
54
|
+
display: 'flex',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
gap: '6px'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleCancel = () => {
|
|
60
|
+
const originalValue = value ? (Array.isArray(value) ? value : [value]) : [];
|
|
61
|
+
setSelectedItems(originalValue);
|
|
62
|
+
setPopupOpened(false);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleOpenPopup = () => {
|
|
66
|
+
// Reset selected items při otevření
|
|
67
|
+
const currentValue = value ? (Array.isArray(value) ? value : [value]) : [];
|
|
68
|
+
setSelectedItems(currentValue);
|
|
69
|
+
setPopupOpened(true);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const isSelected = (item) => {
|
|
73
|
+
return selectedItems.some(selected => selected.id === item.id);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const handleItemClick = (item) => {
|
|
77
|
+
if (selectionMode === 'single') {
|
|
78
|
+
// Single selection - přímé uložení položky bez použití state
|
|
79
|
+
if (onChange) {
|
|
80
|
+
onChange(item);
|
|
81
|
+
} else if (onSave) {
|
|
82
|
+
onSave(item);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Zavři popup
|
|
86
|
+
setPopupOpened(false);
|
|
87
|
+
return;
|
|
88
|
+
} else {
|
|
89
|
+
// Multiple selection - toggle výběr
|
|
90
|
+
let newSelection;
|
|
91
|
+
if (isSelected(item)) {
|
|
92
|
+
newSelection = selectedItems.filter(selected => selected.id !== item.id);
|
|
93
|
+
} else {
|
|
94
|
+
newSelection = [...selectedItems, item];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setSelectedItems(newSelection);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const handleSaveSelection = () => {
|
|
102
|
+
// Uložení vybraných položek pro oba režimy
|
|
103
|
+
let result;
|
|
104
|
+
|
|
105
|
+
if (selectionMode === 'single') {
|
|
106
|
+
result = selectedItems.length > 0 ? selectedItems[0] : null;
|
|
107
|
+
} else {
|
|
108
|
+
result = selectedItems.length === 1 ? selectedItems[0] : selectedItems;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (onChange) {
|
|
112
|
+
onChange(result);
|
|
113
|
+
} else if (onSave) {
|
|
114
|
+
onSave(result);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setPopupOpened(false);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Určí jakou ikonu použít - priorita: icon > lucideIcon > výchozí List
|
|
121
|
+
const renderIcon = () => {
|
|
122
|
+
if (icon) {
|
|
123
|
+
return React.cloneElement(icon, { size, color, ...icon.props });
|
|
124
|
+
}
|
|
125
|
+
if (lucideIcon) {
|
|
126
|
+
const LucideIcon = lucideIcon;
|
|
127
|
+
return <LucideIcon size={size} color={color} />;
|
|
128
|
+
}
|
|
129
|
+
return <ListIcon size={size} color={color} />;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Renderování ikony pro ListItem
|
|
133
|
+
const renderItemIcon = (item) => {
|
|
134
|
+
const itemIcon = item[iconField];
|
|
135
|
+
const itemColor = item[colorField] || color;
|
|
136
|
+
|
|
137
|
+
if (React.isValidElement(itemIcon)) {
|
|
138
|
+
return React.cloneElement(itemIcon, { size: 20, color: itemColor });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return <Icon f7="circle_fill" color={itemColor} size="20" />;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<>
|
|
146
|
+
<Link onClick={handleOpenPopup} className="link" style={linkStyle}>
|
|
147
|
+
{renderIcon()}
|
|
148
|
+
{(() => {
|
|
149
|
+
// Použij value prop pro zobrazení (ne selectedItems state)
|
|
150
|
+
const currentValue = value ? (Array.isArray(value) ? value : [value]) : [];
|
|
151
|
+
|
|
152
|
+
if (currentValue.length === 1) {
|
|
153
|
+
// Pokud je vybraná jedna položka, zobraz hodnotu z displayField
|
|
154
|
+
const item = currentValue[0];
|
|
155
|
+
return item[displayField] || item.name || item.title || item.id || 'Vybraná položka';
|
|
156
|
+
} else if (currentValue.length > 1) {
|
|
157
|
+
return `${currentValue.length} vybraných položek`;
|
|
158
|
+
}
|
|
159
|
+
return placeholder;
|
|
160
|
+
})()}
|
|
161
|
+
</Link>
|
|
162
|
+
|
|
163
|
+
<Popup
|
|
164
|
+
opened={popupOpened}
|
|
165
|
+
onPopupClosed={() => setPopupOpened(false)}
|
|
166
|
+
animate
|
|
167
|
+
backdrop
|
|
168
|
+
push={false}
|
|
169
|
+
className="f7-parallax list-popup"
|
|
170
|
+
style={{
|
|
171
|
+
'--f7-popup-tablet-width': '90vw',
|
|
172
|
+
'--f7-popup-tablet-height': '90vh'
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
<div className="view view-init">
|
|
176
|
+
<div className="page page-with-navbar-large">
|
|
177
|
+
<Navbar large className="navbar-transparent">
|
|
178
|
+
<NavLeft>
|
|
179
|
+
<Link onClick={handleCancel}>
|
|
180
|
+
<Icon f7="arrow_left" style={{ fontWeight: 'bold' }} />
|
|
181
|
+
</Link>
|
|
182
|
+
</NavLeft>
|
|
183
|
+
<NavTitle>Vyberte položku</NavTitle>
|
|
184
|
+
<NavTitleLarge>Vyberte položku</NavTitleLarge>
|
|
185
|
+
</Navbar>
|
|
186
|
+
|
|
187
|
+
<div className="page-content">
|
|
188
|
+
<Block style={{
|
|
189
|
+
padding: '10px 0'
|
|
190
|
+
}}>
|
|
191
|
+
<Card>
|
|
192
|
+
<F7List style={{
|
|
193
|
+
marginTop: '0',
|
|
194
|
+
marginBottom: '0'
|
|
195
|
+
}}>
|
|
196
|
+
{options.map((item, index) => {
|
|
197
|
+
const selected = isSelected(item);
|
|
198
|
+
|
|
199
|
+
// Pokud je předán custom ItemRenderer, použij ho
|
|
200
|
+
if (ItemRenderer) {
|
|
201
|
+
return (
|
|
202
|
+
<ItemRenderer
|
|
203
|
+
key={item.id || index}
|
|
204
|
+
item={item}
|
|
205
|
+
index={index}
|
|
206
|
+
selected={selected}
|
|
207
|
+
onItemClick={(e) => {
|
|
208
|
+
e.preventDefault();
|
|
209
|
+
e.stopPropagation();
|
|
210
|
+
handleItemClick(item);
|
|
211
|
+
}}
|
|
212
|
+
titleField={titleField}
|
|
213
|
+
subtitleField={subtitleField}
|
|
214
|
+
iconField={iconField}
|
|
215
|
+
colorField={colorField}
|
|
216
|
+
color={color}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Defaultní renderování
|
|
222
|
+
const title = item[titleField] || item.name || item.title || `Položka ${index + 1}`;
|
|
223
|
+
const subtitle = item[subtitleField] || item.description;
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<ListItem
|
|
227
|
+
key={item.id || index}
|
|
228
|
+
link="#"
|
|
229
|
+
noChevron
|
|
230
|
+
onClick={() => handleItemClick(item)}
|
|
231
|
+
style={{
|
|
232
|
+
'--f7-list-item-padding-horizontal': '15px',
|
|
233
|
+
'--f7-list-item-padding-vertical': '12px',
|
|
234
|
+
backgroundColor: selected ? '#f0f9ff' : 'transparent'
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
<div
|
|
238
|
+
slot="media"
|
|
239
|
+
style={{
|
|
240
|
+
display: 'flex',
|
|
241
|
+
alignItems: 'center',
|
|
242
|
+
marginRight: '12px'
|
|
243
|
+
}}
|
|
244
|
+
>
|
|
245
|
+
{renderItemIcon(item)}
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div
|
|
249
|
+
slot="inner"
|
|
250
|
+
style={{
|
|
251
|
+
display: 'flex',
|
|
252
|
+
flexDirection: 'column',
|
|
253
|
+
width: '100%',
|
|
254
|
+
minHeight: '50px'
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
{/* Title row */}
|
|
258
|
+
<div style={{
|
|
259
|
+
display: 'flex',
|
|
260
|
+
justifyContent: 'space-between',
|
|
261
|
+
alignItems: 'center',
|
|
262
|
+
marginBottom: subtitle ? '4px' : '0'
|
|
263
|
+
}}>
|
|
264
|
+
<span style={{
|
|
265
|
+
fontWeight: selected ? 600 : 500,
|
|
266
|
+
fontSize: '16px',
|
|
267
|
+
color: selected ? '#007aff' : '#000'
|
|
268
|
+
}}>
|
|
269
|
+
{title}
|
|
270
|
+
</span>
|
|
271
|
+
{selected && (
|
|
272
|
+
<Icon
|
|
273
|
+
f7="checkmark_circle_fill"
|
|
274
|
+
color="#007aff"
|
|
275
|
+
size="20"
|
|
276
|
+
/>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
{/* Subtitle */}
|
|
281
|
+
{subtitle && (
|
|
282
|
+
<div style={{
|
|
283
|
+
fontSize: '14px',
|
|
284
|
+
color: '#666',
|
|
285
|
+
overflow: 'hidden',
|
|
286
|
+
textOverflow: 'ellipsis',
|
|
287
|
+
whiteSpace: 'nowrap'
|
|
288
|
+
}}>
|
|
289
|
+
{subtitle}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
</ListItem>
|
|
294
|
+
);
|
|
295
|
+
})}
|
|
296
|
+
</F7List>
|
|
297
|
+
</Card>
|
|
298
|
+
</Block>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
</Popup>
|
|
303
|
+
</>
|
|
304
|
+
);
|
|
305
|
+
};
|