@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,249 @@
1
+ .container {
2
+ position: relative;
3
+ width: 100%;
4
+ }
5
+
6
+ .selectButton {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: space-between;
10
+ width: 100%;
11
+ text-align: left;
12
+ border-radius: 0.5rem;
13
+ border: 1px solid #d1d5db;
14
+ background-color: white;
15
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
16
+ transition: all 0.2s ease-in-out;
17
+ cursor: pointer;
18
+ outline: none;
19
+ }
20
+
21
+ .selectButton:focus {
22
+ border-color: #3b82f6;
23
+ box-shadow: 0 0 0 2px rgb(59 130 246 / 0.5);
24
+ }
25
+
26
+ .selectButton:hover:not(.disabled) {
27
+ border-color: #9ca3af;
28
+ }
29
+
30
+ .disabled {
31
+ opacity: 0.5;
32
+ background-color: #f9fafb;
33
+ cursor: not-allowed;
34
+ }
35
+
36
+ .error {
37
+ border-color: #ef4444;
38
+ }
39
+
40
+ .error:focus {
41
+ border-color: #ef4444;
42
+ box-shadow: 0 0 0 2px rgb(239 68 68 / 0.5);
43
+ }
44
+
45
+ .default {
46
+ border-color: #d1d5db;
47
+ background-color: white;
48
+ }
49
+
50
+ .outline {
51
+ border-width: 2px;
52
+ border-color: #9ca3af;
53
+ background-color: transparent;
54
+ }
55
+
56
+ .outline:focus {
57
+ border-color: #2563eb;
58
+ }
59
+
60
+ .filled {
61
+ border: none;
62
+ background-color: #f3f4f6;
63
+ }
64
+
65
+ .filled:focus {
66
+ background-color: white;
67
+ box-shadow: 0 0 0 2px rgb(59 130 246 / 0.5);
68
+ }
69
+
70
+ .sm {
71
+ padding: 0.25rem 0.5rem;
72
+ font-size: 0.875rem;
73
+ }
74
+
75
+ .md {
76
+ padding: 0.5rem 0.75rem;
77
+ font-size: 1rem;
78
+ }
79
+
80
+ .lg {
81
+ padding: 0.75rem 1rem;
82
+ font-size: 1.125rem;
83
+ }
84
+
85
+ .buttonContent {
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: space-between;
89
+ width: 100%;
90
+ }
91
+
92
+ .label {
93
+ overflow: hidden;
94
+ text-overflow: ellipsis;
95
+ white-space: nowrap;
96
+ display: block;
97
+ }
98
+
99
+ .placeholder {
100
+ color: #6b7280;
101
+ }
102
+
103
+ .iconsContainer {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 0.5rem;
107
+ flex-shrink: 0;
108
+ margin-left: 0.5rem;
109
+ }
110
+
111
+ .loadingSpinner {
112
+ width: 1rem;
113
+ height: 1rem;
114
+ border: 2px solid #3b82f6;
115
+ border-top: 2px solid transparent;
116
+ border-radius: 50%;
117
+ animation: spin 1s linear infinite;
118
+ flex-shrink: 0;
119
+ }
120
+
121
+ @keyframes spin {
122
+ from {
123
+ transform: rotate(0deg);
124
+ }
125
+ to {
126
+ transform: rotate(360deg);
127
+ }
128
+ }
129
+
130
+ .arrowIcon {
131
+ width: 1rem;
132
+ height: 1rem;
133
+ color: #6b7280;
134
+ transition: transform 0.2s ease-in-out;
135
+ flex-shrink: 0;
136
+ }
137
+
138
+ .arrowIconOpen {
139
+ transform: rotate(180deg);
140
+ }
141
+
142
+ .dropdown {
143
+ position: absolute;
144
+ z-index: 50;
145
+ width: 100%;
146
+ margin-top: 0.25rem;
147
+ background-color: white;
148
+ border: 1px solid #d1d5db;
149
+ border-radius: 0.5rem;
150
+ box-shadow:
151
+ 0 10px 15px -3px rgb(0 0 0 / 0.1),
152
+ 0 4px 6px -4px rgb(0 0 0 / 0.1);
153
+ max-height: 15rem;
154
+ overflow: hidden;
155
+ }
156
+
157
+ .searchContainer {
158
+ padding: 0.5rem;
159
+ border-bottom: 1px solid #e5e7eb;
160
+ }
161
+
162
+ .searchInput {
163
+ width: 90%;
164
+ padding: 0.5rem 0.75rem;
165
+ font-size: 0.875rem;
166
+ border: 1px solid #d1d5db;
167
+ border-radius: 0.375rem;
168
+ outline: none;
169
+ }
170
+
171
+ .searchInput:focus {
172
+ border-color: transparent;
173
+ box-shadow: 0 0 0 2px rgb(59 130 246 / 0.5);
174
+ }
175
+
176
+ .optionsList {
177
+ overflow-y: auto;
178
+ max-height: 12rem;
179
+ padding: 0.25rem 0;
180
+ }
181
+
182
+ .noResults {
183
+ padding: 0.75rem 1rem;
184
+ font-size: 0.875rem;
185
+ color: #6b7280;
186
+ text-align: center;
187
+ }
188
+
189
+ .option {
190
+ padding: 0.5rem 1rem;
191
+ cursor: pointer;
192
+ transition:
193
+ background-color 0.15s ease-in-out,
194
+ color 0.15s ease-in-out;
195
+ display: flex;
196
+ align-items: center;
197
+ justify-content: space-between;
198
+ }
199
+
200
+ .option:hover {
201
+ background-color: #f9fafb;
202
+ }
203
+
204
+ .optionHighlighted {
205
+ background-color: #eff6ff;
206
+ color: #1d4ed8;
207
+ }
208
+
209
+ .optionSelected {
210
+ background-color: #dbeafe;
211
+ color: #1e40af;
212
+ font-weight: 500;
213
+ }
214
+
215
+ .optionDisabled {
216
+ opacity: 0.5;
217
+ cursor: not-allowed;
218
+ }
219
+
220
+ .optionLabel {
221
+ overflow: hidden;
222
+ text-overflow: ellipsis;
223
+ white-space: nowrap;
224
+ display: block;
225
+ }
226
+
227
+ .checkIcon {
228
+ width: 1rem;
229
+ height: 1rem;
230
+ color: #2563eb;
231
+ flex-shrink: 0;
232
+ margin-left: 0.5rem;
233
+ }
234
+
235
+ .widthFull {
236
+ width: 100%;
237
+ }
238
+
239
+ .width64 {
240
+ width: 16rem;
241
+ }
242
+
243
+ .width80 {
244
+ width: 20rem;
245
+ }
246
+
247
+ .width96 {
248
+ width: 24rem;
249
+ }
@@ -0,0 +1,321 @@
1
+ import React, { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import SelectFilter from './SelectFilter';
4
+ import type { SelectFilterProps, SelectOption } from './types';
5
+
6
+ const mockCountries: SelectOption[] = [
7
+ { value: 'fr', label: 'France' },
8
+ { value: 'us', label: 'United States' },
9
+ { value: 'de', label: 'Germany' },
10
+ { value: 'it', label: 'Italy' },
11
+ { value: 'es', label: 'Spain' },
12
+ { value: 'uk', label: 'United Kingdom' },
13
+ { value: 'jp', label: 'Japan' },
14
+ { value: 'ca', label: 'Canada' },
15
+ { value: 'au', label: 'Australia' },
16
+ { value: 'br', label: 'Brazil' },
17
+ ];
18
+
19
+ const mockUsers: SelectOption[] = [
20
+ { value: 1, label: 'John Doe - john@example.com' },
21
+ { value: 2, label: 'Jane Smith - jane@example.com' },
22
+ { value: 3, label: 'Bob Johnson - bob@example.com' },
23
+ { value: 4, label: 'Alice Brown - alice@example.com' },
24
+ { value: 5, label: 'Charlie Wilson - charlie@example.com' },
25
+ ];
26
+
27
+ const mockLongLabels: SelectOption[] = [
28
+ { value: 1041, label: 'MEDECINE VASCULAIRE HOSPITALISATION COMPLETE (1041)' },
29
+ { value: 1042, label: 'CARDIOLOGIE HOSPITALISATION COMPLETE (1042)' },
30
+ { value: 1043, label: 'CHIRURGIE THORACIQUE ET CARDIO-VASCULAIRE (1043)' },
31
+ { value: 1044, label: 'NEUROLOGIE HOSPITALISATION COMPLETE (1044)' },
32
+ { value: 1045, label: 'NEUROCHIRURGIE HOSPITALISATION COMPLETE (1045)' },
33
+ ];
34
+
35
+ type SelectFilterStoryProps = Omit<SelectFilterProps, 'onChange'> & {
36
+ onChange?: (value: string | number) => void;
37
+ };
38
+
39
+ const meta: Meta<SelectFilterStoryProps> = {
40
+ title: 'Components/SelectFilter',
41
+ component: SelectFilter,
42
+ parameters: {
43
+ layout: 'centered',
44
+ },
45
+ tags: ['autodocs'],
46
+ argTypes: {
47
+ size: {
48
+ control: { type: 'radio' },
49
+ options: ['sm', 'md', 'lg'],
50
+ },
51
+ variant: {
52
+ control: { type: 'select' },
53
+ options: ['default', 'outline', 'filled'],
54
+ },
55
+ disabled: {
56
+ control: { type: 'boolean' },
57
+ },
58
+ loading: {
59
+ control: { type: 'boolean' },
60
+ },
61
+ error: {
62
+ control: { type: 'boolean' },
63
+ },
64
+ width: {
65
+ control: { type: 'text' },
66
+ description: 'Classes Tailwind pour la largeur (ex: w-64, w-80, w-full)',
67
+ },
68
+ },
69
+ args: {
70
+ placeholder: 'Sélectionner une option...',
71
+ options: mockCountries,
72
+ width: 'w-80',
73
+ },
74
+ };
75
+
76
+ export default meta;
77
+ type Story = StoryObj<SelectFilterStoryProps>;
78
+
79
+ const SelectWithState = (args: SelectFilterStoryProps) => {
80
+ const [value, setValue] = useState<string | number>('');
81
+
82
+ const handleChange = (newValue: string | number, _option: SelectOption) => {
83
+ setValue(newValue);
84
+ };
85
+
86
+ return (
87
+ <div className={args.width || 'w-80'}>
88
+ <SelectFilter {...args} value={value} onChange={handleChange} />
89
+ <div className='mt-4 text-sm text-gray-600'>Valeur sélectionnée: {value || 'Aucune'}</div>
90
+ </div>
91
+ );
92
+ };
93
+
94
+ export const Default: Story = {
95
+ render: (args) => <SelectWithState {...args} />,
96
+ };
97
+
98
+ export const WithCustomPlaceholder: Story = {
99
+ render: (args) => <SelectWithState {...args} />,
100
+ args: {
101
+ placeholder: 'Choisissez un pays...',
102
+ },
103
+ };
104
+
105
+ export const Small: Story = {
106
+ render: (args) => <SelectWithState {...args} />,
107
+ args: {
108
+ size: 'sm',
109
+ },
110
+ };
111
+
112
+ export const Medium: Story = {
113
+ render: (args) => <SelectWithState {...args} />,
114
+ args: {
115
+ size: 'md',
116
+ },
117
+ };
118
+
119
+ export const Large: Story = {
120
+ render: (args) => <SelectWithState {...args} />,
121
+ args: {
122
+ size: 'lg',
123
+ },
124
+ };
125
+
126
+ export const Outline: Story = {
127
+ render: (args) => <SelectWithState {...args} />,
128
+ args: {
129
+ variant: 'outline',
130
+ },
131
+ };
132
+
133
+ export const Filled: Story = {
134
+ render: (args) => <SelectWithState {...args} />,
135
+ args: {
136
+ variant: 'filled',
137
+ },
138
+ };
139
+
140
+ export const Disabled: Story = {
141
+ args: {
142
+ disabled: true,
143
+ },
144
+ };
145
+
146
+ export const Loading: Story = {
147
+ args: {
148
+ loading: true,
149
+ },
150
+ };
151
+
152
+ export const Error: Story = {
153
+ args: {
154
+ error: true,
155
+ },
156
+ };
157
+
158
+ export const WithUserData: Story = {
159
+ render: (args) => <SelectWithState {...args} />,
160
+ args: {
161
+ options: mockUsers,
162
+ placeholder: 'Rechercher un utilisateur...',
163
+ },
164
+ };
165
+
166
+ export const WithDisabledOptions: Story = {
167
+ render: (args) => <SelectWithState {...args} />,
168
+ args: {
169
+ options: [
170
+ { value: 'fr', label: 'France' },
171
+ { value: 'us', label: 'United States' },
172
+ { value: 'de', label: 'Germany', disabled: true },
173
+ { value: 'it', label: 'Italy' },
174
+ { value: 'es', label: 'Spain', disabled: true },
175
+ ],
176
+ },
177
+ };
178
+
179
+ export const WithLongLabels: Story = {
180
+ render: (args) => <SelectWithState {...args} />,
181
+ args: {
182
+ options: mockLongLabels,
183
+ placeholder: 'Sélectionnez un service médical...',
184
+ width: 'w-96',
185
+ },
186
+ };
187
+
188
+ export const DifferentWidths: Story = {
189
+ render: () => {
190
+ const [value1, setValue1] = useState('');
191
+ const [value2, setValue2] = useState('');
192
+ const [value3, setValue3] = useState('');
193
+
194
+ return (
195
+ <div className='space-y-6 max-w-2xl'>
196
+ <div>
197
+ <label className='block text-sm font-medium text-gray-700 mb-2'>
198
+ Largeur normale (w-64)
199
+ </label>
200
+ <SelectFilter
201
+ options={mockCountries}
202
+ value={value1}
203
+ onChange={(newValue, _option) => setValue1(newValue.toString())}
204
+ placeholder='Pays...'
205
+ width='w-64'
206
+ />
207
+ </div>
208
+
209
+ <div>
210
+ <label className='block text-sm font-medium text-gray-700 mb-2'>
211
+ Largeur moyenne (w-80)
212
+ </label>
213
+ <SelectFilter
214
+ options={mockUsers}
215
+ value={value2}
216
+ onChange={(newValue, _option) => setValue2(newValue.toString())}
217
+ placeholder='Utilisateurs...'
218
+ width='w-80'
219
+ />
220
+ </div>
221
+
222
+ <div>
223
+ <label className='block text-sm font-medium text-gray-700 mb-2'>
224
+ Largeur pour labels longs (w-96)
225
+ </label>
226
+ <SelectFilter
227
+ options={mockLongLabels}
228
+ value={value3}
229
+ onChange={(newValue, _option) => setValue3(newValue.toString())}
230
+ placeholder='Services médicaux...'
231
+ width='w-96'
232
+ />
233
+ </div>
234
+
235
+ <div className='p-4 bg-gray-50 rounded-lg'>
236
+ <div className='text-sm space-y-2'>
237
+ <div>Pays sélectionné: {value1 || 'Aucun'}</div>
238
+ <div>Utilisateur sélectionné: {value2 || 'Aucun'}</div>
239
+ <div>Service sélectionné: {value3 || 'Aucun'}</div>
240
+ </div>
241
+ </div>
242
+ </div>
243
+ );
244
+ },
245
+ };
246
+
247
+ export const MultipleSelects: Story = {
248
+ render: () => {
249
+ const [country, setCountry] = useState('');
250
+ const [user, setUser] = useState('');
251
+
252
+ return (
253
+ <div className='space-y-4 w-80'>
254
+ <div>
255
+ <label className='block text-sm font-medium text-gray-700 mb-2'>Pays</label>
256
+ <SelectFilter
257
+ options={mockCountries}
258
+ value={country}
259
+ onChange={(newValue, _option) => setCountry(newValue.toString())}
260
+ placeholder='Choisir un pays'
261
+ />
262
+ </div>
263
+
264
+ <div>
265
+ <label className='block text-sm font-medium text-gray-700 mb-2'>Utilisateur</label>
266
+ <SelectFilter
267
+ options={mockUsers}
268
+ value={user}
269
+ onChange={(newValue, _option) => setUser(newValue.toString())}
270
+ placeholder='Choisir un utilisateur'
271
+ variant='outline'
272
+ />
273
+ </div>
274
+
275
+ <div className='p-4 bg-gray-50 rounded-lg'>
276
+ <div className='text-sm'>
277
+ <div>Pays sélectionné: {country || 'Aucun'}</div>
278
+ <div>Utilisateur sélectionné: {user || 'Aucun'}</div>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ );
283
+ },
284
+ };
285
+
286
+ export const AsyncLoading: Story = {
287
+ render: () => {
288
+ const [options, setOptions] = useState<SelectOption[]>([]);
289
+ const [loading, setLoading] = useState(false);
290
+ const [value, setValue] = useState('');
291
+
292
+ const loadOptions = () => {
293
+ setLoading(true);
294
+ setTimeout(() => {
295
+ setOptions(mockCountries);
296
+ setLoading(false);
297
+ }, 2000);
298
+ };
299
+
300
+ return (
301
+ <div className='w-80'>
302
+ <SelectFilter
303
+ options={options}
304
+ value={value}
305
+ onChange={(newValue, _option) => setValue(newValue.toString())}
306
+ loading={loading}
307
+ placeholder='Cliquez pour charger les options'
308
+ />
309
+ <div className='mt-2 text-xs text-gray-500'>
310
+ <button
311
+ type='button'
312
+ onClick={loadOptions}
313
+ className='text-blue-600 hover:text-blue-800 underline'
314
+ >
315
+ Charger les options
316
+ </button>
317
+ </div>
318
+ </div>
319
+ );
320
+ },
321
+ };