@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,282 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* MetricsPanel.tsx (réorganisé — importe les sous-composants) */
3
+ import React, { useMemo } from 'react';
4
+ import styles from './MetricsPanel.module.css';
5
+ import MetricsItem from './MetricsItem';
6
+ import PanelHeader from './PanelHeader';
7
+ import chooseDefaultRender from './tools/chooseDefaultRender';
8
+ import SummaryCard from './SummaryCard';
9
+ import { hexToRgba as utilHexToRgba } from './tools/colorUtils';
10
+ import type { MetricsPanelProps, MetricsItem as ItemType } from './tools/MetricsPanelTypes';
11
+ import { Grid } from '../Grid';
12
+ const DEFAULT_SUMMARY_BG_COLORS = ['transparent', 'transparent', 'transparent', 'transparent'];
13
+ const DEFAULT_SUMMARY_BORDER_COLORS = ['transparent', 'transparent', 'transparent', 'transparent'];
14
+ const DEFAULT_SUMMARY_VALUE_COLORS = ['#059669', '#2563eb', '#b45309', '#7c3aed'];
15
+
16
+ const hexToRgba = (c: string | undefined | null, a = 1) => {
17
+ try {
18
+ return utilHexToRgba(c as any, a);
19
+ } catch {
20
+ return `rgba(124, 58, 237, ${a})`;
21
+ }
22
+ };
23
+
24
+ type LocalProps = {
25
+ itemsContainerStyle?: React.CSSProperties;
26
+ itemsContainerClassName?: string;
27
+ summaryGridColumns?: number;
28
+
29
+ compactItemWidth?: string | number;
30
+ compactItemHeight?: string | number;
31
+ compactItemStyle?: React.CSSProperties;
32
+
33
+ summaryCardWidth?: string | number;
34
+ summaryCardHeight?: string | number;
35
+ summaryCardStyle?: React.CSSProperties;
36
+
37
+ grid?: boolean;
38
+ orderGrid?: number[];
39
+ draggableGrid?: boolean;
40
+ onSwapGrid?: (dragIndex: number, dropIndex: number) => void;
41
+ };
42
+
43
+ export const MetricsPanel: React.FC<MetricsPanelProps & LocalProps> = (props) => {
44
+ const {
45
+ items = [],
46
+ layout = 'vertical',
47
+ orientation = 'auto',
48
+ shape = 'rounded',
49
+ color = '#7c3aed',
50
+ compact = false,
51
+ title,
52
+ onItemClick,
53
+ className = '',
54
+ loading = false,
55
+ emptyMessage = 'Aucun élément',
56
+ renderItem,
57
+ summary = false,
58
+ summaryBgColors = DEFAULT_SUMMARY_BG_COLORS,
59
+ summaryBorderColors = DEFAULT_SUMMARY_BORDER_COLORS,
60
+ summaryValueColors = DEFAULT_SUMMARY_VALUE_COLORS,
61
+ preset,
62
+ background,
63
+ grid = false,
64
+ orderGrid = [],
65
+ draggableGrid = false,
66
+ onSwapGrid,
67
+ } = props as any;
68
+
69
+ const {
70
+ itemsContainerStyle,
71
+ itemsContainerClassName,
72
+ summaryGridColumns,
73
+ compactItemWidth,
74
+ compactItemHeight,
75
+ compactItemStyle,
76
+ summaryCardWidth,
77
+ summaryCardHeight,
78
+ summaryCardStyle,
79
+ } = props as LocalProps;
80
+
81
+ const isVertical = layout === 'vertical';
82
+ const rootStyle = {
83
+ ['--mp-accent' as any]: color,
84
+ ['--mp-accent-soft' as any]: hexToRgba(color, 0.08),
85
+ ['--mp-bg-primary' as any]: background,
86
+ } as React.CSSProperties & Record<string, string>;
87
+
88
+ const panelClasses = [
89
+ styles.mpRoot,
90
+ isVertical ? styles.mpVertical : styles.mpHorizontal,
91
+ compact ? styles.mpCompact : '',
92
+ summary ? styles.mpSummaryRoot : '',
93
+ orientation !== 'auto'
94
+ ? orientation === 'vertical'
95
+ ? styles.mpOrientationVertical
96
+ : styles.mpOrientationHorizontal
97
+ : '',
98
+ className,
99
+ ]
100
+ .filter(Boolean)
101
+ .join(' ');
102
+
103
+ const effectiveRenderItem = useMemo(() => {
104
+ if (renderItem) return renderItem;
105
+ const chosen = chooseDefaultRender(preset);
106
+ return chosen ?? undefined;
107
+ }, [renderItem, preset]);
108
+
109
+ if (summary) {
110
+ const summaryContainerClass =
111
+ orientation === 'horizontal' ? styles.mpSummaryFlex : styles.mpSummaryVertical;
112
+
113
+ const gridStyle: React.CSSProperties | undefined = summaryGridColumns
114
+ ? {
115
+ display: 'grid',
116
+ gridTemplateColumns: `repeat(${summaryGridColumns}, 1fr)`,
117
+ gap: 18,
118
+ alignItems: 'stretch',
119
+ width: '100%',
120
+ }
121
+ : undefined;
122
+
123
+ const mergedStyle = { ...(gridStyle ?? {}), ...(itemsContainerStyle ?? {}) };
124
+
125
+ return (
126
+ <div className={panelClasses} style={rootStyle} aria-busy={loading}>
127
+ {title && <PanelHeader title={title} />}
128
+ <div className={styles.mpSummaryInner}>
129
+ <div
130
+ className={`${summaryContainerClass} ${itemsContainerClassName ?? ''}`}
131
+ style={mergedStyle}
132
+ >
133
+ {items.length === 0 ? (
134
+ <div className={styles.mpEmpty}>{emptyMessage}</div>
135
+ ) : (
136
+ items.map((item: ItemType, i: number) => {
137
+ const bg = item.bgColor ?? summaryBgColors[i % summaryBgColors.length];
138
+ const border =
139
+ item.borderColor ?? summaryBorderColors[i % summaryBorderColors.length];
140
+ const valueColor =
141
+ item.valueColor ?? summaryValueColors[i % summaryValueColors.length];
142
+
143
+ if (effectiveRenderItem) {
144
+ return (
145
+ <div
146
+ key={item.id}
147
+ className={
148
+ orientation === 'horizontal'
149
+ ? styles.mpSummaryCardWrapperHorizontal
150
+ : styles.mpSummaryCardWrapper
151
+ }
152
+ style={{
153
+ width: summaryCardWidth ?? undefined,
154
+ height: summaryCardHeight ?? undefined,
155
+ ...(summaryCardStyle ?? {}),
156
+ }}
157
+ >
158
+ {effectiveRenderItem(item, i)}
159
+ </div>
160
+ );
161
+ }
162
+
163
+ return (
164
+ <SummaryCard
165
+ key={item.id}
166
+ item={item}
167
+ orientation={orientation === 'horizontal' ? 'horizontal' : 'vertical'}
168
+ bg={bg}
169
+ border={border}
170
+ valueColor={valueColor}
171
+ onClick={onItemClick}
172
+ width={summaryCardWidth ?? undefined}
173
+ height={summaryCardHeight ?? undefined}
174
+ extraStyle={summaryCardStyle}
175
+ />
176
+ );
177
+ })
178
+ )}
179
+ </div>
180
+ </div>
181
+ </div>
182
+ );
183
+ }
184
+
185
+ /* non-summary list mode */
186
+ return (
187
+ <div className={panelClasses} style={rootStyle} aria-busy={loading}>
188
+ {title && <PanelHeader title={title} />}
189
+
190
+ {grid ? (
191
+ <Grid
192
+ parent
193
+ container
194
+ order={orderGrid}
195
+ draggable={draggableGrid}
196
+ onSwap={onSwapGrid}
197
+ xs={12}
198
+ className={styles.mpList}
199
+ style={{ alignItems: 'stretch', height: '100%', ...itemsContainerStyle }}
200
+ >
201
+ {loading ? (
202
+ <div className={styles.mpEmpty}>Chargement…</div>
203
+ ) : items.length === 0 ? (
204
+ <div className={styles.mpEmpty}>{emptyMessage}</div>
205
+ ) : (
206
+ items.map((item: ItemType, index: number) => {
207
+ const wrapperStyle: React.CSSProperties | undefined = compact
208
+ ? {
209
+ width: compactItemWidth,
210
+ height: compactItemHeight,
211
+ boxSizing: 'border-box',
212
+ ...compactItemStyle,
213
+ }
214
+ : undefined;
215
+
216
+ return (
217
+ <Grid item xs={12} key={item.id} uniqueKey={index}>
218
+ <div
219
+ className={compact ? styles.compactItemWrapper : undefined}
220
+ style={wrapperStyle}
221
+ >
222
+ <MetricsItem
223
+ item={item}
224
+ shape={shape}
225
+ onItemClick={onItemClick}
226
+ color={color}
227
+ renderItem={
228
+ effectiveRenderItem
229
+ ? (it) => effectiveRenderItem(it, items.indexOf(it))
230
+ : undefined
231
+ }
232
+ />
233
+ </div>
234
+ </Grid>
235
+ );
236
+ })
237
+ )}
238
+ </Grid>
239
+ ) : (
240
+ <div className={styles.mpList} style={itemsContainerStyle}>
241
+ {loading ? (
242
+ <div className={styles.mpEmpty}>Chargement…</div>
243
+ ) : items.length === 0 ? (
244
+ <div className={styles.mpEmpty}>{emptyMessage}</div>
245
+ ) : (
246
+ items.map((item: ItemType) => {
247
+ const wrapperStyle: React.CSSProperties | undefined = compact
248
+ ? {
249
+ width: compactItemWidth ?? undefined,
250
+ height: compactItemHeight ?? undefined,
251
+ boxSizing: 'border-box',
252
+ ...(compactItemStyle ?? {}),
253
+ }
254
+ : undefined;
255
+
256
+ return (
257
+ <div
258
+ key={item.id}
259
+ className={compact ? styles.compactItemWrapper : undefined}
260
+ style={wrapperStyle}
261
+ >
262
+ <MetricsItem
263
+ item={item}
264
+ shape={shape}
265
+ onItemClick={onItemClick}
266
+ color={color}
267
+ renderItem={
268
+ effectiveRenderItem
269
+ ? (it) => effectiveRenderItem(it, items.indexOf(it))
270
+ : undefined
271
+ }
272
+ />
273
+ </div>
274
+ );
275
+ })
276
+ )}
277
+ </div>
278
+ )}
279
+ </div>
280
+ );
281
+ };
282
+ export default React.memo(MetricsPanel);
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import styles from './MetricsPanel.module.css';
3
+
4
+ interface PanelHeaderProps {
5
+ title?: string;
6
+ right?: React.ReactNode;
7
+ }
8
+
9
+ const PanelHeader: React.FC<PanelHeaderProps> = ({ title, right }) => {
10
+ if (!title && !right) return null;
11
+ return (
12
+ <div className={styles.mpHeader}>
13
+ <div className={styles.mpTitle}>{title}</div>
14
+ <div>{right}</div>
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export default PanelHeader;
@@ -0,0 +1,61 @@
1
+ /* SummaryCard.tsx */
2
+ import React from 'react';
3
+ import styles from './MetricsPanel.module.css';
4
+ import type { MetricsItem as ItemType } from './tools/MetricsPanelTypes';
5
+
6
+ interface Props {
7
+ item: ItemType;
8
+ orientation: 'horizontal' | 'vertical' | 'auto';
9
+ bg?: string;
10
+ border?: string;
11
+ valueColor?: string;
12
+ onClick?: (item: ItemType) => void;
13
+ width?: string | number;
14
+ height?: string | number;
15
+ extraStyle?: React.CSSProperties;
16
+ }
17
+
18
+ const SummaryCard: React.FC<Props> = ({
19
+ item,
20
+ orientation,
21
+ bg,
22
+ border,
23
+ valueColor,
24
+ onClick,
25
+ width,
26
+ height,
27
+ extraStyle,
28
+ }) => {
29
+ const isHorizontal = orientation === 'horizontal';
30
+ const className = isHorizontal ? styles.mpSummaryCardHorizontal : styles.mpSummaryCard;
31
+ return (
32
+ <div
33
+ className={className}
34
+ style={{
35
+ background: bg,
36
+ border: `1px solid ${border}`,
37
+ width: width ?? undefined,
38
+ height: height ?? undefined,
39
+ ...(extraStyle ?? {}),
40
+ }}
41
+ role={onClick ? 'button' : 'group'}
42
+ tabIndex={onClick ? 0 : -1}
43
+ onClick={() => onClick?.(item)}
44
+ >
45
+ {item.badge && (
46
+ <div className={styles.mpSummaryBadge}>
47
+ {item.badge.text}
48
+ {item.badge.subText && (
49
+ <div className={styles.mpSummaryBadgeSub}>{item.badge.subText}</div>
50
+ )}
51
+ </div>
52
+ )}
53
+ <div className={styles.mpSummaryValue} style={{ color: valueColor }}>
54
+ {item.value}
55
+ </div>
56
+ <div className={styles.mpSummaryLabel}>{item.label}</div>
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default SummaryCard;
@@ -0,0 +1,4 @@
1
+ export * from './MetricsItem';
2
+ export * from './MetricsPanel';
3
+ export * from './PanelHeader';
4
+ export * from './tools';
@@ -0,0 +1,125 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* CompactRenderer.tsx */
3
+ import React from 'react';
4
+ import { hexToRgba as utilHexToRgba } from '../tools/colorUtils';
5
+ import type { MetricsItem as ItemType } from '../tools/MetricsPanelTypes';
6
+
7
+ const hexToRgba = (c: string | undefined | null, a = 1) => {
8
+ try {
9
+ return utilHexToRgba(c as any, a);
10
+ } catch {
11
+ return `rgba(124, 58, 237, ${a})`;
12
+ }
13
+ };
14
+
15
+ const CompactRenderer = (item: ItemType) => (
16
+ <div
17
+ style={{
18
+ display: 'grid',
19
+ gridTemplateRows: 'auto 1fr',
20
+ width: '100%',
21
+ gap: 6,
22
+ alignItems: 'center',
23
+ boxSizing: 'border-box',
24
+ // allow the content to shrink nicely
25
+ minWidth: 0,
26
+ }}
27
+ >
28
+ <div
29
+ style={{
30
+ display: 'flex',
31
+ justifyContent: 'space-between-left',
32
+ alignItems: 'baseline',
33
+ gap: 8,
34
+ width: '100%',
35
+ }}
36
+ >
37
+ <div
38
+ style={{
39
+ fontWeight: 700,
40
+ fontSize: 'clamp(12px, 1.05vw, 13px)',
41
+ overflow: 'hidden',
42
+ textOverflow: 'ellipsis',
43
+ whiteSpace: 'nowrap',
44
+ minWidth: 0,
45
+ }}
46
+ >
47
+ {item.label}
48
+ </div>
49
+ {item.subLabel ? (
50
+ <div
51
+ style={{
52
+ fontSize: 'clamp(11px, 0.9vw, 12px)',
53
+ fontWeight: 600,
54
+ whiteSpace: 'nowrap',
55
+ }}
56
+ >
57
+ {item.subLabel}
58
+ </div>
59
+ ) : null}
60
+ </div>
61
+
62
+ <div
63
+ style={{
64
+ display: 'flex',
65
+ justifyContent: 'space-between',
66
+ alignItems: 'center',
67
+ gap: 12,
68
+ width: '100%',
69
+ }}
70
+ >
71
+ <div
72
+ style={{
73
+ fontWeight: 900,
74
+ fontSize: 'clamp(18px, 2.0vw, 20px)',
75
+ color: (item as any).valueColor,
76
+ lineHeight: 1,
77
+ minWidth: 0,
78
+ overflow: 'hidden',
79
+ textOverflow: 'ellipsis',
80
+ whiteSpace: 'nowrap',
81
+ }}
82
+ >
83
+ {item.value}
84
+ </div>
85
+
86
+ {item.badge ? (
87
+ <div
88
+ style={{
89
+ display: 'flex',
90
+ flexDirection: 'column',
91
+ alignItems: 'center',
92
+ justifyContent: 'center',
93
+ padding: '4px 6px',
94
+ borderRadius: 8,
95
+ backgroundColor: (item as any).badge.bg
96
+ ? (item as any).badge.bg
97
+ : hexToRgba('#7c3aed', 0.08),
98
+ border: `1px solid ${
99
+ (item as any).badge.bg ? (item as any).badge.bg : hexToRgba('#7c3aed', 0.12)
100
+ }`,
101
+ color: (item as any).badge.textColor || '#0f172a',
102
+ fontWeight: 700,
103
+ fontSize: 'clamp(10px, 1vw, 11px)',
104
+ maxWidth: '40%',
105
+ minWidth: 48,
106
+ textAlign: 'center',
107
+ boxSizing: 'border-box',
108
+ overflow: 'hidden',
109
+ }}
110
+ >
111
+ <div style={{ fontSize: 'clamp(10px, 0.9vw, 11px)', fontWeight: 800, lineHeight: 1 }}>
112
+ {(item as any).badge.text}
113
+ </div>
114
+ {(item as any).badge.subText ? (
115
+ <div style={{ fontSize: 'clamp(9px, 0.8vw, 10px)', marginTop: 2, fontWeight: 600 }}>
116
+ {(item as any).badge.subText}
117
+ </div>
118
+ ) : null}
119
+ </div>
120
+ ) : null}
121
+ </div>
122
+ </div>
123
+ );
124
+
125
+ export default CompactRenderer;
@@ -0,0 +1,62 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* ImageCard.tsx */
3
+ import React from 'react';
4
+ import { hexToRgba as utilHexToRgba } from '../tools/colorUtils';
5
+ import type { MetricsItem as ItemType } from '../tools/MetricsPanelTypes';
6
+
7
+ const hexToRgba = (c: string | undefined | null, a = 1) => {
8
+ try {
9
+ return utilHexToRgba(c as any, a);
10
+ } catch {
11
+ return `rgba(124, 58, 237, ${a})`;
12
+ }
13
+ };
14
+
15
+ const ImageCard: React.FC<{ item: ItemType }> = ({ item }) => (
16
+ <div
17
+ style={{
18
+ display: 'flex',
19
+ alignItems: 'center',
20
+ justifyContent: 'space-between',
21
+ padding: 14,
22
+ borderRadius: 12,
23
+ minHeight: 88,
24
+ boxSizing: 'border-box',
25
+ background: (item as any).bgColor ?? 'transparent',
26
+ border: `1px solid ${(item as any).borderColor ?? 'transparent'}`,
27
+ boxShadow: '0 4px 10px rgba(15,23,42,0.03)',
28
+ }}
29
+ >
30
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
31
+ <div style={{ fontSize: 13, fontWeight: 700, color: '#475569' }}>{item.label}</div>
32
+ <div style={{ fontSize: 26, fontWeight: 900, color: '#0f172a', lineHeight: 1 }}>
33
+ {item.value}
34
+ </div>
35
+ </div>
36
+
37
+ <div
38
+ title={String(item.label)}
39
+ style={{
40
+ display: 'flex',
41
+ alignItems: 'center',
42
+ justifyContent: 'center',
43
+ width: 48,
44
+ height: 48,
45
+ borderRadius: 10,
46
+ background: (item as any).iconSquareBg ?? hexToRgba('#7c3aed', 0.12),
47
+ border: `1px solid ${hexToRgba((item as any).iconColor ?? '#7c3aed', 0.12)}`,
48
+ boxShadow: 'none',
49
+ }}
50
+ >
51
+ {(item as any).iconComponent
52
+ ? React.createElement((item as any).iconComponent, {
53
+ size: 18,
54
+ color: (item as any).iconColor ?? '#7c3aed',
55
+ strokeWidth: 1.6,
56
+ })
57
+ : null}
58
+ </div>
59
+ </div>
60
+ );
61
+
62
+ export default ImageCard;
@@ -0,0 +1,55 @@
1
+ /* PertinenceCard.tsx */
2
+ import React from 'react';
3
+ import type { MetricsItem as ItemType } from '../tools/MetricsPanelTypes';
4
+ import styles from '../MetricsPanel.module.css';
5
+
6
+ interface Props {
7
+ item: ItemType;
8
+ bg?: string;
9
+ border?: string;
10
+ valueColor?: string;
11
+ onClick?: (item: ItemType) => void;
12
+ width?: string | number;
13
+ height?: string | number;
14
+ extraStyle?: React.CSSProperties;
15
+ }
16
+
17
+ const PertinenceCard: React.FC<Props> = ({ item, bg, onClick, width, height, extraStyle }) => {
18
+ return (
19
+ <div
20
+ className={styles.mpSummaryCard}
21
+ style={{
22
+ background: bg,
23
+ width: width ?? undefined,
24
+ height: height ?? undefined,
25
+ ...(extraStyle ?? {}),
26
+ }}
27
+ role={onClick ? 'button' : 'group'}
28
+ tabIndex={onClick ? 0 : -1}
29
+ onClick={() => onClick?.(item)}
30
+ >
31
+ {/* Label */}
32
+ <div className={styles.mpSummaryLabel}>{item.label}</div>
33
+
34
+ {/* Valeur */}
35
+ <div
36
+ className={styles.mpSummaryValue}
37
+ style={{ color: (item.valueColor as string) ?? '#2a24deff' }}
38
+ >
39
+ {item.value}
40
+ </div>
41
+
42
+ {/* Autre info si nécessaire */}
43
+ {item.badge && (
44
+ <div className={styles.mpSummaryBadge}>
45
+ {item.badge.text}
46
+ {item.badge.subText && (
47
+ <div className={styles.mpSummaryBadgeSub}>{item.badge.subText}</div>
48
+ )}
49
+ </div>
50
+ )}
51
+ </div>
52
+ );
53
+ };
54
+
55
+ export default PertinenceCard;
@@ -0,0 +1,62 @@
1
+ // MetricsPanelTypes.ts
2
+ import React from 'react';
3
+
4
+ export interface Badge {
5
+ text: string;
6
+ subText?: string;
7
+ color?: string; // hex, 'transparent' or any CSS color
8
+ bg?: string;
9
+ textColor?: string;
10
+ small?: boolean;
11
+ }
12
+
13
+ export interface MetricsItem {
14
+ id: string | number;
15
+ label: string;
16
+ subLabel?: string | React.ReactNode;
17
+ value: string | number | React.ReactNode;
18
+ date?: string;
19
+ icon?: React.ReactNode;
20
+ badge?: Badge;
21
+ hint?: string;
22
+ // optional visual overrides for summary-like cards
23
+ bgColor?: string;
24
+ borderColor?: string;
25
+ valueColor?: string;
26
+ }
27
+
28
+ export type Preset =
29
+ | 'default'
30
+ | 'compact'
31
+ | 'summary'
32
+ | 'image'
33
+ | 'pertinence'
34
+ | 'alternateHorizontal';
35
+
36
+ export interface MetricsPanelProps {
37
+ items: MetricsItem[];
38
+ layout?: 'vertical' | 'horizontal';
39
+ orientation?: 'auto' | 'vertical' | 'horizontal';
40
+ shape?: 'rounded' | 'pill';
41
+ color?: string;
42
+ compact?: boolean;
43
+ title?: string | React.ReactNode;
44
+ onItemClick?: (item: MetricsItem) => void;
45
+ className?: string;
46
+ loading?: boolean;
47
+ emptyMessage?: string | React.ReactNode;
48
+ renderItem?: (item: MetricsItem, index: number) => React.ReactNode;
49
+ summary?: boolean;
50
+ // Nouveaux props pour les couleurs personnalisables
51
+ summaryBgColors?: string[];
52
+ summaryBorderColors?: string[];
53
+ summaryValueColors?: string[];
54
+
55
+ // NEW: preset to auto-select rendering style so stories don't need renderItem
56
+ preset?: Preset;
57
+ itemsContainerStyle?: React.CSSProperties;
58
+ itemsContainerClassName?: string;
59
+ summaryGridColumns?: number;
60
+
61
+ background?: string;
62
+ }