@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
@@ -0,0 +1,219 @@
1
+ import React, { useState, useRef, useEffect, useMemo } from 'react';
2
+ import type { SelectFilterProps, SelectOption } from './types';
3
+ import styles from './SelectFilter.module.css';
4
+
5
+ const SelectFilter: React.FC<SelectFilterProps> = ({
6
+ options,
7
+ value,
8
+ onChange,
9
+ placeholder = 'Sélectionner une option...',
10
+ disabled = false,
11
+ loading = false,
12
+ error = false,
13
+ className = '',
14
+ size = 'md',
15
+ variant = 'default',
16
+ width = 'w-full',
17
+ }) => {
18
+ const [isOpen, setIsOpen] = useState(false);
19
+ const [searchTerm, setSearchTerm] = useState('');
20
+ const [highlightedIndex, setHighlightedIndex] = useState(0);
21
+ const inputRef = useRef<HTMLInputElement>(null);
22
+ const dropdownRef = useRef<HTMLDivElement>(null);
23
+
24
+ const filteredOptions = useMemo(() => {
25
+ if (!searchTerm) return options;
26
+ return options.filter((option) =>
27
+ option.label.toLowerCase().includes(searchTerm.toLowerCase())
28
+ );
29
+ }, [options, searchTerm]);
30
+
31
+ const selectedOption = options.find((opt) => opt.value === value);
32
+
33
+ useEffect(() => {
34
+ const handleClickOutside = (event: MouseEvent) => {
35
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
36
+ setIsOpen(false);
37
+ setSearchTerm('');
38
+ }
39
+ };
40
+
41
+ document.addEventListener('mousedown', handleClickOutside);
42
+ return () => document.removeEventListener('mousedown', handleClickOutside);
43
+ }, []);
44
+
45
+ useEffect(() => {
46
+ setHighlightedIndex(0);
47
+ }, [filteredOptions]);
48
+
49
+ const handleSelect = (option: SelectOption) => {
50
+ if (option.disabled) return;
51
+ onChange?.(option.value, option);
52
+ setIsOpen(false);
53
+ setSearchTerm('');
54
+ };
55
+
56
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
57
+ setSearchTerm(e.target.value);
58
+ if (!isOpen) setIsOpen(true);
59
+ };
60
+
61
+ const handleKeyDown = (e: React.KeyboardEvent) => {
62
+ if (!isOpen) {
63
+ if (e.key === 'ArrowDown' || e.key === 'Enter') {
64
+ setIsOpen(true);
65
+ }
66
+ return;
67
+ }
68
+
69
+ switch (e.key) {
70
+ case 'ArrowDown':
71
+ e.preventDefault();
72
+ setHighlightedIndex((prev) => (prev < filteredOptions.length - 1 ? prev + 1 : prev));
73
+ break;
74
+ case 'ArrowUp':
75
+ e.preventDefault();
76
+ setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : prev));
77
+ break;
78
+ case 'Enter':
79
+ e.preventDefault();
80
+ if (filteredOptions[highlightedIndex]) {
81
+ handleSelect(filteredOptions[highlightedIndex]);
82
+ }
83
+ break;
84
+ case 'Escape':
85
+ setIsOpen(false);
86
+ setSearchTerm('');
87
+ break;
88
+ }
89
+ };
90
+
91
+ const getButtonClasses = () => {
92
+ const baseClasses = [
93
+ styles.selectButton,
94
+ styles[size],
95
+ styles[variant],
96
+ disabled ? styles.disabled : '',
97
+ error ? styles.error : '',
98
+ className,
99
+ ]
100
+ .filter(Boolean)
101
+ .join(' ');
102
+
103
+ return baseClasses;
104
+ };
105
+
106
+ const getWidthClass = () => {
107
+ switch (width) {
108
+ case 'w-64':
109
+ return styles.width64;
110
+ case 'w-80':
111
+ return styles.width80;
112
+ case 'w-96':
113
+ return styles.width96;
114
+ default:
115
+ return styles.widthFull;
116
+ }
117
+ };
118
+
119
+ const getOptionClasses = (option: SelectOption, index: number) => {
120
+ return [
121
+ styles.option,
122
+ index === highlightedIndex ? styles.optionHighlighted : '',
123
+ option.value === value ? styles.optionSelected : '',
124
+ option.disabled ? styles.optionDisabled : '',
125
+ ]
126
+ .filter(Boolean)
127
+ .join(' ');
128
+ };
129
+
130
+ return (
131
+ <div className={`${styles.container} ${getWidthClass()}`} ref={dropdownRef}>
132
+ <button
133
+ className={getButtonClasses()}
134
+ onClick={() => !disabled && setIsOpen(!isOpen)}
135
+ onKeyDown={handleKeyDown}
136
+ tabIndex={disabled ? -1 : 0}
137
+ type='button'
138
+ aria-haspopup='listbox'
139
+ aria-expanded={isOpen}
140
+ disabled={disabled}
141
+ >
142
+ <div className={styles.buttonContent}>
143
+ <span className={`${styles.label} ${!selectedOption ? styles.placeholder : ''}`}>
144
+ {selectedOption ? selectedOption.label : placeholder}
145
+ </span>
146
+ <div className={styles.iconsContainer}>
147
+ {loading && <div className={styles.loadingSpinner} />}
148
+ <svg
149
+ className={`${styles.arrowIcon} ${isOpen ? styles.arrowIconOpen : ''}`}
150
+ fill='none'
151
+ stroke='currentColor'
152
+ viewBox='0 0 24 24'
153
+ >
154
+ <path
155
+ strokeLinecap='round'
156
+ strokeLinejoin='round'
157
+ strokeWidth={2}
158
+ d='M19 9l-7 7-7-7'
159
+ />
160
+ </svg>
161
+ </div>
162
+ </div>
163
+ </button>
164
+
165
+ {isOpen && (
166
+ <div className={`${styles.dropdown} ${getWidthClass()}`}>
167
+ <div className={styles.searchContainer}>
168
+ <input
169
+ ref={inputRef}
170
+ type='text'
171
+ value={searchTerm}
172
+ onChange={handleInputChange}
173
+ placeholder='Rechercher...'
174
+ className={styles.searchInput}
175
+ autoFocus
176
+ />
177
+ </div>
178
+
179
+ <div className={styles.optionsList}>
180
+ {filteredOptions.length === 0 ? (
181
+ <div className={styles.noResults}>Aucun résultat trouvé</div>
182
+ ) : (
183
+ <ul role='listbox'>
184
+ {filteredOptions.map((option, index) => (
185
+ <li
186
+ key={option.value}
187
+ className={getOptionClasses(option, index)}
188
+ onClick={() => handleSelect(option)}
189
+ onMouseEnter={() => setHighlightedIndex(index)}
190
+ role='option'
191
+ aria-selected={option.value === value}
192
+ aria-disabled={option.disabled}
193
+ >
194
+ <div className={styles.buttonContent}>
195
+ <span className={styles.optionLabel} title={option.label}>
196
+ {option.label}
197
+ </span>
198
+ {option.value === value && (
199
+ <svg className={styles.checkIcon} fill='currentColor' viewBox='0 0 20 20'>
200
+ <path
201
+ fillRule='evenodd'
202
+ d='M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z'
203
+ clipRule='evenodd'
204
+ />
205
+ </svg>
206
+ )}
207
+ </div>
208
+ </li>
209
+ ))}
210
+ </ul>
211
+ )}
212
+ </div>
213
+ </div>
214
+ )}
215
+ </div>
216
+ );
217
+ };
218
+
219
+ export default SelectFilter;
@@ -0,0 +1,2 @@
1
+ export { default as SelectFilter } from './SelectFilter';
2
+ export type { SelectFilterProps, SelectOption } from './types';
@@ -0,0 +1,19 @@
1
+ export interface SelectOption {
2
+ value: string | number;
3
+ label: string;
4
+ disabled?: boolean;
5
+ }
6
+
7
+ export interface SelectFilterProps {
8
+ options: SelectOption[];
9
+ value?: string | number;
10
+ onChange?: (value: string | number, option: SelectOption) => void;
11
+ placeholder?: string;
12
+ disabled?: boolean;
13
+ loading?: boolean;
14
+ error?: boolean;
15
+ className?: string;
16
+ size?: 'sm' | 'md' | 'lg';
17
+ variant?: 'default' | 'outline' | 'filled';
18
+ width?: 'w-full' | 'w-64' | 'w-80' | 'w-96';
19
+ }