@tsiky/components-r19 1.0.0 → 1.2.0

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.
Files changed (61) hide show
  1. package/index.ts +35 -33
  2. package/package.json +1 -1
  3. package/src/components/AnnouncementPanel/FlexRowContainer.css +17 -17
  4. package/src/components/AnnouncementPanel/FlexRowContainer.stories.tsx +329 -329
  5. package/src/components/AnnouncementPanel/FlexRowContainer.tsx +24 -24
  6. package/src/components/AnnouncementPanel/ListBox/CounterListBox.css +56 -56
  7. package/src/components/AnnouncementPanel/ListBox/CounterListBox.stories.tsx +292 -292
  8. package/src/components/AnnouncementPanel/ListBox/CounterListBox.tsx +106 -106
  9. package/src/components/AnnouncementPanel/ListBox/SimpleListBox.css +57 -57
  10. package/src/components/AnnouncementPanel/ListBox/SimpleListBox.stories.tsx +189 -189
  11. package/src/components/AnnouncementPanel/ListBox/SimpleListBox.tsx +138 -138
  12. package/src/components/AnnouncementPanel/ListBox/TrendListBox.css +61 -61
  13. package/src/components/AnnouncementPanel/ListBox/TrendListBox.stories.tsx +257 -257
  14. package/src/components/AnnouncementPanel/ListBox/TrendListBox.tsx +90 -90
  15. package/src/components/AnnouncementPanel/ListBox/index.ts +3 -3
  16. package/src/components/AnnouncementPanel/ListContentContainer.css +23 -23
  17. package/src/components/AnnouncementPanel/ListContentContainer.stories.tsx +212 -212
  18. package/src/components/AnnouncementPanel/ListContentContainer.tsx +33 -33
  19. package/src/components/AnnouncementPanel/index.ts +3 -3
  20. package/src/components/Charts/area-chart-admission/AreaChartAdmission.tsx +129 -89
  21. package/src/components/Charts/bar-chart/BarChart.tsx +171 -132
  22. package/src/components/Charts/boxplot-chart/BoxPlotChart.tsx +114 -114
  23. package/src/components/Charts/mixed-chart/MixedChart.tsx +65 -9
  24. package/src/components/Charts/sankey-adaptation/sankey.tsx +70 -70
  25. package/src/components/Charts/sankey-chart/SankeyChart.tsx +183 -155
  26. package/src/components/Confirmationpopup/ConfirmationPopup.module.css +88 -0
  27. package/src/components/Confirmationpopup/ConfirmationPopup.stories.tsx +94 -0
  28. package/src/components/Confirmationpopup/ConfirmationPopup.tsx +47 -0
  29. package/src/components/Confirmationpopup/index.ts +6 -0
  30. package/src/components/Confirmationpopup/useConfirmationPopup.ts +48 -0
  31. package/src/components/DayStatCard/DayStatCard.tsx +96 -69
  32. package/src/components/DraggableSwitcher/DraggableSwitcherButton.tsx +58 -58
  33. package/src/components/DraggableSwitcher/context/useDraggableSwitcher.tsx +45 -45
  34. package/src/components/DraggableSwitcher/index.ts +2 -2
  35. package/src/components/DynamicInput/input/SelectInput.tsx +75 -75
  36. package/src/components/DynamicInput/input/assets/SelectInput.module.css +95 -95
  37. package/src/components/DynamicTable/TableCell.tsx +38 -30
  38. package/src/components/DynamicTable/TableHeader.tsx +39 -34
  39. package/src/components/DynamicTable/TableauDynamique.module.css +1333 -1287
  40. package/src/components/DynamicTable/TableauDynamique.tsx +154 -154
  41. package/src/components/DynamicTable/tools/tableTypes.ts +63 -63
  42. package/src/components/Grid/Grid.tsx +5 -0
  43. package/src/components/Grid/grid.css +285 -285
  44. package/src/components/Header/Header.tsx +4 -2
  45. package/src/components/Header/header.css +61 -31
  46. package/src/components/MetricsPanel/MetricsPanel.module.css +688 -636
  47. package/src/components/MetricsPanel/MetricsPanel.tsx +220 -282
  48. package/src/components/MetricsPanel/renderers/CompactRenderer.tsx +148 -125
  49. package/src/components/NavBar/NavBar.tsx +1 -1
  50. package/src/components/NavItem/NavItem.tsx +58 -58
  51. package/src/components/PeriodRange/PeriodRange.module.css +158 -158
  52. package/src/components/PeriodRange/PeriodRange.tsx +130 -130
  53. package/src/components/SearchBar/SearchBar.css +40 -40
  54. package/src/components/SelectFilter/SelectFilter.module.css +249 -0
  55. package/src/components/SelectFilter/SelectFilter.stories.tsx +321 -0
  56. package/src/components/SelectFilter/SelectFilter.tsx +219 -0
  57. package/src/components/SelectFilter/index.ts +2 -0
  58. package/src/components/SelectFilter/types.ts +19 -0
  59. package/src/components/TranslationKey/TranslationKey.css +272 -272
  60. package/src/components/TranslationKey/TranslationKey.tsx +266 -245
  61. package/src/components/TrendList/TrendList.tsx +72 -45
@@ -1,245 +1,266 @@
1
- import React, { useState } from 'react';
2
- import { Edit3, Trash2, X, ChevronDown, ChevronRight } from 'lucide-react';
3
- import './TranslationKey.css';
4
- import { AddItemModal } from '../AddItemModal/AddItemModal';
5
-
6
- export interface Translation {
7
- language: string;
8
- value: string;
9
- }
10
-
11
- interface TranslationKeyProps {
12
- keyName: string;
13
- translations: Translation[];
14
- availableLanguages: { code: string; name: string }[];
15
- onDeleteKey?: (keyName: string) => void;
16
- onEditKey?: (oldKey: string, newKey: string) => void; // <- nouvelle prop
17
- onEditTranslation?: (keyName: string, language: string, newValue: string) => void;
18
- onDeleteTranslation?: (keyName: string, language: string) => void;
19
- onAddTranslation?: (keyName: string, language: string, value: string) => void;
20
- }
21
-
22
- export const TranslationKey: React.FC<TranslationKeyProps> = ({
23
- keyName,
24
- translations,
25
- availableLanguages,
26
- onDeleteKey,
27
- onEditKey,
28
- onEditTranslation,
29
- onDeleteTranslation,
30
- onAddTranslation,
31
- }) => {
32
- const [showAddLanguage, setShowAddLanguage] = useState(false);
33
- const [selectedLanguage, setSelectedLanguage] = useState('');
34
- const [editingTranslation, setEditingTranslation] = useState<string | null>(null);
35
- const [editValue, setEditValue] = useState('');
36
- const [newTranslationValue, setNewTranslationValue] = useState('');
37
-
38
- // état pour le modal de renommage de la clé
39
- const [isRenameModalOpen, setIsRenameModalOpen] = useState(false);
40
-
41
- // état pour replier/déplier
42
- const [isCollapsed, setIsCollapsed] = useState(false);
43
-
44
- const usedLanguages = translations.map((t) => t.language);
45
- const availableToAdd = availableLanguages.filter((lang) => !usedLanguages.includes(lang.code));
46
-
47
- const handleAddTranslation = () => {
48
- if (selectedLanguage && newTranslationValue.trim()) {
49
- onAddTranslation?.(keyName, selectedLanguage, newTranslationValue.trim());
50
- setSelectedLanguage('');
51
- setNewTranslationValue('');
52
- setShowAddLanguage(false);
53
- }
54
- };
55
-
56
- const handleEditStart = (language: string, currentValue: string) => {
57
- setEditingTranslation(language);
58
- setEditValue(currentValue);
59
- };
60
-
61
- const handleEditSave = (language: string) => {
62
- onEditTranslation?.(keyName, language, editValue);
63
- setEditingTranslation(null);
64
- setEditValue('');
65
- };
66
-
67
- const handleEditCancel = () => {
68
- setEditingTranslation(null);
69
- setEditValue('');
70
- };
71
-
72
- const handleRenameSave = (values: Record<string, string | number>) => {
73
- const newName = (values.name as string)?.trim();
74
- if (!newName) return;
75
- if (newName === keyName) {
76
- setIsRenameModalOpen(false);
77
- return;
78
- }
79
- onEditKey?.(keyName, newName);
80
- setIsRenameModalOpen(false);
81
- };
82
-
83
- return (
84
- <div className='translation-card'>
85
- {/* Header */}
86
- <div className='card-header'>
87
- <div className='header-left'>
88
- {/* Toggle button aligné à gauche */}
89
- <button
90
- className='collapse-button'
91
- onClick={() => setIsCollapsed(!isCollapsed)}
92
- title={isCollapsed ? 'Déplier' : 'Replier'}
93
- >
94
- {isCollapsed ? (
95
- <ChevronRight className='collapse-icon' />
96
- ) : (
97
- <ChevronDown className='collapse-icon' />
98
- )}
99
- </button>
100
-
101
- <h3 className='card-title'>{keyName}</h3>
102
- </div>
103
-
104
- <div className='header-actions'>
105
- <button
106
- className='action-button'
107
- onClick={(e) => {
108
- e.stopPropagation();
109
- setIsRenameModalOpen(true);
110
- }}
111
- title='Renommer la clé'
112
- >
113
- <Edit3 className='action-icon' />
114
- </button>
115
- <button
116
- onClick={(e) => {
117
- e.stopPropagation();
118
- onDeleteKey?.(keyName);
119
- }}
120
- className='action-button delete'
121
- title='Supprimer la clé'
122
- >
123
- <Trash2 className='action-icon' />
124
- </button>
125
- </div>
126
- </div>
127
-
128
- {/* Contenu (masqué si replié) */}
129
- {!isCollapsed && (
130
- <div className='translations-container'>
131
- {translations.map((translation) => (
132
- <div key={translation.language} className='translation-row'>
133
- <div className='language-code'>{translation.language}</div>
134
- <div className='translation-content'>
135
- {editingTranslation === translation.language ? (
136
- <>
137
- <input
138
- type='text'
139
- value={editValue}
140
- onChange={(e) => setEditValue(e.target.value)}
141
- className='edit-input'
142
- autoFocus
143
- />
144
- <button
145
- onClick={() => handleEditSave(translation.language)}
146
- className='save-button'
147
- >
148
- Enregistrer
149
- </button>
150
- <button onClick={handleEditCancel} className='cancel-button'>
151
- <X className='action-icon' />
152
- </button>
153
- </>
154
- ) : (
155
- <>
156
- <span className='translation-text'>{translation.value}</span>
157
- <button
158
- onClick={() => handleEditStart(translation.language, translation.value)}
159
- className='edit-button'
160
- title='Éditer la traduction'
161
- >
162
- <Edit3 className='action-icon' />
163
- </button>
164
- <button
165
- onClick={() => onDeleteTranslation?.(keyName, translation.language)}
166
- className='delete-translation-button'
167
- title='Supprimer la traduction'
168
- >
169
- <X className='action-icon' />
170
- </button>
171
- </>
172
- )}
173
- </div>
174
- </div>
175
- ))}
176
-
177
- {/* Add language */}
178
- {showAddLanguage ? (
179
- <div className='add-language-row'>
180
- <div className='language-code'></div>
181
- <div className='add-language-content'>
182
- <div className='language-select-container'>
183
- <select
184
- value={selectedLanguage}
185
- onChange={(e) => setSelectedLanguage(e.target.value)}
186
- className='language-select'
187
- >
188
- <option value=''>Sélectionnez la langue</option>
189
- {availableToAdd.map((lang) => (
190
- <option key={lang.code} value={lang.code}>
191
- {lang.name}
192
- </option>
193
- ))}
194
- </select>
195
- </div>
196
- {selectedLanguage && (
197
- <input
198
- type='text'
199
- value={newTranslationValue}
200
- onChange={(e) => setNewTranslationValue(e.target.value)}
201
- placeholder='Entrez la traduction...'
202
- className='translation-input'
203
- />
204
- )}
205
- <button
206
- onClick={handleAddTranslation}
207
- disabled={!selectedLanguage || !newTranslationValue.trim()}
208
- className='show-add-button'
209
- >
210
- <span className='plus-icon'>+</span>
211
- Ajouter
212
- </button>
213
- <button
214
- onClick={() => {
215
- setShowAddLanguage(false);
216
- setSelectedLanguage('');
217
- setNewTranslationValue('');
218
- }}
219
- className='cancel-button'
220
- >
221
- <X className='action-icon' />
222
- </button>
223
- </div>
224
- </div>
225
- ) : (
226
- <button onClick={() => setShowAddLanguage(true)} className='show-add-button'>
227
- <span className='plus-icon'>+</span>
228
- Ajouter
229
- </button>
230
- )}
231
- </div>
232
- )}
233
-
234
- {/* Modal pour renommer la clé */}
235
- <AddItemModal
236
- isOpen={isRenameModalOpen}
237
- onClose={() => setIsRenameModalOpen(false)}
238
- onSave={handleRenameSave}
239
- title='Renommer la clé'
240
- initialValues={{ name: keyName }}
241
- fields={[{ name: 'name', label: 'Nom de la clé', placeholder: 'Nom de la clé' }]}
242
- />
243
- </div>
244
- );
245
- };
1
+ import React, { useState } from 'react';
2
+ import { Edit3, Trash2, X, ChevronDown, ChevronRight, Move } from 'lucide-react';
3
+ import './TranslationKey.css';
4
+ import { AddItemModal } from '../AddItemModal/AddItemModal';
5
+
6
+ export interface Translation {
7
+ language: string;
8
+ value: string;
9
+ }
10
+
11
+ interface TranslationKeyProps {
12
+ keyName: string;
13
+ translations: Translation[];
14
+ availableLanguages: { code: string; name: string }[];
15
+ onDeleteKey?: (keyName: string) => void;
16
+ onEditKey?: (oldKey: string, newKey: string) => void; // <- nouvelle prop
17
+ onEditTranslation?: (keyName: string, language: string, newValue: string) => void;
18
+ onDeleteTranslation?: (keyName: string, language: string) => void;
19
+ onAddTranslation?: (keyName: string, language: string, value: string) => void;
20
+ draggable?: boolean;
21
+ }
22
+
23
+ export const TranslationKey: React.FC<TranslationKeyProps> = ({
24
+ keyName,
25
+ translations,
26
+ availableLanguages,
27
+ onDeleteKey,
28
+ onEditKey,
29
+ onEditTranslation,
30
+ onDeleteTranslation,
31
+ onAddTranslation,
32
+ draggable = false,
33
+ }) => {
34
+ const [showAddLanguage, setShowAddLanguage] = useState(false);
35
+ const [selectedLanguage, setSelectedLanguage] = useState('');
36
+ const [editingTranslation, setEditingTranslation] = useState<string | null>(null);
37
+ const [editValue, setEditValue] = useState('');
38
+ const [newTranslationValue, setNewTranslationValue] = useState('');
39
+
40
+ // état pour le modal de renommage de la clé
41
+ const [isRenameModalOpen, setIsRenameModalOpen] = useState(false);
42
+
43
+ // état pour replier/déplier
44
+ const [isCollapsed, setIsCollapsed] = useState(false);
45
+
46
+ const usedLanguages = translations.map((t) => t.language);
47
+ const availableToAdd = availableLanguages.filter((lang) => !usedLanguages.includes(lang.code));
48
+
49
+ const handleAddTranslation = () => {
50
+ if (selectedLanguage && newTranslationValue.trim()) {
51
+ onAddTranslation?.(keyName, selectedLanguage, newTranslationValue.trim());
52
+ setSelectedLanguage('');
53
+ setNewTranslationValue('');
54
+ setShowAddLanguage(false);
55
+ }
56
+ };
57
+
58
+ const handleEditStart = (language: string, currentValue: string) => {
59
+ setEditingTranslation(language);
60
+ setEditValue(currentValue);
61
+ };
62
+
63
+ const handleEditSave = (language: string) => {
64
+ onEditTranslation?.(keyName, language, editValue);
65
+ setEditingTranslation(null);
66
+ setEditValue('');
67
+ };
68
+
69
+ const handleEditCancel = () => {
70
+ setEditingTranslation(null);
71
+ setEditValue('');
72
+ };
73
+
74
+ const handleRenameSave = (values: Record<string, string | number>) => {
75
+ const newName = (values.name as string)?.trim();
76
+ if (!newName) return;
77
+ if (newName === keyName) {
78
+ setIsRenameModalOpen(false);
79
+ return;
80
+ }
81
+ onEditKey?.(keyName, newName);
82
+ setIsRenameModalOpen(false);
83
+ };
84
+
85
+ return (
86
+ <div className='translation-card' style={{ position: 'relative' }}>
87
+ {/* Header toujours visible */}
88
+ <div className='card-header'>
89
+ <div className='header-left'>
90
+ <button
91
+ className='collapse-button'
92
+ onClick={() => setIsCollapsed(!isCollapsed)}
93
+ title={isCollapsed ? 'Déplier' : 'Replier'}
94
+ >
95
+ {isCollapsed ? (
96
+ <ChevronRight className='collapse-icon' />
97
+ ) : (
98
+ <ChevronDown className='collapse-icon' />
99
+ )}
100
+ </button>
101
+ <h3 className='card-title'>{keyName}</h3>
102
+ </div>
103
+
104
+ <div className='header-actions'>
105
+ <button
106
+ className='action-button'
107
+ onClick={(e) => {
108
+ e.stopPropagation();
109
+ setIsRenameModalOpen(true);
110
+ }}
111
+ title='Renommer la clé'
112
+ >
113
+ <Edit3 className='action-icon' />
114
+ </button>
115
+ <button
116
+ onClick={(e) => {
117
+ e.stopPropagation();
118
+ onDeleteKey?.(keyName);
119
+ }}
120
+ className='action-button delete'
121
+ title='Supprimer la clé'
122
+ >
123
+ <Trash2 className='action-icon' />
124
+ </button>
125
+ </div>
126
+ </div>
127
+
128
+ {/* Contenu (overlay draggable si nécessaire) */}
129
+ <div className='translations-container' style={{ opacity: draggable ? 0 : 1 }}>
130
+ {!isCollapsed && !draggable && (
131
+ <>
132
+ {translations.map((translation) => (
133
+ <div key={translation.language} className='translation-row'>
134
+ <div className='language-code'>{translation.language}</div>
135
+ <div className='translation-content'>
136
+ {editingTranslation === translation.language ? (
137
+ <>
138
+ <input
139
+ type='text'
140
+ value={editValue}
141
+ onChange={(e) => setEditValue(e.target.value)}
142
+ className='edit-input'
143
+ autoFocus
144
+ />
145
+ <button
146
+ onClick={() => handleEditSave(translation.language)}
147
+ className='save-button'
148
+ >
149
+ Enregistrer
150
+ </button>
151
+ <button onClick={handleEditCancel} className='cancel-button'>
152
+ <X className='action-icon' />
153
+ </button>
154
+ </>
155
+ ) : (
156
+ <>
157
+ <span className='translation-text'>{translation.value}</span>
158
+ <button
159
+ onClick={() => handleEditStart(translation.language, translation.value)}
160
+ className='edit-button'
161
+ title='Éditer la traduction'
162
+ >
163
+ <Edit3 className='action-icon' />
164
+ </button>
165
+ <button
166
+ onClick={() => onDeleteTranslation?.(keyName, translation.language)}
167
+ className='delete-translation-button'
168
+ title='Supprimer la traduction'
169
+ >
170
+ <X className='action-icon' />
171
+ </button>
172
+ </>
173
+ )}
174
+ </div>
175
+ </div>
176
+ ))}
177
+
178
+ {/* Add language */}
179
+ {showAddLanguage ? (
180
+ <div className='add-language-row'>
181
+ <div className='language-code'></div>
182
+ <div className='add-language-content'>
183
+ <div className='language-select-container'>
184
+ <select
185
+ value={selectedLanguage}
186
+ onChange={(e) => setSelectedLanguage(e.target.value)}
187
+ className='language-select'
188
+ >
189
+ <option value=''>Sélectionnez la langue</option>
190
+ {availableToAdd.map((lang) => (
191
+ <option key={lang.code} value={lang.code}>
192
+ {lang.name}
193
+ </option>
194
+ ))}
195
+ </select>
196
+ </div>
197
+ {selectedLanguage && (
198
+ <input
199
+ type='text'
200
+ value={newTranslationValue}
201
+ onChange={(e) => setNewTranslationValue(e.target.value)}
202
+ placeholder='Entrez la traduction...'
203
+ className='translation-input'
204
+ />
205
+ )}
206
+ <button
207
+ onClick={handleAddTranslation}
208
+ disabled={!selectedLanguage || !newTranslationValue.trim()}
209
+ className='show-add-button'
210
+ >
211
+ <span className='plus-icon'>+</span>
212
+ Ajouter
213
+ </button>
214
+ <button
215
+ onClick={() => {
216
+ setShowAddLanguage(false);
217
+ setSelectedLanguage('');
218
+ setNewTranslationValue('');
219
+ }}
220
+ className='cancel-button'
221
+ >
222
+ <X className='action-icon' />
223
+ </button>
224
+ </div>
225
+ </div>
226
+ ) : (
227
+ <button onClick={() => setShowAddLanguage(true)} className='show-add-button'>
228
+ <span className='plus-icon'>+</span>
229
+ Ajouter
230
+ </button>
231
+ )}
232
+ </>
233
+ )}
234
+ </div>
235
+
236
+ {/* Overlay Move */}
237
+ {draggable && (
238
+ <div
239
+ style={{
240
+ position: 'absolute',
241
+ top: 40, // pour laisser le header visible
242
+ left: 0,
243
+ width: '100%',
244
+ height: 'calc(100% - 40px)',
245
+ display: 'flex',
246
+ alignItems: 'center',
247
+ justifyContent: 'center',
248
+ pointerEvents: 'none',
249
+ }}
250
+ >
251
+ <Move size={32} strokeWidth={1.5} style={{ opacity: 0.8 }} />
252
+ </div>
253
+ )}
254
+
255
+ {/* Modal pour renommer la clé */}
256
+ <AddItemModal
257
+ isOpen={isRenameModalOpen}
258
+ onClose={() => setIsRenameModalOpen(false)}
259
+ onSave={handleRenameSave}
260
+ title='Renommer la clé'
261
+ initialValues={{ name: keyName }}
262
+ fields={[{ name: 'name', label: 'Nom de la clé', placeholder: 'Nom de la clé' }]}
263
+ />
264
+ </div>
265
+ );
266
+ };
@@ -1,45 +1,72 @@
1
- import React from 'react';
2
- import './TrendList.css';
3
- import { TrendItem } from '../TrendItem/TrendItem';
4
- import type { TrendItemProps } from '../TrendItem/TrendItem';
5
-
6
- type TrendListProps = {
7
- items: TrendItemProps[];
8
- header?: string;
9
- headerStyle?: React.CSSProperties;
10
- listStyle?: React.CSSProperties;
11
- size?: number | string;
12
- gap?: number;
13
- className?: string;
14
- background?: string; // Nouvelle prop pour le background
15
- };
16
-
17
- export const TrendList: React.FC<TrendListProps> = ({
18
- items,
19
- header = 'Tendance',
20
- headerStyle,
21
- listStyle,
22
- size,
23
- gap,
24
- className = '',
25
- background = 'var(--color-card-background)',
26
- }) => {
27
- return (
28
- <div
29
- className={`trend-list ${className}`}
30
- style={{
31
- width: size,
32
- gap: `${gap}px`,
33
- background,
34
- ...listStyle,
35
- }}
36
- >
37
- <h3 className='trend-header' style={headerStyle}>
38
- {header}
39
- </h3>
40
- {items.map((item, idx) => (
41
- <TrendItem key={idx} {...item} />
42
- ))}
43
- </div>
44
- );
45
- };
1
+ import React from 'react';
2
+ import './TrendList.css';
3
+ import { TrendItem } from '../TrendItem/TrendItem';
4
+ import type { TrendItemProps } from '../TrendItem/TrendItem';
5
+ import { Move } from 'lucide-react';
6
+
7
+ type TrendListProps = {
8
+ items: TrendItemProps[];
9
+ header?: string;
10
+ headerStyle?: React.CSSProperties;
11
+ listStyle?: React.CSSProperties;
12
+ size?: number | string;
13
+ gap?: number;
14
+ className?: string;
15
+ background?: string;
16
+ draggable?: boolean; // nouvelle prop
17
+ };
18
+
19
+ export const TrendList: React.FC<TrendListProps> = ({
20
+ items,
21
+ header = 'Tendance',
22
+ headerStyle,
23
+ listStyle,
24
+ size,
25
+ gap = 0,
26
+ className = '',
27
+ background = 'var(--color-card-background)',
28
+ draggable = false,
29
+ }) => {
30
+ return (
31
+ <div
32
+ className={`trend-list ${className}`}
33
+ style={{
34
+ width: size,
35
+ gap: `${gap}px`,
36
+ background,
37
+ position: 'relative', // nécessaire pour overlay
38
+ ...listStyle,
39
+ }}
40
+ >
41
+ <h3 className='trend-header' style={headerStyle}>
42
+ {header}
43
+ </h3>
44
+
45
+ {/* Liste réelle */}
46
+ <div style={{ opacity: draggable ? 0 : 1 }}>
47
+ {items.map((item, idx) => (
48
+ <TrendItem key={idx} {...item} />
49
+ ))}
50
+ </div>
51
+
52
+ {/* Overlay Move */}
53
+ {draggable && (
54
+ <div
55
+ style={{
56
+ position: 'absolute',
57
+ top: 0,
58
+ left: 0,
59
+ width: '100%',
60
+ height: '100%',
61
+ display: 'flex',
62
+ alignItems: 'center',
63
+ justifyContent: 'center',
64
+ pointerEvents: 'none', // laisse passer les événements si nécessaire
65
+ }}
66
+ >
67
+ <Move size={48} strokeWidth={1.5} style={{ opacity: 0.8 }} />
68
+ </div>
69
+ )}
70
+ </div>
71
+ );
72
+ };