@tsiky/components-r19 1.0.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 (287) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc +9 -0
  3. package/.storybook/main.ts +17 -0
  4. package/.storybook/preview.ts +21 -0
  5. package/.storybook/vitest.setup.ts +7 -0
  6. package/README.md +69 -0
  7. package/chart.ts +1 -0
  8. package/eslint.config.js +40 -0
  9. package/index.html +13 -0
  10. package/index.ts +33 -0
  11. package/package.json +63 -0
  12. package/public/vite.svg +1 -0
  13. package/src/App.css +42 -0
  14. package/src/App.tsx +12 -0
  15. package/src/assets/accessibility.png +0 -0
  16. package/src/assets/accessibility.svg +1 -0
  17. package/src/assets/addon-library.png +0 -0
  18. package/src/assets/assets.png +0 -0
  19. package/src/assets/avif-test-image.avif +0 -0
  20. package/src/assets/context.png +0 -0
  21. package/src/assets/discord.svg +1 -0
  22. package/src/assets/docs.png +0 -0
  23. package/src/assets/figma-plugin.png +0 -0
  24. package/src/assets/github.svg +1 -0
  25. package/src/assets/react.svg +1 -0
  26. package/src/assets/share.png +0 -0
  27. package/src/assets/styling.png +0 -0
  28. package/src/assets/testing.png +0 -0
  29. package/src/assets/theming.png +0 -0
  30. package/src/assets/tutorials.svg +1 -0
  31. package/src/assets/youtube.svg +1 -0
  32. package/src/components/AddItemModal/AddItemModal.module.css +72 -0
  33. package/src/components/AddItemModal/AddItemModal.tsx +82 -0
  34. package/src/components/AddItemModal/index.ts +1 -0
  35. package/src/components/Alert/Alert.css +54 -0
  36. package/src/components/Alert/Alert.stories.tsx +82 -0
  37. package/src/components/Alert/Alert.tsx +85 -0
  38. package/src/components/Alert/AlertContext.tsx +200 -0
  39. package/src/components/Alert/AlertModels.ts +34 -0
  40. package/src/components/Alert/index.ts +3 -0
  41. package/src/components/AnnouncementPanel/FlexRowContainer.css +17 -0
  42. package/src/components/AnnouncementPanel/FlexRowContainer.stories.tsx +329 -0
  43. package/src/components/AnnouncementPanel/FlexRowContainer.tsx +24 -0
  44. package/src/components/AnnouncementPanel/ListBox/CounterListBox.css +56 -0
  45. package/src/components/AnnouncementPanel/ListBox/CounterListBox.stories.tsx +292 -0
  46. package/src/components/AnnouncementPanel/ListBox/CounterListBox.tsx +106 -0
  47. package/src/components/AnnouncementPanel/ListBox/SimpleListBox.css +57 -0
  48. package/src/components/AnnouncementPanel/ListBox/SimpleListBox.stories.tsx +189 -0
  49. package/src/components/AnnouncementPanel/ListBox/SimpleListBox.tsx +138 -0
  50. package/src/components/AnnouncementPanel/ListBox/TrendListBox.css +61 -0
  51. package/src/components/AnnouncementPanel/ListBox/TrendListBox.stories.tsx +257 -0
  52. package/src/components/AnnouncementPanel/ListBox/TrendListBox.tsx +90 -0
  53. package/src/components/AnnouncementPanel/ListBox/index.ts +3 -0
  54. package/src/components/AnnouncementPanel/ListContentContainer.css +23 -0
  55. package/src/components/AnnouncementPanel/ListContentContainer.stories.tsx +212 -0
  56. package/src/components/AnnouncementPanel/ListContentContainer.tsx +33 -0
  57. package/src/components/AnnouncementPanel/index.ts +3 -0
  58. package/src/components/BandChart/index.tsx +282 -0
  59. package/src/components/Button/Button.module.css +165 -0
  60. package/src/components/Button/Button.stories.ts +132 -0
  61. package/src/components/Button/Button.tsx +55 -0
  62. package/src/components/Button/button.css +29 -0
  63. package/src/components/Button/index.ts +2 -0
  64. package/src/components/ChartContainer/ChartContainer.css +116 -0
  65. package/src/components/ChartContainer/ChartContainer.stories.tsx +159 -0
  66. package/src/components/ChartContainer/ChartContainer.tsx +155 -0
  67. package/src/components/ChartContainer/index.ts +1 -0
  68. package/src/components/Charts/area-chart-admission/AreaChartAdmission.stories.tsx +65 -0
  69. package/src/components/Charts/area-chart-admission/AreaChartAdmission.tsx +89 -0
  70. package/src/components/Charts/area-chart-admission/content.json +48 -0
  71. package/src/components/Charts/area-chart-hospitalisation/AreaChartHospitalisation.stories.tsx +141 -0
  72. package/src/components/Charts/area-chart-hospitalisation/AreaChartHospitalisation.tsx +262 -0
  73. package/src/components/Charts/area-chart-hospitalisation/content.json +55 -0
  74. package/src/components/Charts/bar-chart/BarChart.stories.tsx +50 -0
  75. package/src/components/Charts/bar-chart/BarChart.tsx +132 -0
  76. package/src/components/Charts/bar-chart/content.json +15 -0
  77. package/src/components/Charts/boxplot-chart/BoxPlotChart.stories.tsx +63 -0
  78. package/src/components/Charts/boxplot-chart/BoxPlotChart.tsx +114 -0
  79. package/src/components/Charts/boxplot-chart/boxUtils.ts +22 -0
  80. package/src/components/Charts/boxplot-chart/content.json +11 -0
  81. package/src/components/Charts/mixed-chart/MixedChart.stories.tsx +83 -0
  82. package/src/components/Charts/mixed-chart/MixedChart.tsx +625 -0
  83. package/src/components/Charts/mixed-chart/content.json +34 -0
  84. package/src/components/Charts/sankey-adaptation/sankey.tsx +70 -0
  85. package/src/components/Charts/sankey-chart/SankeyChart.stories.tsx +69 -0
  86. package/src/components/Charts/sankey-chart/SankeyChart.tsx +155 -0
  87. package/src/components/Charts/sankey-chart/content.json +15 -0
  88. package/src/components/Charts/stacked-column/StackedColumn.stories.tsx +72 -0
  89. package/src/components/Charts/stacked-column/StackedColumn.tsx +406 -0
  90. package/src/components/Charts/stacked-column/content.json +37 -0
  91. package/src/components/Charts/stacked-column-one-hundred-percent/StackedColumnOneHundredPercent.stories.tsx +43 -0
  92. package/src/components/Charts/stacked-column-one-hundred-percent/StackedColumnOneHundredPercent.tsx +75 -0
  93. package/src/components/Charts/stacked-column-one-hundred-percent/content.json +6 -0
  94. package/src/components/CircularProgress/CircularProgress.css +79 -0
  95. package/src/components/CircularProgress/CircularProgress.stories.tsx +251 -0
  96. package/src/components/CircularProgress/CircularProgress.tsx +101 -0
  97. package/src/components/CircularProgress/index.ts +2 -0
  98. package/src/components/Configure.mdx +369 -0
  99. package/src/components/DayStatCard/DayStatCard.css +50 -0
  100. package/src/components/DayStatCard/DayStatCard.stories.tsx +273 -0
  101. package/src/components/DayStatCard/DayStatCard.tsx +69 -0
  102. package/src/components/DayStatCard/index.ts +2 -0
  103. package/src/components/DraggableSwitcher/DraggableSwitcherButton.tsx +58 -0
  104. package/src/components/DraggableSwitcher/context/useDraggableSwitcher.tsx +45 -0
  105. package/src/components/DraggableSwitcher/index.ts +2 -0
  106. package/src/components/DropdownMenu/DropdownMenu.css +100 -0
  107. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +174 -0
  108. package/src/components/DropdownMenu/DropdownMenu.tsx +106 -0
  109. package/src/components/DropdownMenu/index.ts +1 -0
  110. package/src/components/DynamicForm/DynamicFom.stories.ts +773 -0
  111. package/src/components/DynamicForm/DynamicForm.module.css +468 -0
  112. package/src/components/DynamicForm/DynamicForm.tsx +224 -0
  113. package/src/components/DynamicForm/index.ts +3 -0
  114. package/src/components/DynamicForm/tools/form-metadata.ts +82 -0
  115. package/src/components/DynamicForm/tools/validation.ts +168 -0
  116. package/src/components/DynamicInput/DynamicInput.module.css +126 -0
  117. package/src/components/DynamicInput/DynamicInput.stories.ts +350 -0
  118. package/src/components/DynamicInput/DynamicInput.tsx +144 -0
  119. package/src/components/DynamicInput/index.ts +2 -0
  120. package/src/components/DynamicInput/input/CheckboxInput.tsx +41 -0
  121. package/src/components/DynamicInput/input/ColorInput.tsx +46 -0
  122. package/src/components/DynamicInput/input/DateInput.tsx +57 -0
  123. package/src/components/DynamicInput/input/FileInput.tsx +174 -0
  124. package/src/components/DynamicInput/input/InputWrapper.tsx +26 -0
  125. package/src/components/DynamicInput/input/RadioInput.tsx +46 -0
  126. package/src/components/DynamicInput/input/RangeInput.tsx +46 -0
  127. package/src/components/DynamicInput/input/SelectInput.tsx +75 -0
  128. package/src/components/DynamicInput/input/TextInput.tsx +101 -0
  129. package/src/components/DynamicInput/input/TextareaInput.tsx +47 -0
  130. package/src/components/DynamicInput/input/assets/ColorInput.module.css +48 -0
  131. package/src/components/DynamicInput/input/assets/DateInput.module.css +83 -0
  132. package/src/components/DynamicInput/input/assets/FileInput.module.css +133 -0
  133. package/src/components/DynamicInput/input/assets/RangeInput.module.css +169 -0
  134. package/src/components/DynamicInput/input/assets/SelectInput.module.css +95 -0
  135. package/src/components/DynamicInput/input/index.ts +10 -0
  136. package/src/components/DynamicTable/AdvancedFilters.tsx +196 -0
  137. package/src/components/DynamicTable/ColumnSorter.tsx +185 -0
  138. package/src/components/DynamicTable/Pagination.tsx +115 -0
  139. package/src/components/DynamicTable/TableBody.tsx +42 -0
  140. package/src/components/DynamicTable/TableCell.tsx +30 -0
  141. package/src/components/DynamicTable/TableHeader.tsx +34 -0
  142. package/src/components/DynamicTable/TableRow.tsx +56 -0
  143. package/src/components/DynamicTable/TableauDynamic.stories.ts +1014 -0
  144. package/src/components/DynamicTable/TableauDynamique.module.css +1287 -0
  145. package/src/components/DynamicTable/TableauDynamique.tsx +154 -0
  146. package/src/components/DynamicTable/filters/BooleanFilter.tsx +30 -0
  147. package/src/components/DynamicTable/filters/DateFilter.tsx +28 -0
  148. package/src/components/DynamicTable/filters/DateRangeFilter.tsx +51 -0
  149. package/src/components/DynamicTable/filters/FilterRenderer.tsx +117 -0
  150. package/src/components/DynamicTable/filters/MultiSelectFilter.tsx +59 -0
  151. package/src/components/DynamicTable/filters/NumberFilter.tsx +37 -0
  152. package/src/components/DynamicTable/filters/OperatorFilter.tsx +64 -0
  153. package/src/components/DynamicTable/filters/SelectFilter.tsx +69 -0
  154. package/src/components/DynamicTable/filters/TextFilter.tsx +39 -0
  155. package/src/components/DynamicTable/filters/index.ts +9 -0
  156. package/src/components/DynamicTable/hooks/index.ts +2 -0
  157. package/src/components/DynamicTable/hooks/useAsyncActions.ts +36 -0
  158. package/src/components/DynamicTable/hooks/useFilters.ts +142 -0
  159. package/src/components/DynamicTable/hooks/useTableData.ts +216 -0
  160. package/src/components/DynamicTable/index.ts +11 -0
  161. package/src/components/DynamicTable/tools/filterTypes.ts +118 -0
  162. package/src/components/DynamicTable/tools/index.ts +3 -0
  163. package/src/components/DynamicTable/tools/tableConfig.ts +96 -0
  164. package/src/components/DynamicTable/tools/tableTypes.ts +63 -0
  165. package/src/components/EntryControl/EntryControl.module.css +218 -0
  166. package/src/components/EntryControl/EntryControl.stories.tsx +71 -0
  167. package/src/components/EntryControl/EntryControl.tsx +117 -0
  168. package/src/components/EntryControl/index.ts +2 -0
  169. package/src/components/Grid/Grid.stories.tsx +94 -0
  170. package/src/components/Grid/Grid.tsx +214 -0
  171. package/src/components/Grid/grid.css +285 -0
  172. package/src/components/Grid/index.ts +2 -0
  173. package/src/components/Header/Header.stories.tsx +164 -0
  174. package/src/components/Header/Header.tsx +59 -0
  175. package/src/components/Header/header.css +31 -0
  176. package/src/components/Header/index.ts +1 -0
  177. package/src/components/IconText/IconText..stories.tsx +135 -0
  178. package/src/components/IconText/IconText.css +43 -0
  179. package/src/components/IconText/IconText.tsx +43 -0
  180. package/src/components/IconText/index.ts +1 -0
  181. package/src/components/LanguageSelector/LanguageSelector.css +31 -0
  182. package/src/components/LanguageSelector/LanguageSelector.stories.tsx +38 -0
  183. package/src/components/LanguageSelector/LanguageSelector.tsx +37 -0
  184. package/src/components/LanguageSelector/index.ts +1 -0
  185. package/src/components/Logo/Logo.css +89 -0
  186. package/src/components/Logo/Logo.stories.tsx +79 -0
  187. package/src/components/Logo/Logo.tsx +22 -0
  188. package/src/components/Logo/index.ts +2 -0
  189. package/src/components/MetricsPanel/MetricsItem.tsx +128 -0
  190. package/src/components/MetricsPanel/MetricsPanel.module.css +636 -0
  191. package/src/components/MetricsPanel/MetricsPanel.stories.tsx +692 -0
  192. package/src/components/MetricsPanel/MetricsPanel.tsx +282 -0
  193. package/src/components/MetricsPanel/PanelHeader.tsx +19 -0
  194. package/src/components/MetricsPanel/SummaryCard.tsx +61 -0
  195. package/src/components/MetricsPanel/index.ts +4 -0
  196. package/src/components/MetricsPanel/renderers/CompactRenderer.tsx +125 -0
  197. package/src/components/MetricsPanel/renderers/ImageCard.tsx +62 -0
  198. package/src/components/MetricsPanel/renderers/PertinenceCard.tsx +55 -0
  199. package/src/components/MetricsPanel/tools/MetricsPanelTypes.ts +62 -0
  200. package/src/components/MetricsPanel/tools/chooseDefaultRender.ts +50 -0
  201. package/src/components/MetricsPanel/tools/colorUtils.ts +39 -0
  202. package/src/components/MetricsPanel/tools/index.ts +2 -0
  203. package/src/components/ModuleHeader/ModuleHeader.css +37 -0
  204. package/src/components/ModuleHeader/ModuleHeader.stories.tsx +37 -0
  205. package/src/components/ModuleHeader/ModuleHeader.tsx +42 -0
  206. package/src/components/ModuleHeader/index.ts +1 -0
  207. package/src/components/ModuleSideBar/ModuleSideBar.css +227 -0
  208. package/src/components/ModuleSideBar/ModuleSideBar.stories.tsx +40 -0
  209. package/src/components/ModuleSideBar/ModuleSideBar.tsx +155 -0
  210. package/src/components/ModuleSideBar/index.ts +1 -0
  211. package/src/components/NavBar/NavBar.css +58 -0
  212. package/src/components/NavBar/NavBar.stories.tsx +169 -0
  213. package/src/components/NavBar/NavBar.tsx +100 -0
  214. package/src/components/NavBar/NavContext.tsx +30 -0
  215. package/src/components/NavBar/index.ts +2 -0
  216. package/src/components/NavItem/NavItem.css +29 -0
  217. package/src/components/NavItem/NavItem.tsx +58 -0
  218. package/src/components/NavItem/index.ts +1 -0
  219. package/src/components/Page/Dashboard.tsx +93 -0
  220. package/src/components/Page/Page.stories.ts +33 -0
  221. package/src/components/Page/Page.tsx +73 -0
  222. package/src/components/Page/page.css +81 -0
  223. package/src/components/PerformanceCard/PerformanceCard.module.css +232 -0
  224. package/src/components/PerformanceCard/PerformanceCard.stories.tsx +441 -0
  225. package/src/components/PerformanceCard/PerformanceCard.tsx +198 -0
  226. package/src/components/PerformanceCard/defaultHistogramRenderer.tsx +32 -0
  227. package/src/components/PerformanceCard/tools/types.ts +50 -0
  228. package/src/components/PerformanceCard/tools/usePerformanceCard.ts +93 -0
  229. package/src/components/PeriodRange/PeriodRange.module.css +158 -0
  230. package/src/components/PeriodRange/PeriodRange.stories.tsx +66 -0
  231. package/src/components/PeriodRange/PeriodRange.tsx +130 -0
  232. package/src/components/PeriodSelect/PeriodSelect.module.css +65 -0
  233. package/src/components/PeriodSelect/PeriodSelect.stories.tsx +40 -0
  234. package/src/components/PeriodSelect/PeriodSelect.tsx +42 -0
  235. package/src/components/PeriodSelect/index.ts +1 -0
  236. package/src/components/ScrollableHorizontale/ScrollableHorizontale.css +40 -0
  237. package/src/components/ScrollableHorizontale/ScrollableHorizontale.stories.tsx +133 -0
  238. package/src/components/ScrollableHorizontale/ScrollableHorizontale.tsx +29 -0
  239. package/src/components/ScrollableHorizontale/index.ts +1 -0
  240. package/src/components/SearchBar/SearchBar.css +40 -0
  241. package/src/components/SearchBar/SearchBar.stories.tsx +36 -0
  242. package/src/components/SearchBar/SearchBar.tsx +30 -0
  243. package/src/components/SearchBar/index.ts +1 -0
  244. package/src/components/SectionTitle/SectionTitle.css +21 -0
  245. package/src/components/SectionTitle/SectionTitle.stories.tsx +39 -0
  246. package/src/components/SectionTitle/SectionTitle.tsx +18 -0
  247. package/src/components/SideComponent/PatientEditor.tsx +64 -0
  248. package/src/components/SideComponent/SideComponent.css +179 -0
  249. package/src/components/SideComponent/SideComponent.stories.tsx +547 -0
  250. package/src/components/SideComponent/SideComponent.tsx +243 -0
  251. package/src/components/SideComponent/hooks/useBodyScrollLock.ts +15 -0
  252. package/src/components/SideComponent/index.ts +2 -0
  253. package/src/components/SideComponent/portal.ts +11 -0
  254. package/src/components/SubNavBar/SubNavBar.tsx +41 -0
  255. package/src/components/SubNavBar/index.ts +1 -0
  256. package/src/components/Switcher/Switcher.css +65 -0
  257. package/src/components/Switcher/Switcher.stories.tsx +153 -0
  258. package/src/components/Switcher/Switcher.tsx +83 -0
  259. package/src/components/Switcher/index.ts +1 -0
  260. package/src/components/Title/Title.stories.tsx +18 -0
  261. package/src/components/Title/Title.tsx +26 -0
  262. package/src/components/Title/index.ts +1 -0
  263. package/src/components/TranslationKey/TranslationKey.css +272 -0
  264. package/src/components/TranslationKey/TranslationKey.stories.tsx +50 -0
  265. package/src/components/TranslationKey/TranslationKey.tsx +245 -0
  266. package/src/components/TranslationKey/index.ts +1 -0
  267. package/src/components/TrendItem/TrendItem.css +36 -0
  268. package/src/components/TrendItem/TrendItem.stories.tsx +276 -0
  269. package/src/components/TrendItem/TrendItem.tsx +46 -0
  270. package/src/components/TrendItem/index.ts +1 -0
  271. package/src/components/TrendList/TrendList.css +16 -0
  272. package/src/components/TrendList/TrendList.stories.tsx +337 -0
  273. package/src/components/TrendList/TrendList.tsx +45 -0
  274. package/src/components/TrendList/index.ts +1 -0
  275. package/src/components/theme/ThemeSwitcherButton.tsx +64 -0
  276. package/src/components/theme/context/ThemeContext.tsx +61 -0
  277. package/src/components/theme/context/index.ts +2 -0
  278. package/src/components/theme/context/useThemeSwitcher.ts +18 -0
  279. package/src/components/theme/index.ts +1 -0
  280. package/src/index.css +68 -0
  281. package/src/main.tsx +10 -0
  282. package/src/vite-env.d.ts +1 -0
  283. package/tsconfig.app.json +27 -0
  284. package/tsconfig.json +4 -0
  285. package/tsconfig.node.json +25 -0
  286. package/vite.config.ts +43 -0
  287. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,82 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Alert } from './Alert';
3
+ import { alertModels } from './AlertModels';
4
+
5
+ const meta: Meta<typeof Alert> = {
6
+ title: 'Components/Alert',
7
+ component: Alert,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ size: {
11
+ control: { type: 'text' },
12
+ description: "Alert width (e.g., '100%', '300px', or number)",
13
+ },
14
+ text: { control: 'text', description: 'Main text' },
15
+ subtext: { control: 'text', description: 'Subtext' },
16
+ background: { control: 'color', description: 'Background color' },
17
+ colorTextPrimary: { control: 'color', description: 'Primary text color' },
18
+ colorTextSub: { control: 'color', description: 'Secondary text color' },
19
+ onClick: { action: 'clicked' },
20
+ onClose: { action: 'closed' },
21
+ },
22
+ };
23
+ export default meta;
24
+
25
+ type Story = StoryObj<typeof Alert>;
26
+
27
+ /**
28
+ * --- STORIES ---
29
+ */
30
+
31
+ export const Default: Story = {
32
+ args: {
33
+ text: 'This is a default alert',
34
+ subtext: 'A secondary message',
35
+ background: '#f3f4f6',
36
+ colorTextPrimary: '#111827',
37
+ colorTextSub: '#4b5563',
38
+ },
39
+ };
40
+
41
+ export const Info: Story = {
42
+ args: {
43
+ text: 'Important information',
44
+ subtext: 'Additional details',
45
+ model: alertModels.info,
46
+ },
47
+ };
48
+
49
+ export const Success: Story = {
50
+ args: {
51
+ text: 'Operation successful',
52
+ subtext: 'Everything went well',
53
+ model: alertModels.success,
54
+ },
55
+ };
56
+
57
+ export const Warning: Story = {
58
+ args: {
59
+ text: 'Attention required',
60
+ subtext: 'Please verify your actions',
61
+ model: alertModels.warning,
62
+ },
63
+ };
64
+
65
+ export const Error: Story = {
66
+ args: {
67
+ text: 'An error occurred',
68
+ subtext: 'Please try again later',
69
+ model: alertModels.error,
70
+ },
71
+ };
72
+
73
+ export const Custom: Story = {
74
+ args: {
75
+ text: 'Custom alert',
76
+ subtext: 'With direct props',
77
+ background: '#ede9fe',
78
+ colorTextPrimary: '#5b21b6',
79
+ colorTextSub: '#7c3aed',
80
+ icon: 'Bell',
81
+ },
82
+ };
@@ -0,0 +1,85 @@
1
+ import React from 'react';
2
+ import * as Icons from 'lucide-react';
3
+ import type { LucideProps } from 'lucide-react';
4
+ import './Alert.css';
5
+
6
+ export type AlertModel = {
7
+ background: string;
8
+ icon?: string;
9
+ colorTextPrimary: string;
10
+ colorTextSub?: string;
11
+ };
12
+
13
+ export type AlertProps = {
14
+ id?: string;
15
+ size?: number | string;
16
+ background?: string;
17
+ icon?: string;
18
+ text: string;
19
+ subtext?: string;
20
+ onClose?: () => void;
21
+ onClick?: () => void;
22
+ style?: React.CSSProperties;
23
+ className?: string;
24
+ colorTextPrimary?: string;
25
+ colorTextSub?: string;
26
+ model?: AlertModel;
27
+ };
28
+
29
+ export const Alert: React.FC<AlertProps> = ({
30
+ id,
31
+ size = '100%',
32
+ background,
33
+ icon,
34
+ text,
35
+ subtext,
36
+ onClose,
37
+ onClick,
38
+ style,
39
+ className = '',
40
+ colorTextPrimary,
41
+ colorTextSub,
42
+ model,
43
+ }) => {
44
+ const LucideIcon =
45
+ icon || model?.icon
46
+ ? (Icons[(icon || model?.icon) as keyof typeof Icons] as React.ComponentType<LucideProps>)
47
+ : null;
48
+
49
+ return (
50
+ <div
51
+ id={id}
52
+ className={`alert ${className}`}
53
+ style={{
54
+ width: typeof size === 'number' ? `${size}px` : size,
55
+ background: background || model?.background,
56
+ ...style,
57
+ }}
58
+ role='alert'
59
+ onClick={onClick}
60
+ >
61
+ {LucideIcon && (
62
+ <LucideIcon
63
+ className='alert-icon'
64
+ size={20}
65
+ color={colorTextPrimary || model?.colorTextPrimary}
66
+ />
67
+ )}
68
+ <div className='alert-content'>
69
+ <span className='alert-text' style={{ color: colorTextPrimary || model?.colorTextPrimary }}>
70
+ {text}
71
+ </span>
72
+ {subtext && (
73
+ <span className='alert-subtext' style={{ color: colorTextSub || model?.colorTextSub }}>
74
+ {subtext}
75
+ </span>
76
+ )}
77
+ </div>
78
+ {onClose && (
79
+ <button className='alert-close' onClick={onClose} aria-label='Fermer'>
80
+
81
+ </button>
82
+ )}
83
+ </div>
84
+ );
85
+ };
@@ -0,0 +1,200 @@
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ type ReactNode,
9
+ } from 'react';
10
+ import { createPortal } from 'react-dom';
11
+ import { Alert, type AlertModel } from './Alert';
12
+
13
+ // --- Types -------------------------------------------------
14
+ export type ShowAlertOptions = {
15
+ text: string;
16
+ subtext?: string;
17
+ model?: AlertModel;
18
+ icon?: string;
19
+ background?: string;
20
+ colorTextPrimary?: string;
21
+ colorTextSub?: string;
22
+ duration?: number; // ms (if 0 or undefined = use defaultDuration; if -1 => never auto-dismiss)
23
+ };
24
+
25
+ export type AlertItem = ShowAlertOptions & {
26
+ id: string;
27
+ createdAt: number;
28
+ };
29
+
30
+ export type AlertsContextValue = {
31
+ showAlert: (opts: ShowAlertOptions) => string;
32
+ dismissAlert: (id: string) => void;
33
+ updateAlert: (id: string, patch: Partial<ShowAlertOptions>) => void;
34
+ clearAlerts: () => void;
35
+ setContainerRef: (el: HTMLElement | null) => void;
36
+ getAlertsSnapshot: () => AlertItem[];
37
+ };
38
+
39
+ // --- Context -------------------------------------------------
40
+ const AlertsContext = createContext<AlertsContextValue | null>(null);
41
+
42
+ // --- Helpers -------------------------------------------------
43
+ const genId = (prefix = 'alert') => `${prefix}_${Math.random().toString(36).slice(2, 9)}`;
44
+
45
+ // --- Provider -------------------------------------------------
46
+ export const AlertsProvider: React.FC<{
47
+ children?: ReactNode;
48
+ defaultDuration?: number; // ms
49
+ maxAlerts?: number; // initial max limit (undefined => unlimited)
50
+ }> = ({ children, defaultDuration = 5000, maxAlerts: initialMax }) => {
51
+ const [alerts, setAlerts] = useState<AlertItem[]>([]);
52
+ const timeouts = useRef(new Map<string, number>());
53
+ const containerRef = useRef<HTMLElement | null>(null);
54
+ const externalContainerRef = useRef<HTMLElement | null>(null);
55
+ const maxAlertsRef = useRef<number | undefined>(initialMax);
56
+
57
+ useEffect(() => {
58
+ return () => {
59
+ // cleanup timeouts on unmount
60
+ timeouts.current.forEach((t) => window.clearTimeout(t));
61
+ timeouts.current.clear();
62
+ };
63
+ }, []);
64
+
65
+ // --- internal helpers
66
+ const scheduleAutoDismiss = (id: string, duration?: number) => {
67
+ // duration === -1 => never auto dismiss
68
+ const d = duration === undefined || duration === 0 ? defaultDuration : duration;
69
+ if (d === -1) return;
70
+ const t = window.setTimeout(() => {
71
+ dismissAlert(id);
72
+ }, d);
73
+ timeouts.current.set(id, t);
74
+ };
75
+
76
+ const cancelAutoDismiss = (id: string) => {
77
+ const t = timeouts.current.get(id);
78
+ if (t) {
79
+ window.clearTimeout(t);
80
+ timeouts.current.delete(id);
81
+ }
82
+ };
83
+
84
+ const showAlert = (opts: ShowAlertOptions) => {
85
+ const id = genId();
86
+ const item: AlertItem = { ...opts, id, createdAt: Date.now() };
87
+
88
+ setAlerts((prev) => {
89
+ // newest first => unshift-like
90
+ const next = [item, ...prev];
91
+ const max = maxAlertsRef.current;
92
+ if (typeof max === 'number' && max >= 0 && next.length > max) {
93
+ // remove oldest (end of array)
94
+ const removed = next.slice(max);
95
+ // clear their timeouts
96
+ removed.forEach((r) => cancelAutoDismiss(r.id));
97
+ return next.slice(0, max);
98
+ }
99
+ return next;
100
+ });
101
+
102
+ // schedule dismissal
103
+ scheduleAutoDismiss(id, opts.duration);
104
+
105
+ return id;
106
+ };
107
+
108
+ const dismissAlert = (id: string) => {
109
+ cancelAutoDismiss(id);
110
+ setAlerts((prev) => prev.filter((a) => a.id !== id));
111
+ };
112
+
113
+ const updateAlert = (id: string, patch: Partial<ShowAlertOptions>) => {
114
+ setAlerts((prev) => prev.map((a) => (a.id === id ? { ...a, ...patch } : a)));
115
+ };
116
+
117
+ const clearAlerts = () => {
118
+ timeouts.current.forEach((t) => window.clearTimeout(t));
119
+ timeouts.current.clear();
120
+ setAlerts([]);
121
+ };
122
+
123
+ const setContainerRef = (el: HTMLElement | null) => {
124
+ externalContainerRef.current = el;
125
+ };
126
+
127
+ const getAlertsSnapshot = () => alerts.slice();
128
+
129
+ const value = useMemo<AlertsContextValue>(
130
+ () => ({
131
+ showAlert,
132
+ dismissAlert,
133
+ updateAlert,
134
+ clearAlerts,
135
+ setContainerRef,
136
+ getAlertsSnapshot,
137
+ }),
138
+ // eslint-disable-next-line react-hooks/exhaustive-deps
139
+ [alerts]
140
+ );
141
+
142
+ // decide which container to render into
143
+ const target = externalContainerRef.current;
144
+ const alertsList = (
145
+ <>
146
+ {alerts.map((a) => (
147
+ <Alert
148
+ key={a.id}
149
+ id={a.id}
150
+ text={a.text}
151
+ subtext={a.subtext}
152
+ icon={a.icon}
153
+ background={a.background}
154
+ colorTextPrimary={a.colorTextPrimary}
155
+ colorTextSub={a.colorTextSub}
156
+ model={a.model}
157
+ onClose={() => dismissAlert(a.id)}
158
+ size='100%'
159
+ />
160
+ ))}
161
+ </>
162
+ );
163
+ const alertsNode = (
164
+ <div
165
+ ref={(el) => {
166
+ if (!externalContainerRef.current) containerRef.current = el;
167
+ }}
168
+ style={{
169
+ width: '100%',
170
+ display: 'flex',
171
+ flexDirection: 'column',
172
+ gap: 5,
173
+ ...(alerts.length && { padding: '20px' }),
174
+ }}
175
+ aria-live='polite'
176
+ >
177
+ {alertsList}
178
+ </div>
179
+ );
180
+
181
+ return (
182
+ <AlertsContext.Provider value={value}>
183
+ {target ? createPortal(alertsList, target) : alertsNode}
184
+ {children}
185
+ </AlertsContext.Provider>
186
+ );
187
+ };
188
+
189
+ // --- Hook -------------------------------------------------
190
+ export const useAlerts = (): AlertsContextValue => {
191
+ const ctx = useContext(AlertsContext);
192
+ if (!ctx) throw new Error('useAlerts must be used inside an AlertsProvider');
193
+ return ctx;
194
+ };
195
+
196
+ // --- Minimal CSS (you can move to Alert.css) --------------
197
+ // .alert-container { display:flex; flex-direction:column; gap:8px; align-items:stretch; padding:8px; /* decorative only - NOT positioned */ }
198
+ // .alert { border-radius:8px; padding:8px 12px; box-shadow:0 6px 18px rgba(0,0,0,0.06); display:flex; gap:8px; align-items:center; }
199
+
200
+ export default AlertsProvider;
@@ -0,0 +1,34 @@
1
+ import type { AlertModel } from './Alert';
2
+
3
+ export const alertModels: Record<string, AlertModel> = {
4
+ default: {
5
+ background: '#f3f4f6',
6
+ icon: 'Info',
7
+ colorTextPrimary: '#111827',
8
+ colorTextSub: '#4b5563',
9
+ },
10
+ info: {
11
+ background: '#dbeafe',
12
+ icon: 'Info',
13
+ colorTextPrimary: '#1e40af',
14
+ colorTextSub: '#3b82f6',
15
+ },
16
+ success: {
17
+ background: '#dcfce7',
18
+ icon: 'CheckCircle2',
19
+ colorTextPrimary: '#166534',
20
+ colorTextSub: '#22c55e',
21
+ },
22
+ warning: {
23
+ background: '#fef3c7',
24
+ icon: 'AlertTriangle',
25
+ colorTextPrimary: '#92400e',
26
+ colorTextSub: '#f59e0b',
27
+ },
28
+ error: {
29
+ background: '#fee2e2',
30
+ icon: 'XCircle',
31
+ colorTextPrimary: '#991b1b',
32
+ colorTextSub: '#ef4444',
33
+ },
34
+ };
@@ -0,0 +1,3 @@
1
+ export * from './AlertContext';
2
+ export * from './AlertModels';
3
+ export * from './Alert';
@@ -0,0 +1,17 @@
1
+ .frc-root {
2
+ display: flex;
3
+ gap: var(--frc-gap, 12px);
4
+ flex-wrap: wrap;
5
+ }
6
+
7
+ .frc-item {
8
+ box-sizing: border-box;
9
+ display: flex;
10
+ flex: 1 1 auto;
11
+ min-width: min-content;
12
+ }
13
+
14
+ .frc-item > * {
15
+ width: 100%;
16
+ min-width: 0;
17
+ }