@bit.rhplus/ui.f7.detail-item 0.0.12 → 0.0.14

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.
@@ -0,0 +1,260 @@
1
+ /* eslint-disable */
2
+ import React, { useRef, useEffect, useState, useCallback } from 'react';
3
+ import { f7, Popup, Page, Navbar, NavLeft, NavTitle, NavTitleLarge, NavRight, Link, Block, Icon, Card } from 'framework7-react';
4
+ import { Calendar as CalendarIcon } from 'lucide-react';
5
+
6
+ export const Calendar = ({
7
+ value,
8
+ title,
9
+ onChange,
10
+ placeholder = 'Vyberte datum',
11
+ format = 'dd.MM.yyyy',
12
+ color,
13
+ style,
14
+ disabled = false,
15
+ lucideIcon = CalendarIcon,
16
+ icon,
17
+ iconSize = 16,
18
+ field = null,
19
+ calendarParams = {},
20
+ ...restProps
21
+ }) => {
22
+ const calendarRef = useRef(null);
23
+ const calendarContainerRef = useRef(null);
24
+ const isInitializingRef = useRef(false);
25
+ const [displayValue, setDisplayValue] = useState('');
26
+ const [popupOpened, setPopupOpened] = useState(false);
27
+ const [selectedDate, setSelectedDate] = useState(value ? new Date(value) : null);
28
+
29
+ // Převod hodnoty na display formát
30
+ const formatDate = (date) => {
31
+ if (!date) return '';
32
+ const d = new Date(date);
33
+ if (isNaN(d.getTime())) return '';
34
+
35
+ const day = String(d.getDate()).padStart(2, '0');
36
+ const month = String(d.getMonth() + 1).padStart(2, '0');
37
+ const year = d.getFullYear();
38
+
39
+ return `${day}.${month}.${year}`;
40
+ };
41
+
42
+ // Aktualizace display hodnoty při změně value
43
+ useEffect(() => {
44
+ setDisplayValue(formatDate(value));
45
+ setSelectedDate(value ? new Date(value) : null);
46
+ }, [value]);
47
+
48
+ // Otevření popup s kalendářem
49
+ const openCalendar = useCallback(() => {
50
+ if (disabled) return;
51
+ setPopupOpened(true);
52
+ }, [disabled]);
53
+
54
+ // Cleanup kalendáře při zavírání popup
55
+ useEffect(() => {
56
+ if (!popupOpened && calendarRef.current) {
57
+ calendarRef.current.destroy();
58
+ calendarRef.current = null;
59
+ // Vyčisti i DOM kontejner
60
+ if (calendarContainerRef.current) {
61
+ calendarContainerRef.current.innerHTML = '';
62
+ }
63
+ }
64
+ }, [popupOpened]);
65
+
66
+ // Inicializace kalendáře po otevření popup
67
+ useEffect(() => {
68
+ if (!popupOpened || !calendarContainerRef.current) return;
69
+
70
+ // Počkej na vykreslení popup
71
+ const timeoutId = setTimeout(() => {
72
+ if (!calendarContainerRef.current || !popupOpened) return;
73
+
74
+ // Vyčisti předchozí obsah
75
+ calendarContainerRef.current.innerHTML = '';
76
+
77
+ // Zničí existující kalendář pokud existuje
78
+ if (calendarRef.current) {
79
+ calendarRef.current.destroy();
80
+ calendarRef.current = null;
81
+ }
82
+
83
+ // Nastav flag že se kalendář inicializuje
84
+ isInitializingRef.current = true;
85
+
86
+ // Měsíce v češtině
87
+ const monthNames = [
88
+ 'Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen',
89
+ 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'
90
+ ];
91
+
92
+ // Dny v češtině
93
+ const dayNames = ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'];
94
+
95
+ const defaultParams = {
96
+ containerEl: calendarContainerRef.current,
97
+ value: selectedDate ? [selectedDate] : [],
98
+ // Nastavení výchozího roku a měsíce podle vybraného data
99
+ ...(selectedDate && {
100
+ currentYear: selectedDate.getFullYear(),
101
+ currentMonth: selectedDate.getMonth(),
102
+ currentDate: selectedDate
103
+ }),
104
+ monthNames,
105
+ monthNamesShort: monthNames.map(name => name.substring(0, 3)),
106
+ dayNames,
107
+ dayNamesShort: dayNames,
108
+ firstDay: 1, // Pondělí jako první den
109
+ on: {
110
+ change: (calendar, value) => {
111
+ // Ignoruj změny během inicializace
112
+ if (isInitializingRef.current) {
113
+ return;
114
+ }
115
+
116
+ if (value && value.length > 0) {
117
+ const newSelectedDate = value[0];
118
+ setSelectedDate(newSelectedDate);
119
+ // Automaticky potvrď a zavři popup při výběru data
120
+ if (onChange) {
121
+ onChange(newSelectedDate);
122
+ }
123
+ // Zavři popup s mírným zpožděním pro lepší UX
124
+ setTimeout(() => {
125
+ setPopupOpened(false);
126
+ }, 200);
127
+ }
128
+ }
129
+ },
130
+ ...calendarParams
131
+ };
132
+
133
+ calendarRef.current = f7.calendar.create(defaultParams);
134
+
135
+ // Po vytvoření kalendáře vypni inicializační flag
136
+ setTimeout(() => {
137
+ isInitializingRef.current = false;
138
+ }, 300);
139
+ }, 150);
140
+
141
+ return () => {
142
+ clearTimeout(timeoutId);
143
+ if (calendarRef.current) {
144
+ calendarRef.current.destroy();
145
+ calendarRef.current = null;
146
+ }
147
+ };
148
+ }, [popupOpened, selectedDate, calendarParams]);
149
+
150
+ // Potvrzení výběru data
151
+ const confirmSelection = useCallback(() => {
152
+ if (selectedDate && onChange) {
153
+ onChange(selectedDate);
154
+ }
155
+ setPopupOpened(false);
156
+ }, [selectedDate, onChange]);
157
+
158
+ // Zrušení výběru
159
+ const cancelSelection = useCallback(() => {
160
+ setSelectedDate(value ? new Date(value) : null);
161
+ setPopupOpened(false);
162
+ }, [value]);
163
+
164
+ const containerStyle = {
165
+ display: 'flex',
166
+ alignItems: 'center',
167
+ justifyContent: 'flex-end',
168
+ gap: '6px',
169
+ cursor: disabled ? 'not-allowed' : 'pointer',
170
+ opacity: disabled ? 0.6 : 1,
171
+ minWidth: '120px',
172
+ textAlign: 'right',
173
+ ...style
174
+ };
175
+
176
+ const displayStyle = {
177
+ border: 'none',
178
+ background: 'transparent',
179
+ outline: 'none',
180
+ fontSize: 'inherit',
181
+ color: '#6887d3',
182
+ cursor: disabled ? 'not-allowed' : 'pointer',
183
+ pointerEvents: 'none',
184
+ fontWeight: displayValue ? '500' : 'normal'
185
+ };
186
+
187
+ // Určí jakou ikonu použít
188
+ const renderIcon = () => {
189
+ const iconColor = '#6887d3';
190
+
191
+ if (icon) {
192
+ return React.cloneElement(icon, { size: iconSize, color: iconColor, ...icon.props });
193
+ }
194
+ if (lucideIcon) {
195
+ const LucideIcon = lucideIcon;
196
+ return <LucideIcon size={iconSize} color={iconColor} />;
197
+ }
198
+ return null;
199
+ };
200
+
201
+ return (
202
+ <>
203
+ <div style={containerStyle} onClick={openCalendar} {...restProps}>
204
+ {renderIcon()}
205
+ <span style={displayStyle}>
206
+ {displayValue || placeholder}
207
+ </span>
208
+ </div>
209
+
210
+ <Popup
211
+ opened={popupOpened}
212
+ onPopupClosed={cancelSelection}
213
+ animate
214
+ backdrop
215
+ push={false}
216
+ className="f7-parallax calendar-popup"
217
+ style={{
218
+ '--f7-popup-tablet-width': '90vw',
219
+ '--f7-popup-tablet-height': '90vh'
220
+ }}
221
+ >
222
+ <div className="view view-init">
223
+ <div className="page page-with-navbar-large">
224
+ <Navbar large>
225
+ <NavLeft>
226
+ <Link onClick={cancelSelection}>
227
+ <Icon f7="arrow_left" style={{ fontWeight: 'bold' }} />
228
+ </Link>
229
+ </NavLeft>
230
+ <NavTitle>{title}</NavTitle>
231
+ <NavTitleLarge>{title}</NavTitleLarge>
232
+ </Navbar>
233
+
234
+ <div className="page-content">
235
+ <Block
236
+ style={{
237
+ padding: '10px 0',
238
+ }}
239
+ >
240
+ <Card>
241
+ <Block style={{ padding: '20px' }}>
242
+ <div
243
+ ref={calendarContainerRef}
244
+ style={{
245
+ minHeight: '300px',
246
+ display: 'flex',
247
+ justifyContent: 'center',
248
+ alignItems: 'flex-start'
249
+ }}
250
+ />
251
+ </Block>
252
+ </Card>
253
+ </Block>
254
+ </div>
255
+ </div>
256
+ </div>
257
+ </Popup>
258
+ </>
259
+ );
260
+ };
@@ -16,7 +16,7 @@ import {
16
16
  Card,
17
17
  Button,
18
18
  } from 'framework7-react';
19
- import { List as ListIcon, Globe, MapPin } from 'lucide-react';
19
+ import { List as ListIcon, Globe, MapPin, Clock } from 'lucide-react';
20
20
  import SaveButton from '@bit.rhplus/ui.f7.save-button';
21
21
  import Filter from '@bit.rhplus/ui.f7.filter';
22
22
  import { Segmented } from '@bit.rhplus/ui.f7.segmented';
@@ -78,13 +78,11 @@ export const Project = ({
78
78
 
79
79
  // Pokud Places není dostupné, zkusíme je načíst
80
80
  if (!window.google.maps.places) {
81
- console.log('🔄 Places library není načtená, načítám...');
82
81
  try {
83
82
  // Dynamicky načteme Places library
84
83
  await new Promise((resolve, reject) => {
85
84
  if (window.google && window.google.maps && window.google.maps.importLibrary) {
86
85
  window.google.maps.importLibrary('places').then(() => {
87
- console.log('✅ Places library úspěšně načtena');
88
86
  resolve();
89
87
  }).catch(reject);
90
88
  } else {
@@ -92,7 +90,6 @@ export const Project = ({
92
90
  const script = document.createElement('script');
93
91
  script.src = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&libraries=places&v=weekly`;
94
92
  script.onload = () => {
95
- console.log('✅ Places library načtena přes script tag');
96
93
  resolve();
97
94
  };
98
95
  script.onerror = reject;
@@ -115,7 +112,6 @@ export const Project = ({
115
112
  zoom: 10
116
113
  });
117
114
  placesServiceRef.current = new window.google.maps.places.PlacesService(map);
118
- console.log('✅ Places Service připraven');
119
115
  return true;
120
116
  } catch (error) {
121
117
  console.error('❌ Chyba při inicializaci Places Service:', error);
@@ -129,10 +125,8 @@ export const Project = ({
129
125
 
130
126
  // Funkce pro vyhledávání Google Places
131
127
  const searchGooglePlaces = useCallback(async (query) => {
132
- console.log('🔍 searchGooglePlaces volána s:', query);
133
128
 
134
129
  if (!query || query.trim() === '') {
135
- console.log('❌ Prázdný dotaz');
136
130
  setGooglePlaces([]);
137
131
  setCurrentSearchText('');
138
132
  return;
@@ -143,12 +137,10 @@ export const Project = ({
143
137
 
144
138
  // Zkusíme inicializovat Places Service pokud není připraven
145
139
  if (!(await initPlacesServiceIfNeeded())) {
146
- console.log('❌ Nepodařilo se inicializovat Places Service');
147
140
  setGoogleError('Google Maps API není dostupné');
148
141
  return;
149
142
  }
150
143
 
151
- console.log('🚀 Spouštím Google Places search pro:', trimmedQuery);
152
144
  setGoogleLoading(true);
153
145
  setGoogleError(null);
154
146
 
@@ -163,11 +155,9 @@ export const Project = ({
163
155
  };
164
156
 
165
157
  autocompleteService.getPlacePredictions(request, (predictions, status) => {
166
- console.log('📥 Google Places odpověď:', { status, predictions });
167
158
  setGoogleLoading(false);
168
159
 
169
160
  if (status === window.google.maps.places.PlacesServiceStatus.OK && predictions) {
170
- console.log('✅ Nalezeno míst:', predictions.length);
171
161
  // Převedeme predictions na náš formát
172
162
  const places = predictions.slice(0, 9).map((prediction, index) => ({
173
163
  id: prediction.place_id,
@@ -192,10 +182,8 @@ export const Project = ({
192
182
 
193
183
  const finalPlaces = [exactTextItem, ...places];
194
184
 
195
- console.log('🏗️ Převedená místa s přesným textem:', finalPlaces);
196
185
  setGooglePlaces(finalPlaces);
197
186
  } else if (status === window.google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
198
- console.log('🔍 Žádné výsledky z Google, zobrazím jen přesný text');
199
187
  // I když Google nic nenajde, zobrazíme přesný text
200
188
  const exactTextItem = {
201
189
  id: `exact_text_${Date.now()}`,
@@ -309,9 +297,6 @@ export const Project = ({
309
297
  };
310
298
 
311
299
  const handleItemClick = (item) => {
312
- console.log('🎯 Project item clicked:', item);
313
- console.log('🔧 Field parameter:', field);
314
-
315
300
  if (selectionMode === 'single') {
316
301
  // Single selection - přímé uložení položky bez použití state
317
302
 
@@ -323,8 +308,6 @@ export const Project = ({
323
308
  country: item.country?.name || null
324
309
  };
325
310
 
326
- console.log('📤 Project sending return value:', returnValue);
327
-
328
311
  if (onChange) {
329
312
  onChange(returnValue);
330
313
  } else if (onSave) {
@@ -379,8 +362,6 @@ export const Project = ({
379
362
  returnValue = null;
380
363
  }
381
364
 
382
- console.log('📤 Multiple selection sending return value:', returnValue);
383
-
384
365
  if (onChange) {
385
366
  onChange(returnValue);
386
367
  } else if (onSave) {
@@ -392,9 +373,6 @@ export const Project = ({
392
373
 
393
374
  // Handle klik na Google Place
394
375
  const handleGooglePlaceClick = useCallback((place) => {
395
- console.log('🎯 Google Place clicked:', place);
396
- console.log('🔧 Field parameter:', field);
397
-
398
376
  // Převedeme Google Place na požadovaný formát {name, address, country} (bez key)
399
377
  const googlePlaceAsProject = {
400
378
  name: place.name,
@@ -402,20 +380,14 @@ export const Project = ({
402
380
  country: null // Google Places standardně neobsahují strukturované country info
403
381
  };
404
382
 
405
- console.log('🏗️ Created Google Place object:', googlePlaceAsProject);
406
-
407
383
  // Pro single selection mode - přímé uložení (stejně jako handleItemClick)
408
384
  if (selectionMode === 'single') {
409
385
  // Vrať objekt {name, address, country}
410
386
  const returnValue = googlePlaceAsProject;
411
387
 
412
- console.log('📤 Google Places sending return value:', returnValue);
413
-
414
388
  if (onChange) {
415
- console.log('✅ Calling onChange with:', returnValue);
416
389
  onChange(returnValue);
417
390
  } else if (onSave) {
418
- console.log('✅ Calling onSave with:', returnValue);
419
391
  onSave(returnValue);
420
392
  }
421
393
 
@@ -431,16 +403,13 @@ export const Project = ({
431
403
 
432
404
  // Handle filtrování - aplikuj filtry a informuj rodiče
433
405
  const handleFilterApply = useCallback((searchText, filters) => {
434
- console.log('🎯 handleFilterApply volána:', { searchText, filters, selectedTab });
435
406
  setActiveFilters({ searchText, filters });
436
407
 
437
408
  // Pro Google tab spusť vyhledávání Google Places
438
409
  if (selectedTab === 'google' && searchText && searchText.trim() !== '') {
439
- console.log('✅ Spouštím Google search pro tab:', selectedTab);
440
410
  searchGooglePlaces(searchText);
441
411
  } else if (selectedTab === 'google' && (!searchText || searchText.trim() === '')) {
442
412
  // Vymaž Google výsledky když je search prázdný
443
- console.log('🧹 Mažu Google výsledky');
444
413
  setGooglePlaces([]);
445
414
  }
446
415
 
@@ -554,7 +523,6 @@ export const Project = ({
554
523
  return <Icon f7="circle_fill" color={itemColor} size="20" />;
555
524
  };
556
525
 
557
- console.log('🚀 ~ value:', value)
558
526
  return (
559
527
  <>
560
528
  <Link onClick={handleOpenPopup} className="link" style={linkStyle}>
@@ -570,7 +538,6 @@ export const Project = ({
570
538
  if (typeof currentValue === 'object' && currentValue !== null) {
571
539
  // Zobraz name || address
572
540
  const displayValue = currentValue.name || currentValue.address || 'Vybraná hodnota';
573
- console.log('✅ Object value found - displaying:', displayValue);
574
541
  return displayValue;
575
542
  }
576
543
 
@@ -704,7 +671,7 @@ export const Project = ({
704
671
  onClick={() => handleItemClick(item)}
705
672
  style={{
706
673
  '--f7-list-item-padding-horizontal': '15px',
707
- '--f7-list-item-padding-vertical': '12px',
674
+ '--f7-list-item-padding-vertical': '0px',
708
675
  backgroundColor: selected
709
676
  ? '#f0f9ff'
710
677
  : 'transparent',
@@ -718,7 +685,19 @@ export const Project = ({
718
685
  marginRight: '12px',
719
686
  }}
720
687
  >
721
- {renderItemIcon(item)}
688
+ <div
689
+ style={{
690
+ width: '32px',
691
+ height: '32px',
692
+ borderRadius: '50%',
693
+ backgroundColor: '#e0e0e0',
694
+ display: 'flex',
695
+ alignItems: 'center',
696
+ justifyContent: 'center',
697
+ }}
698
+ >
699
+ <Clock size={16} color="#000" strokeWidth={2.5} />
700
+ </div>
722
701
  </div>
723
702
 
724
703
  <div
@@ -728,36 +707,32 @@ export const Project = ({
728
707
  flexDirection: 'column',
729
708
  width: '100%',
730
709
  minHeight: '50px',
710
+ paddingTop: '12px',
711
+ paddingBottom: '12px',
712
+ borderBottom: '1px solid #e0e0e0',
731
713
  }}
732
714
  >
733
- {/* Title row */}
715
+ {/* Title */}
734
716
  <div
735
717
  style={{
736
- display: 'flex',
737
- justifyContent: 'space-between',
738
- alignItems: 'center',
718
+ fontWeight: 500,
719
+ fontSize: '16px',
720
+ color: '#000',
739
721
  marginBottom: subtitle ? '4px' : '0',
740
722
  }}
741
723
  >
742
- <span
743
- style={{
744
- fontWeight: selected ? 600 : 500,
745
- fontSize: '16px',
746
- color: selected ? '#007aff' : '#000',
747
- }}
748
- >
749
- {title}
750
- </span>
724
+ {title}
751
725
  {selected && (
752
726
  <Icon
753
727
  f7="checkmark_circle_fill"
754
728
  color="#007aff"
755
729
  size="20"
730
+ style={{ marginLeft: '8px' }}
756
731
  />
757
732
  )}
758
733
  </div>
759
734
 
760
- {/* Subtitle */}
735
+ {/* Subtitle/Address */}
761
736
  {subtitle && (
762
737
  <div
763
738
  style={{
@@ -831,7 +806,7 @@ export const Project = ({
831
806
  onClick={() => handleGooglePlaceClick(place)}
832
807
  style={{
833
808
  '--f7-list-item-padding-horizontal': '15px',
834
- '--f7-list-item-padding-vertical': '12px',
809
+ '--f7-list-item-padding-vertical': '0px',
835
810
  }}
836
811
  >
837
812
  <div
@@ -842,11 +817,19 @@ export const Project = ({
842
817
  marginRight: '12px',
843
818
  }}
844
819
  >
845
- {place.source === 'google_exact' ? (
846
- <Icon f7="pencil" size={20} color="#007aff" />
847
- ) : (
848
- <MapPin size={20} color="#4285f4" />
849
- )}
820
+ <div
821
+ style={{
822
+ width: '32px',
823
+ height: '32px',
824
+ borderRadius: '50%',
825
+ backgroundColor: '#e0e0e0',
826
+ display: 'flex',
827
+ alignItems: 'center',
828
+ justifyContent: 'center',
829
+ }}
830
+ >
831
+ <Clock size={16} color="#000" strokeWidth={2.5} />
832
+ </div>
850
833
  </div>
851
834
 
852
835
  <div
@@ -856,47 +839,35 @@ export const Project = ({
856
839
  flexDirection: 'column',
857
840
  width: '100%',
858
841
  minHeight: '50px',
842
+ paddingTop: '12px',
843
+ paddingBottom: '12px',
844
+ borderBottom: '1px solid #e0e0e0',
859
845
  }}
860
846
  >
861
- {/* Title row */}
847
+ {/* Title */}
862
848
  <div
863
849
  style={{
864
- display: 'flex',
865
- justifyContent: 'space-between',
866
- alignItems: 'center',
867
- marginBottom: place.description ? '4px' : '0',
850
+ fontWeight: 500,
851
+ fontSize: '16px',
852
+ color: '#000',
853
+ marginBottom: '4px',
868
854
  }}
869
855
  >
870
- <span
871
- style={{
872
- fontWeight: 500,
873
- fontSize: '16px',
874
- color: '#000',
875
- }}
876
- >
877
- {place.name}
878
- </span>
879
- {place.source === 'google_exact' ? (
880
- <Icon f7="text_cursor" size={16} color="#007aff" />
881
- ) : (
882
- <Globe size={16} color="#4285f4" />
883
- )}
856
+ {place.name}
884
857
  </div>
885
858
 
886
859
  {/* Address */}
887
- {place.address && (
888
- <div
889
- style={{
890
- fontSize: '14px',
891
- color: '#666',
892
- overflow: 'hidden',
893
- textOverflow: 'ellipsis',
894
- whiteSpace: 'nowrap',
895
- }}
896
- >
897
- {place.address}
898
- </div>
899
- )}
860
+ <div
861
+ style={{
862
+ fontSize: '14px',
863
+ color: '#666',
864
+ overflow: 'hidden',
865
+ textOverflow: 'ellipsis',
866
+ whiteSpace: 'nowrap',
867
+ }}
868
+ >
869
+ {place.address}
870
+ </div>
900
871
  </div>
901
872
  </ListItem>
902
873
  ))}
@@ -11,4 +11,5 @@ export {Select} from './Select';
11
11
  export {Grid} from './Grid';
12
12
  export {List} from './List';
13
13
  export {CountryFlag} from './CountryFlag';
14
- export {Project} from './Project';
14
+ export {Project} from './Project';
15
+ export {Calendar} from './Calendar';
@@ -0,0 +1,17 @@
1
+ export function Calendar({ value, title, onChange, placeholder, format, color, style, disabled, lucideIcon, icon, iconSize, field, calendarParams, ...restProps }: {
2
+ [x: string]: any;
3
+ value: any;
4
+ title: any;
5
+ onChange: any;
6
+ placeholder?: string | undefined;
7
+ format?: string | undefined;
8
+ color: any;
9
+ style: any;
10
+ disabled?: boolean | undefined;
11
+ lucideIcon?: React.ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & React.RefAttributes<SVGSVGElement>> | undefined;
12
+ icon: any;
13
+ iconSize?: number | undefined;
14
+ field?: null | undefined;
15
+ calendarParams?: {} | undefined;
16
+ }): import("react/jsx-runtime").JSX.Element;
17
+ import React from 'react';