@topconsultnpm/sdkui-react 6.22.0-dev2.47 → 6.22.0-dev2.48

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.
@@ -24,11 +24,9 @@ import TMToppyMessage from '../../../helper/TMToppyMessage';
24
24
  import TMMetadataSorterForm from './TMMetadataSorterForm';
25
25
  import TMMetadataOutputForm from './TMMetadataOutputForm';
26
26
  import TMTooltip from '../../base/TMTooltip';
27
- const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, inputMids, onSearchCompleted, onSqdSaved, onBack, onClosePanel, allowMaximize = true, onMaximizePanel, onBackToResult, passToArchiveCallback, maxDcmtsToBeReturned }) => {
27
+ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, inputMids, onSearchCompleted, onSqdSaved, onBack, onClosePanel, allowMaximize = true, onMaximizePanel, onBackToResult, passToArchiveCallback, maxDcmtsToBeReturned, onRefreshRequested }) => {
28
28
  const [confirmQueryParams, ConfirmQueryParamsDialog] = useQueryParametersDialog();
29
29
  const [qd, setQd] = useState();
30
- const [shouldSearch, setShouldSearch] = useState(false);
31
- const [isQueryPanelActive, setIsQueryPanelActive] = useState(false);
32
30
  const [lastQdParams, setLastQdParams] = useState([]);
33
31
  const [dcmtTypesList, setDcmtTypesList] = useState([]);
34
32
  const [showSqdForm, setShowSqdForm] = useState(false);
@@ -45,12 +43,18 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
45
43
  const isMobile = deviceType === DeviceType.MOBILE;
46
44
  let initialMaxItems = deviceType === DeviceType.MOBILE ? 8 : 12;
47
45
  const tmPanelRef = useRef(null);
46
+ // Contenitore del form: l'Invio avvia la ricerca solo se il focus si trova al suo interno
47
+ const contentRef = useRef(null);
48
48
  // Passando nella vista risultati si prenota il focus, che verrà ripreso al rientro nella ricerca
49
49
  const shouldRefocusOnReturnRef = useRef(false);
50
50
  const appliedInputMidsRef = useRef(null);
51
51
  const pendingMidsRef = useRef(null);
52
- // Flag "accoda": impostato dall'azione secondaria del bottone Ricerca, consumato in executeSearch
53
- const appendNextSearchRef = useRef(false);
52
+ // Impedisce ricerche sovrapposte (doppio Invio, o Invio mentre la precedente e' ancora in corso)
53
+ const isSearchingRef = useRef(false);
54
+ // Snapshot letti da runSearch al momento dell'esecuzione: evitano di cercare con dati stantii
55
+ const qdRef = useRef(undefined);
56
+ const showAdvancedSearchRef = useRef(false);
57
+ const searchAsyncRef = useRef();
54
58
  // Tornando dalla vista risultati il pannello dei metadati di ricerca riprende il focus,
55
59
  // così è lui quello evidenziato: prendendo il focus gli altri pannelli ricevono l'onBlur e si disattivano
56
60
  useEffect(() => {
@@ -135,39 +139,6 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
135
139
  setQd({ ...qd, where: newWhere });
136
140
  setShowAllMdWhere(true);
137
141
  }, [qd, fromDTD, inputMids]);
138
- // Eseguire la ricerca quando shouldSearch è true e qd è definito
139
- useEffect(() => {
140
- if (shouldSearch && qd) {
141
- const executeSearch = async () => {
142
- // Legge e consuma il flag "accoda" impostato dall'azione secondaria del bottone Ricerca
143
- const append = appendNextSearchRef.current;
144
- appendNextSearchRef.current = false;
145
- await searchAsync(qd, showAdvancedSearch, append);
146
- setShouldSearch(false); // Resetta il trigger dopo la ricerca
147
- };
148
- executeSearch();
149
- }
150
- }, [shouldSearch, qd, showAdvancedSearch]);
151
- // Se il pannello è attivo e non ci sono modali aperti, rimuove il focus e avvia la ricerca
152
- useEffect(() => {
153
- const handleKeyDown = (e) => {
154
- if (isQueryPanelActive && !modalIsOpen) {
155
- if (e.key === 'Enter') {
156
- e.preventDefault();
157
- // Disattiva l'elemento attivo per forzare l'onBlur
158
- if (document.activeElement instanceof HTMLElement) {
159
- document.activeElement.blur();
160
- }
161
- // Attiva il flag di ricerca. L'useEffect sopra gestirà l'attesa di qd.
162
- setShouldSearch(true);
163
- }
164
- }
165
- };
166
- window.addEventListener('keydown', handleKeyDown);
167
- return () => {
168
- window.removeEventListener('keydown', handleKeyDown);
169
- };
170
- }, [isQueryPanelActive, modalIsOpen]);
171
142
  const setDataAsync = async (sqd) => {
172
143
  let newSqd = (sqd?.id == 1) ? sqd : await SavedQueryCacheService.GetAsync(sqd?.id);
173
144
  let newQd = SearchEngine.NormalizeQueryDescriptor(newSqd?.qd);
@@ -199,11 +170,61 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
199
170
  let results = [];
200
171
  if (searchResult?.result)
201
172
  results.push(searchResult?.result);
202
- // Before notifying onSearchComplete, let's set the panel to NOT active
203
- setIsQueryPanelActive(false);
204
173
  onSearchCompleted?.(results, searchResult?.qd, append);
205
174
  }
206
175
  };
176
+ // Allineati a ogni render: runSearch li legge quando esegue, non quando viene creata
177
+ qdRef.current = qd;
178
+ showAdvancedSearchRef.current = showAdvancedSearch;
179
+ searchAsyncRef.current = searchAsync;
180
+ // Unico punto di avvio della ricerca: bottone Ricerca, Ricerca e accoda e tasto Invio passano tutti da qui
181
+ const runSearch = useCallback((append) => {
182
+ if (isSearchingRef.current)
183
+ return;
184
+ isSearchingRef.current = true;
185
+ // Toglie il focus per far committare il valore del campo in editing
186
+ if (document.activeElement instanceof HTMLElement)
187
+ document.activeElement.blur();
188
+ // Alcuni editor committano il valore in modo asincrono: si attende un tick prima di leggere il qd definitivo
189
+ setTimeout(async () => {
190
+ try {
191
+ await searchAsyncRef.current?.(qdRef.current, showAdvancedSearchRef.current, append);
192
+ }
193
+ catch (ex) {
194
+ TMExceptionBoxManager.show({ exception: ex });
195
+ }
196
+ finally {
197
+ isSearchingRef.current = false;
198
+ }
199
+ }, 0);
200
+ }, []);
201
+ // I dialoghi (TMModal/chooser) escono da contentRef perche' DevExtreme Popup li rende in un portale su document.body:
202
+ // il controllo di contenimento li esclude gia'. Questi flag coprono il caso in cui il focus sia sul contenitore del pannello.
203
+ // NB: si usano solo flag locali affidabili, non modalIsOpen, che resterebbe a true se un chooser venisse smontato senza onClose.
204
+ const isAnyDialogOpen = showFiltersConfig || showOutputConfig || showMetadataSorterForm || showSqdForm || showDistinctValuesPanel;
205
+ // L'Invio avvia SEMPRE la ricerca quando il focus e' nel form di ricerca (o sul pannello che lo contiene).
206
+ // Il criterio e' il contenimento nel DOM, non uno stato di "pannello attivo" che puo' desincronizzarsi.
207
+ useEffect(() => {
208
+ const handleKeyDown = (e) => {
209
+ if (e.key !== 'Enter' || e.repeat)
210
+ return;
211
+ if (isAnyDialogOpen)
212
+ return;
213
+ const content = contentRef.current;
214
+ const target = e.target;
215
+ if (!content || !target)
216
+ return;
217
+ const isInsideForm = content.contains(target);
218
+ // Al rientro dai risultati il focus e' sul contenitore del pannello, che e' antenato del form
219
+ const isPanelContainer = target !== document.body && target !== document.documentElement && target.contains(content);
220
+ if (!isInsideForm && !isPanelContainer)
221
+ return;
222
+ e.preventDefault();
223
+ runSearch(false);
224
+ };
225
+ window.addEventListener('keydown', handleKeyDown);
226
+ return () => { window.removeEventListener('keydown', handleKeyDown); };
227
+ }, [isAnyDialogOpen, runSearch]);
207
228
  const updateIsModalOpen = useCallback((isOpen) => {
208
229
  setModalIsOpen(isOpen);
209
230
  }, []);
@@ -230,36 +251,15 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
230
251
  }
231
252
  }
232
253
  };
233
- const handleSearchButtonClick = useCallback(async () => {
234
- // Propagazione blur
235
- if (document.activeElement instanceof HTMLElement) {
236
- document.activeElement.blur();
237
- }
238
- // Ricerca normale: azzera l'eventuale flag accoda e avvia
239
- appendNextSearchRef.current = false;
240
- setShouldSearch(true);
241
- }, []);
242
- const handleSearchAndAppendClick = useCallback(() => {
243
- // Propagazione blur
244
- if (document.activeElement instanceof HTMLElement) {
245
- document.activeElement.blur();
246
- }
247
- // Ricerca e accoda: imposta il flag consumato da executeSearch
248
- appendNextSearchRef.current = true;
249
- setShouldSearch(true);
250
- }, []);
254
+ const handleSearchButtonClick = useCallback(() => { runSearch(false); }, [runSearch]);
255
+ const handleSearchAndAppendClick = useCallback(() => { runSearch(true); }, [runSearch]);
251
256
  const searchButtonSecondaryActions = useMemo(() => [{
252
257
  name: SDKUI_Localizator.SearchAndAppend,
253
258
  icon: _jsx(IconSearch, {}),
254
259
  onClick: handleSearchAndAppendClick
255
260
  }], [handleSearchAndAppendClick]);
256
261
  const handleQdChanged = useCallback((newQd) => {
257
- if (!deepCompare(qd, newQd)) {
258
- setQd(newQd);
259
- }
260
- }, []);
261
- const handlePanelActiveChanged = useCallback((isActive) => {
262
- setIsQueryPanelActive(isActive);
262
+ setQd(prev => deepCompare(prev, newQd) ? prev : newQd);
263
263
  }, []);
264
264
  const handleAdvancedMenuClick = useCallback((e) => {
265
265
  if (e.button === AdvancedMenuButtons.DistinctValues) {
@@ -357,10 +357,10 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
357
357
  return false;
358
358
  return qd?.orderBy && qd.orderBy.length > 0;
359
359
  }, [showAdvancedSearch, qd?.orderBy]);
360
- return (_jsxs(_Fragment, { children: [_jsxs(TMPanel, { ref: tmPanelRef, title: fromDTD?.nameLoc ?? SDKUI_Localizator.Search_Metadata, allowMaximize: allowMaximize, onMaximize: onMaximizePanel, onHeaderDoubleClick: onMaximizePanel, onBack: onBack, onActiveChanged: handlePanelActiveChanged, toolbar: _jsx(_Fragment, { children: (SQD && !showSqdForm) ?
361
- _jsx(ContextMenu, { items: contextMenuItems, trigger: "left", children: _jsx(TMButton, { btnStyle: 'icon', caption: 'Altro', icon: _jsx(IconMenuVertical, { color: 'white' }), showTooltip: false, onClick: () => setIsQueryPanelActive(true) }) })
360
+ return (_jsxs(_Fragment, { children: [_jsxs(TMPanel, { ref: tmPanelRef, title: fromDTD?.nameLoc ?? SDKUI_Localizator.Search_Metadata, allowMaximize: allowMaximize, onMaximize: onMaximizePanel, onHeaderDoubleClick: onMaximizePanel, onBack: onBack, onRefresh: (!SQD || showSqdForm) ? undefined : onRefreshRequested, toolbar: _jsx(_Fragment, { children: (SQD && !showSqdForm) ?
361
+ _jsx(ContextMenu, { items: contextMenuItems, trigger: "left", children: _jsx(TMButton, { btnStyle: 'icon', caption: 'Altro', icon: _jsx(IconMenuVertical, { color: 'white' }), showTooltip: false }) })
362
362
  : _jsx(_Fragment, {}) }), children: [_jsx(ConfirmQueryParamsDialog, {}), SQD
363
- ? _jsxs("div", { onContextMenu: (e) => e.preventDefault(), style: { height: '100%', width: '100%', position: 'relative', display: showSqdForm ? 'none' : 'flex', flexDirection: 'column', gap: 5 }, children: [showAdvancedSearch
363
+ ? _jsxs("div", { ref: contentRef, onContextMenu: (e) => e.preventDefault(), style: { height: '100%', width: '100%', position: 'relative', display: showSqdForm ? 'none' : 'flex', flexDirection: 'column', gap: 5 }, children: [showAdvancedSearch
364
364
  ? _jsx(TMQueryEditor, { formMode: FormModes.Update, showToolbar: false, inputData: qd, validateSelect: true, showApply: false, onQDChanged: handleQdChanged, updateIsModalOpen: updateIsModalOpen })
365
365
  : _jsx(TMSearchQueryEditor, { qd: qd, dcmtTypesList: dcmtTypesList, isExpertMode: isExpertMode, showAllMdWhere: showAllMdWhere, onQdChanged: handleQdChanged, onFocusedMetadataChanged: setFocusedTidMid, onAdvancedMenuClick: handleAdvancedMenuClick, updateIsModalOpen: updateIsModalOpen }), shouldShowOutputBadges && qd?.select && qd.select.length > 0 && (_jsxs(StyledSelectBadgesContainer, { children: [_jsxs(StyledSelectLabel, { children: [SDK_Localizator.QuerySelect, ":"] }), _jsx(StyledSelectBadgesWrapper, { children: (() => {
366
366
  const selectItems = qd.select ?? [];
@@ -475,205 +475,205 @@ export const refreshLastSearch = async (qd) => {
475
475
  // =============================================================================
476
476
  // STYLED COMPONENTS per i badge degli ordinamenti
477
477
  // =============================================================================
478
- const StyledOrderByBadgesContainer = styled.div `
479
- display: flex;
480
- flex-wrap: wrap;
481
- align-items: center;
482
- gap: 8px;
483
- padding: 6px 10px;
484
- margin: 1px 0;
485
- background: linear-gradient(135deg, rgba(74, 150, 210, 0.08) 0%, rgba(37, 89, 165, 0.08) 100%);
486
- border-radius: 8px;
487
- border: 1px solid rgba(74, 150, 210, 0.2);
478
+ const StyledOrderByBadgesContainer = styled.div `
479
+ display: flex;
480
+ flex-wrap: wrap;
481
+ align-items: center;
482
+ gap: 8px;
483
+ padding: 6px 10px;
484
+ margin: 1px 0;
485
+ background: linear-gradient(135deg, rgba(74, 150, 210, 0.08) 0%, rgba(37, 89, 165, 0.08) 100%);
486
+ border-radius: 8px;
487
+ border: 1px solid rgba(74, 150, 210, 0.2);
488
488
  `;
489
- const StyledOrderByLabel = styled.span `
490
- font-size: 0.82rem;
491
- font-weight: 600;
492
- color: #4A96D2;
493
- white-space: nowrap;
489
+ const StyledOrderByLabel = styled.span `
490
+ font-size: 0.82rem;
491
+ font-weight: 600;
492
+ color: #4A96D2;
493
+ white-space: nowrap;
494
494
  `;
495
- const StyledOrderByBadgesWrapper = styled.div `
496
- display: flex;
497
- flex-wrap: wrap;
498
- gap: 6px;
499
- align-items: center;
500
- flex: 1;
495
+ const StyledOrderByBadgesWrapper = styled.div `
496
+ display: flex;
497
+ flex-wrap: wrap;
498
+ gap: 6px;
499
+ align-items: center;
500
+ flex: 1;
501
501
  `;
502
- const StyledOrderByBadge = styled.div `
503
- display: inline-flex;
504
- align-items: center;
505
- gap: 4px;
506
- padding: 3px 8px;
507
- background: white;
508
- border: 1px solid #d1d9e6;
509
- border-radius: 16px;
510
- font-size: 0.82rem;
511
- color: #333;
512
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
513
-
514
- .badge-name {
515
- max-width: 120px;
516
- overflow: hidden;
517
- text-overflow: ellipsis;
518
- white-space: nowrap;
519
-
520
- @media (max-width: 600px) {
521
- max-width: 80px;
522
- }
523
- }
502
+ const StyledOrderByBadge = styled.div `
503
+ display: inline-flex;
504
+ align-items: center;
505
+ gap: 4px;
506
+ padding: 3px 8px;
507
+ background: white;
508
+ border: 1px solid #d1d9e6;
509
+ border-radius: 16px;
510
+ font-size: 0.82rem;
511
+ color: #333;
512
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
513
+
514
+ .badge-name {
515
+ max-width: 120px;
516
+ overflow: hidden;
517
+ text-overflow: ellipsis;
518
+ white-space: nowrap;
519
+
520
+ @media (max-width: 600px) {
521
+ max-width: 80px;
522
+ }
523
+ }
524
524
  `;
525
- const StyledOrderByBadgeMore = styled.div `
526
- display: inline-flex;
527
- align-items: center;
528
- justify-content: center;
529
- padding: 3px 10px;
530
- background: linear-gradient(135deg, #4A96D2 0%, #2559A5 100%);
531
- border: none;
532
- border-radius: 16px;
533
- font-size: 0.82rem;
534
- font-weight: 600;
535
- color: white;
536
- cursor: pointer;
537
- transition: all 0.2s ease;
538
-
539
- &:hover {
540
- transform: scale(1.05);
541
- box-shadow: 0 2px 6px rgba(74, 150, 210, 0.4);
542
- }
525
+ const StyledOrderByBadgeMore = styled.div `
526
+ display: inline-flex;
527
+ align-items: center;
528
+ justify-content: center;
529
+ padding: 3px 10px;
530
+ background: linear-gradient(135deg, #4A96D2 0%, #2559A5 100%);
531
+ border: none;
532
+ border-radius: 16px;
533
+ font-size: 0.82rem;
534
+ font-weight: 600;
535
+ color: white;
536
+ cursor: pointer;
537
+ transition: all 0.2s ease;
538
+
539
+ &:hover {
540
+ transform: scale(1.05);
541
+ box-shadow: 0 2px 6px rgba(74, 150, 210, 0.4);
542
+ }
543
543
  `;
544
- const StyledOrderByEditButton = styled.div `
545
- display: flex;
546
- align-items: center;
547
- justify-content: center;
548
- width: 28px;
549
- height: 28px;
550
- border-radius: 4px;
551
- color: #4A96D2;
552
- cursor: pointer;
553
- transition: all 0.2s ease;
554
-
555
- &:hover {
556
- background-color: rgba(74, 150, 210, 0.15);
557
- }
544
+ const StyledOrderByEditButton = styled.div `
545
+ display: flex;
546
+ align-items: center;
547
+ justify-content: center;
548
+ width: 28px;
549
+ height: 28px;
550
+ border-radius: 4px;
551
+ color: #4A96D2;
552
+ cursor: pointer;
553
+ transition: all 0.2s ease;
554
+
555
+ &:hover {
556
+ background-color: rgba(74, 150, 210, 0.15);
557
+ }
558
558
  `;
559
559
  // =============================================================================
560
560
  // STYLED COMPONENTS per i badge degli output (select) - colore verde/teal
561
561
  // =============================================================================
562
- const StyledSelectBadgesContainer = styled.div `
563
- display: flex;
564
- flex-wrap: wrap;
565
- align-items: center;
566
- gap: 8px;
567
- padding: 6px 10px;
568
- margin: 1px 0;
569
- background: linear-gradient(135deg, rgba(46, 125, 50, 0.08) 0%, rgba(27, 94, 32, 0.08) 100%);
570
- border-radius: 8px;
571
- border: 1px solid rgba(46, 125, 50, 0.2);
562
+ const StyledSelectBadgesContainer = styled.div `
563
+ display: flex;
564
+ flex-wrap: wrap;
565
+ align-items: center;
566
+ gap: 8px;
567
+ padding: 6px 10px;
568
+ margin: 1px 0;
569
+ background: linear-gradient(135deg, rgba(46, 125, 50, 0.08) 0%, rgba(27, 94, 32, 0.08) 100%);
570
+ border-radius: 8px;
571
+ border: 1px solid rgba(46, 125, 50, 0.2);
572
572
  `;
573
- const StyledSelectLabel = styled.span `
574
- font-size: 0.82rem;
575
- font-weight: 600;
576
- color: #2E7D32;
577
- white-space: nowrap;
573
+ const StyledSelectLabel = styled.span `
574
+ font-size: 0.82rem;
575
+ font-weight: 600;
576
+ color: #2E7D32;
577
+ white-space: nowrap;
578
578
  `;
579
- const StyledSelectBadgesWrapper = styled.div `
580
- display: flex;
581
- flex-wrap: wrap;
582
- gap: 6px;
583
- align-items: center;
584
- flex: 1;
579
+ const StyledSelectBadgesWrapper = styled.div `
580
+ display: flex;
581
+ flex-wrap: wrap;
582
+ gap: 6px;
583
+ align-items: center;
584
+ flex: 1;
585
585
  `;
586
- const StyledSelectBadge = styled.div `
587
- display: inline-flex;
588
- align-items: center;
589
- gap: 4px;
590
- padding: 3px 8px;
591
- background: white;
592
- border: 1px solid #c8e6c9;
593
- border-radius: 16px;
594
- font-size: 0.82rem;
595
- color: #333;
596
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
597
-
598
- .badge-name {
599
- max-width: 120px;
600
- overflow: hidden;
601
- text-overflow: ellipsis;
602
- white-space: nowrap;
603
-
604
- @media (max-width: 600px) {
605
- max-width: 80px;
606
- }
607
- }
586
+ const StyledSelectBadge = styled.div `
587
+ display: inline-flex;
588
+ align-items: center;
589
+ gap: 4px;
590
+ padding: 3px 8px;
591
+ background: white;
592
+ border: 1px solid #c8e6c9;
593
+ border-radius: 16px;
594
+ font-size: 0.82rem;
595
+ color: #333;
596
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
597
+
598
+ .badge-name {
599
+ max-width: 120px;
600
+ overflow: hidden;
601
+ text-overflow: ellipsis;
602
+ white-space: nowrap;
603
+
604
+ @media (max-width: 600px) {
605
+ max-width: 80px;
606
+ }
607
+ }
608
608
  `;
609
- const StyledSelectBadgeMore = styled.div `
610
- display: inline-flex;
611
- align-items: center;
612
- justify-content: center;
613
- padding: 3px 10px;
614
- background: linear-gradient(135deg, #43A047 0%, #2E7D32 100%);
615
- border: none;
616
- border-radius: 16px;
617
- font-size: 0.82rem;
618
- font-weight: 600;
619
- color: white;
620
- cursor: pointer;
621
- transition: all 0.2s ease;
622
-
623
- &:hover {
624
- transform: scale(1.05);
625
- box-shadow: 0 2px 6px rgba(46, 125, 50, 0.4);
626
- }
609
+ const StyledSelectBadgeMore = styled.div `
610
+ display: inline-flex;
611
+ align-items: center;
612
+ justify-content: center;
613
+ padding: 3px 10px;
614
+ background: linear-gradient(135deg, #43A047 0%, #2E7D32 100%);
615
+ border: none;
616
+ border-radius: 16px;
617
+ font-size: 0.82rem;
618
+ font-weight: 600;
619
+ color: white;
620
+ cursor: pointer;
621
+ transition: all 0.2s ease;
622
+
623
+ &:hover {
624
+ transform: scale(1.05);
625
+ box-shadow: 0 2px 6px rgba(46, 125, 50, 0.4);
626
+ }
627
627
  `;
628
- const StyledSelectEditButton = styled.div `
629
- display: flex;
630
- align-items: center;
631
- justify-content: center;
632
- width: 28px;
633
- height: 28px;
634
- border-radius: 4px;
635
- color: #2E7D32;
636
- cursor: pointer;
637
- transition: all 0.2s ease;
638
-
639
- &:hover {
640
- background-color: rgba(46, 125, 50, 0.15);
641
- }
628
+ const StyledSelectEditButton = styled.div `
629
+ display: flex;
630
+ align-items: center;
631
+ justify-content: center;
632
+ width: 28px;
633
+ height: 28px;
634
+ border-radius: 4px;
635
+ color: #2E7D32;
636
+ cursor: pointer;
637
+ transition: all 0.2s ease;
638
+
639
+ &:hover {
640
+ background-color: rgba(46, 125, 50, 0.15);
641
+ }
642
642
  `;
643
- export const StyledToppyTextContainer = styled.div `
644
- padding: 22px 8px;
645
- width: 100%;
646
- max-width: 300px;
647
- border: 1px solid #2559A5;
648
- border-radius: 30px;
649
- display: flex;
650
- align-items: center;
651
- justify-content: center;
652
- box-sizing: border-box;
653
- min-height: 100px;
643
+ export const StyledToppyTextContainer = styled.div `
644
+ padding: 22px 8px;
645
+ width: 100%;
646
+ max-width: 300px;
647
+ border: 1px solid #2559A5;
648
+ border-radius: 30px;
649
+ display: flex;
650
+ align-items: center;
651
+ justify-content: center;
652
+ box-sizing: border-box;
653
+ min-height: 100px;
654
654
  `;
655
- export const StyledToppyText = styled.p `
656
- text-align: center;
657
- color: #2559A5;
658
- font-size: 1rem;
659
- user-select: none;
660
- -webkit-touch-callout: none;
661
- -webkit-user-select: none;
662
- -moz-user-select: none;
663
- -ms-user-select: none;
664
- margin: 0;
665
- display: -webkit-box;
666
- -webkit-box-orient: vertical;
667
- -webkit-line-clamp: 3;
668
- overflow: hidden;
669
- text-overflow: ellipsis;
670
- white-space: normal;
671
- word-break: break-word;
672
- line-height: 1.2;
655
+ export const StyledToppyText = styled.p `
656
+ text-align: center;
657
+ color: #2559A5;
658
+ font-size: 1rem;
659
+ user-select: none;
660
+ -webkit-touch-callout: none;
661
+ -webkit-user-select: none;
662
+ -moz-user-select: none;
663
+ -ms-user-select: none;
664
+ margin: 0;
665
+ display: -webkit-box;
666
+ -webkit-box-orient: vertical;
667
+ -webkit-line-clamp: 3;
668
+ overflow: hidden;
669
+ text-overflow: ellipsis;
670
+ white-space: normal;
671
+ word-break: break-word;
672
+ line-height: 1.2;
673
673
  `;
674
- export const StyledToppyImage = styled.img `
675
- width: 100%;
676
- max-width: 120px;
677
- height: auto;
678
- display: block;
674
+ export const StyledToppyImage = styled.img `
675
+ width: 100%;
676
+ max-width: 120px;
677
+ height: auto;
678
+ display: block;
679
679
  `;