kelt-ui-kit-react 1.8.2 → 1.8.3

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 (245) hide show
  1. package/.github/workflows/publish.yml +31 -0
  2. package/dist/_core/type/generiqueType.d.ts +4 -0
  3. package/dist/badge/Badge.d.ts +3 -1
  4. package/dist/button/button.interface.d.ts +1 -1
  5. package/dist/dataTable/DataTable.d.ts +17 -3
  6. package/dist/dataTable/dataTable.interface.d.ts +21 -3
  7. package/dist/dataTable/dataTableActions/DataTableAction.d.ts +2 -2
  8. package/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +13 -7
  9. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.d.ts +10 -0
  10. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.d.ts +9 -0
  11. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.d.ts +9 -0
  12. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.d.ts +13 -0
  13. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.d.ts +9 -0
  14. package/dist/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.d.ts +11 -0
  15. package/dist/dataTable/dataTableFilters/DataTableFilters.d.ts +7 -0
  16. package/dist/dataTable/dataTableLoading/DataTableLoading.d.ts +1 -3
  17. package/dist/dataTable/dataTableRow/DataTableRow.d.ts +5 -3
  18. package/dist/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.d.ts +12 -0
  19. package/dist/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.d.ts +11 -0
  20. package/dist/expands/expand/expand.d.ts +1 -1
  21. package/dist/form/form.enum.d.ts +1 -0
  22. package/dist/form/input/Input.d.ts +3 -0
  23. package/dist/form/inputNumber/InputNumber.d.ts +0 -2
  24. package/dist/icon/Icon.d.ts +1 -1
  25. package/dist/icon/icon.interface.d.ts +1 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +3652 -2836
  28. package/dist/overlayPanel/OverlayPanel.d.ts +3 -1
  29. package/dist/select/Select.d.ts +4 -1
  30. package/dist/select/selectOption.interface.d.ts +1 -0
  31. package/dist/style.css +1 -1
  32. package/dist/tabs/Tabs.d.ts +2 -1
  33. package/kelt-ui-kit-react-1.6.9.tgz +0 -0
  34. package/package/README.md +46 -0
  35. package/package/index.html +20 -0
  36. package/package/package.json +56 -0
  37. package/package/public/favicon.ico +0 -0
  38. package/package/public/index.html +18 -0
  39. package/package/public/logo192.png +0 -0
  40. package/package/public/logo512.png +0 -0
  41. package/package/public/manifest.json +25 -0
  42. package/package/public/robots.txt +3 -0
  43. package/package/src/App.css +11 -0
  44. package/package/src/App.menu.tsx +249 -0
  45. package/package/src/App.routes.tsx +16 -0
  46. package/package/src/App.tsx +30 -0
  47. package/package/src/_core/hooks/HistoryProvider.tsx +25 -0
  48. package/package/src/_core/hooks/ImageChecker.tsx +26 -0
  49. package/package/src/_core/hooks/imageExist.tsx +9 -0
  50. package/package/src/_core/hooks/isEAN13.tsx +16 -0
  51. package/package/src/_core/hooks/objectToQueryParams.tsx +32 -0
  52. package/package/src/_core/hooks/useCurrency.tsx +18 -0
  53. package/package/src/_core/hooks/useDateutils.ts +64 -0
  54. package/package/src/_core/hooks/useIsMobile.ts +18 -0
  55. package/package/src/_core/hooks/useToken.tsx +19 -0
  56. package/package/src/_core/hooks/useValidate.tsx +18 -0
  57. package/package/src/_core/hooks/widgetSupport.tsx +39 -0
  58. package/package/src/action/Action.view.tsx +21 -0
  59. package/package/src/action/ButtonAction.tsx +32 -0
  60. package/package/src/action/action.css +20 -0
  61. package/package/src/badge/Badge.tsx +34 -0
  62. package/package/src/badge/Badge.view.tsx +15 -0
  63. package/package/src/badge/badge.css +55 -0
  64. package/package/src/button/Button.tsx +46 -0
  65. package/package/src/button/Button.view.tsx +85 -0
  66. package/package/src/button/button.css +53 -0
  67. package/package/src/button/button.interface.tsx +21 -0
  68. package/package/src/button/buttonActions/ButtonActions.tsx +104 -0
  69. package/package/src/button/buttonFile/ButtonFile.tsx +59 -0
  70. package/package/src/button/buttonFile/ButtonFile.view.tsx +70 -0
  71. package/package/src/button/buttonFilters/ButtonFilters.tsx +102 -0
  72. package/package/src/button/buttonFilters/buttonFilters.css +22 -0
  73. package/package/src/card/Card.tsx +125 -0
  74. package/package/src/card/Card.view.tsx +73 -0
  75. package/package/src/card/card.css +145 -0
  76. package/package/src/card/card.interface.tsx +9 -0
  77. package/package/src/card/cardAction/CardAction.tsx +135 -0
  78. package/package/src/card/cardAction/cardAction.css +10 -0
  79. package/package/src/card/cardAction.interface.tsx +10 -0
  80. package/package/src/card/hook/useCardInteractions.tsx +30 -0
  81. package/package/src/carousel/Carousel.css +44 -0
  82. package/package/src/carousel/Carousel.tsx +115 -0
  83. package/package/src/carousel/Carousel.view.tsx +13 -0
  84. package/package/src/damier/Damier.tsx +64 -0
  85. package/package/src/damier/Damier.view.tsx +31 -0
  86. package/package/src/damier/damier.css +50 -0
  87. package/package/src/damier/damierCell/DamierCell.tsx +18 -0
  88. package/package/src/damier/damierCell/damierCell.interface.tsx +5 -0
  89. package/package/src/dataTable/DataTable.tsx +259 -0
  90. package/package/src/dataTable/DataTable.view.tsx +62 -0
  91. package/package/src/dataTable/dataTable.css +30 -0
  92. package/package/src/dataTable/dataTable.interface.ts +30 -0
  93. package/package/src/dataTable/dataTableActions/DataTableAction.tsx +44 -0
  94. package/package/src/dataTable/dataTableColumn/DataTableColumn.tsx +27 -0
  95. package/package/src/dataTable/dataTableLoading/DataTableLoading.tsx +21 -0
  96. package/package/src/dataTable/dataTableRow/DataTableRow.tsx +54 -0
  97. package/package/src/datePicker/DatePicker.tsx +110 -0
  98. package/package/src/datePicker/DatePicker.view.tsx +9 -0
  99. package/package/src/datePicker/datePicker.css +77 -0
  100. package/package/src/expands/Expands.tsx +42 -0
  101. package/package/src/expands/Expands.view.tsx +90 -0
  102. package/package/src/expands/expand/expand.interface.tsx +8 -0
  103. package/package/src/expands/expand/expand.tsx +75 -0
  104. package/package/src/expands/expands.css +97 -0
  105. package/package/src/filAriane/FilAriane.tsx +57 -0
  106. package/package/src/filAriane/FilAriane.view.tsx +28 -0
  107. package/package/src/filAriane/filAriane.css +23 -0
  108. package/package/src/filAriane/filAriane.interface.tsx +6 -0
  109. package/package/src/form/Form.tsx +279 -0
  110. package/package/src/form/Form.view.tsx +59 -0
  111. package/package/src/form/form.css +62 -0
  112. package/package/src/form/form.enum.ts +13 -0
  113. package/package/src/form/form.interface.tsx +37 -0
  114. package/package/src/form/input/Input.tsx +106 -0
  115. package/package/src/form/input/input.css +26 -0
  116. package/package/src/form/inputNumber/InputNumber.tsx +132 -0
  117. package/package/src/form/inputNumber/InputNumber.view.tsx +15 -0
  118. package/package/src/form/inputPrice/InputPrice.tsx +42 -0
  119. package/package/src/form/inputPrice/InputPrice.view.tsx +15 -0
  120. package/package/src/form/textArea/TextArea.tsx +59 -0
  121. package/package/src/form/textArea/TextArea.view.tsx +34 -0
  122. package/package/src/form/textArea/textArea.css +9 -0
  123. package/package/src/grid/Grid.tsx +21 -0
  124. package/package/src/grid/Grid.view.tsx +24 -0
  125. package/package/src/grid/col/Col.tsx +15 -0
  126. package/package/src/grid/col/colStyled/ColStyled.tsx +41 -0
  127. package/package/src/grid/container/Container.tsx +8 -0
  128. package/package/src/grid/container/container.css +5 -0
  129. package/package/src/grid/grid.interface.tsx +7 -0
  130. package/package/src/grid/row/Row.tsx +12 -0
  131. package/package/src/grid/row/row.css +18 -0
  132. package/package/src/header/Header.tsx +62 -0
  133. package/package/src/header/Header.view.tsx +5 -0
  134. package/package/src/header/header.css +26 -0
  135. package/package/src/header/header.interface.tsx +10 -0
  136. package/package/src/home/Home.tsx +3 -0
  137. package/package/src/icon/Icon.tsx +6 -0
  138. package/package/src/icon/Icons.view.tsx +29 -0
  139. package/package/src/icon/icon.css +20 -0
  140. package/package/src/icon/icon.interface.tsx +6 -0
  141. package/package/src/icon/iconSize.enum.ts +7 -0
  142. package/package/src/index.css +502 -0
  143. package/package/src/index.ts +58 -0
  144. package/package/src/loader/Loader.tsx +37 -0
  145. package/package/src/loader/Loader.view.tsx +20 -0
  146. package/package/src/loader/loader.css +30 -0
  147. package/package/src/logo.svg +1 -0
  148. package/package/src/main.tsx +10 -0
  149. package/package/src/menus/Menus.tsx +42 -0
  150. package/package/src/menus/Menus.view.tsx +39 -0
  151. package/package/src/menus/menu/Menu.tsx +17 -0
  152. package/package/src/menus/menu/menu.interface.tsx +9 -0
  153. package/package/src/menus/menus.css +47 -0
  154. package/package/src/message/Message.tsx +30 -0
  155. package/package/src/message/Message.view.tsx +22 -0
  156. package/package/src/message/message.css +29 -0
  157. package/package/src/modal/Modal.tsx +59 -0
  158. package/package/src/modal/Modal.view.tsx +26 -0
  159. package/package/src/modal/modal.css +71 -0
  160. package/package/src/notFound/NotFound.tsx +3 -0
  161. package/package/src/overlayPanel/OverlayPanel.tsx +195 -0
  162. package/package/src/overlayPanel/OverlayPanel.view.tsx +25 -0
  163. package/package/src/overlayPanel/overlay.context.tsx +28 -0
  164. package/package/src/overlayPanel/overlayPanel.css +35 -0
  165. package/package/src/overlayPanel/overlayPanelStyled/OverlayPanelStyled.tsx +18 -0
  166. package/package/src/pdfViewer/PdfViewer.tsx +73 -0
  167. package/package/src/pdfViewer/PdfViewer.view.tsx +5 -0
  168. package/package/src/quantity/Quantity.tsx +103 -0
  169. package/package/src/quantity/Quantity.view.tsx +0 -0
  170. package/package/src/quantity/quantity.css +26 -0
  171. package/package/src/search/Search.tsx +161 -0
  172. package/package/src/search/Search.view.tsx +14 -0
  173. package/package/src/search/search.css +59 -0
  174. package/package/src/select/Select.tsx +103 -0
  175. package/package/src/select/Select.view.tsx +71 -0
  176. package/package/src/select/select.css +69 -0
  177. package/package/src/select/selectOption.interface.ts +4 -0
  178. package/package/src/sidebar/Sidebar.tsx +208 -0
  179. package/package/src/sidebar/Sidebar.view.tsx +17 -0
  180. package/package/src/sidebar/sidebar.css +104 -0
  181. package/package/src/sidebarData/SidebarData.tsx +19 -0
  182. package/package/src/sidebarData/SidebarData.view.tsx +19 -0
  183. package/package/src/sidebarData/sidebarData.css +27 -0
  184. package/package/src/tabs/Tabs.tsx +90 -0
  185. package/package/src/tabs/Tabs.view.tsx +20 -0
  186. package/package/src/tabs/tabs.css +24 -0
  187. package/package/src/toaster/Toaster.tsx +55 -0
  188. package/package/src/toaster/Toaster.view.tsx +24 -0
  189. package/package/src/toaster/store/useToasterStore.tsx +41 -0
  190. package/package/src/toaster/toaster.css +87 -0
  191. package/package/tsconfig.json +28 -0
  192. package/package/vite.config.ts +20 -0
  193. package/package/vite.config.ts.timestamp-1733262892554-a13dfef6e8a29.mjs +24 -0
  194. package/package/vite.config.ts.timestamp-1757847121745-e5a5bf54b81b.mjs +28 -0
  195. package/package/vite.config.ts.timestamp-1759742478362-4ac5c5a7db34b.mjs +28 -0
  196. package/package.json +1 -1
  197. package/src/_core/type/generiqueType.tsx +4 -0
  198. package/src/badge/Badge.tsx +9 -1
  199. package/src/badge/badge.css +4 -0
  200. package/src/button/Button.tsx +1 -1
  201. package/src/button/button.interface.tsx +1 -1
  202. package/src/card/card.css +23 -3
  203. package/src/card/cardAction/CardAction.tsx +4 -4
  204. package/src/dataTable/DataTable.tsx +670 -45
  205. package/src/dataTable/DataTable.view.tsx +47 -14
  206. package/src/dataTable/dataTable.css +153 -5
  207. package/src/dataTable/dataTable.interface.ts +21 -3
  208. package/src/dataTable/dataTableActions/DataTableAction.tsx +2 -2
  209. package/src/dataTable/dataTableColumn/DataTableColumn.tsx +64 -14
  210. package/src/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.tsx +36 -0
  211. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilter.css +0 -0
  212. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.tsx +39 -0
  213. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.tsx +80 -0
  214. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.tsx +89 -0
  215. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.tsx +37 -0
  216. package/src/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.tsx +42 -0
  217. package/src/dataTable/dataTableFilters/DataTableFilters.tsx +91 -0
  218. package/src/dataTable/dataTableFilters/dataTableFilters.css +16 -0
  219. package/src/dataTable/dataTableLoading/DataTableLoading.tsx +4 -15
  220. package/src/dataTable/dataTableRow/DataTableRow.tsx +60 -33
  221. package/src/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.tsx +80 -0
  222. package/src/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.tsx +123 -0
  223. package/src/expands/Expands.tsx +2 -16
  224. package/src/expands/expand/expand.tsx +6 -18
  225. package/src/form/Form.tsx +13 -12
  226. package/src/form/form.css +13 -2
  227. package/src/form/form.enum.ts +1 -0
  228. package/src/form/input/Input.tsx +61 -7
  229. package/src/form/input/input.css +23 -1
  230. package/src/form/inputNumber/InputNumber.tsx +6 -11
  231. package/src/form/inputPrice/InputPrice.tsx +2 -4
  232. package/src/icon/Icon.tsx +6 -2
  233. package/src/icon/icon.interface.tsx +1 -0
  234. package/src/index.ts +2 -0
  235. package/src/overlayPanel/OverlayPanel.tsx +23 -10
  236. package/src/overlayPanel/overlay.context.tsx +6 -4
  237. package/src/overlayPanel/overlayPanel.css +10 -1
  238. package/src/select/Select.tsx +134 -38
  239. package/src/select/select.css +65 -14
  240. package/src/select/selectOption.interface.ts +1 -0
  241. package/src/sidebar/Sidebar.tsx +9 -80
  242. package/src/sidebar/sidebar.css +1 -18
  243. package/src/tabs/Tabs.tsx +19 -1
  244. package/vite.config.ts.timestamp-1757847121745-e5a5bf54b81b.mjs +28 -28
  245. package/vite.config.ts.timestamp-1759742478362-4ac5c5a7db34b.mjs +28 -28
@@ -0,0 +1,208 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import ReactDOM from "react-dom";
3
+ import { Icon } from "../icon/Icon";
4
+ import "./sidebar.css";
5
+
6
+ export interface SidebarProps {
7
+ id?: string;
8
+ open?: boolean;
9
+ title?: string;
10
+ children?: React.ReactNode;
11
+ className?: string;
12
+ width?: string;
13
+ displayOverlay?: boolean;
14
+ closeOnClickOutside?: boolean;
15
+ onClose?: (close: boolean) => void;
16
+ }
17
+
18
+ const SWIPE_CLOSE_THRESHOLD = 60; // px à parcourir pour fermer
19
+ const SWIPE_AXIS_LOCK_THRESHOLD = 10; // px pour déterminer l'axe du geste
20
+
21
+ export const Sidebar = ({
22
+ open,
23
+ children,
24
+ title,
25
+ onClose,
26
+ className,
27
+ id,
28
+ width,
29
+ closeOnClickOutside = true,
30
+ displayOverlay = true,
31
+ }: SidebarProps): JSX.Element => {
32
+ const [isOpen, setIsOpen] = useState(open || false);
33
+ const [dragX, setDragX] = useState(0);
34
+ const [isDragging, setIsDragging] = useState(false);
35
+ const sidebarRef = useRef<HTMLDivElement | null>(null);
36
+ const rootRef = useRef<HTMLDivElement | null>(null);
37
+ const touchStartX = useRef(0);
38
+ const touchStartY = useRef(0);
39
+ const axisLocked = useRef<"x" | "y" | null>(null);
40
+
41
+ // Créer le conteneur une seule fois
42
+ useEffect(() => {
43
+ if (!rootRef.current) {
44
+ rootRef.current = document.createElement("div");
45
+ rootRef.current.id = `sidebar-root-${id || crypto.randomUUID()}`;
46
+ document.body.appendChild(rootRef.current);
47
+ }
48
+
49
+ return () => {
50
+ // Nettoyage lors du démontage
51
+ if (rootRef.current && rootRef.current.parentNode) {
52
+ document.body.removeChild(rootRef.current);
53
+ rootRef.current = null;
54
+ }
55
+ };
56
+ }, [id]);
57
+
58
+ // Synchroniser l'état `isOpen` avec la prop `open`
59
+ useEffect(() => {
60
+ setIsOpen(open || false);
61
+ }, [open]);
62
+
63
+ const toggleSidebar = useCallback(() => {
64
+ setIsOpen(false);
65
+ if (onClose) {
66
+ onClose(false);
67
+ }
68
+ }, [onClose]);
69
+
70
+ const handleClickOutside = useCallback(
71
+ (event: MouseEvent | TouchEvent) => {
72
+ if (!closeOnClickOutside) return;
73
+ if (
74
+ sidebarRef.current &&
75
+ !sidebarRef.current.contains(event.target as Node)
76
+ ) {
77
+ toggleSidebar();
78
+ }
79
+ },
80
+ [toggleSidebar, closeOnClickOutside],
81
+ );
82
+
83
+ const handleTouchStart = useCallback((event: React.TouchEvent) => {
84
+ const touch = event.touches[0];
85
+ touchStartX.current = touch.clientX;
86
+ touchStartY.current = touch.clientY;
87
+ axisLocked.current = null;
88
+ setIsDragging(false);
89
+ setDragX(0);
90
+ }, []);
91
+
92
+ const handleTouchMove = useCallback(
93
+ (event: React.TouchEvent) => {
94
+ if (!isOpen) return;
95
+
96
+ const touch = event.touches[0];
97
+ const deltaX = touch.clientX - touchStartX.current;
98
+ const deltaY = touch.clientY - touchStartY.current;
99
+
100
+ // Détermine l'axe du geste une seule fois, dès qu'on a un déplacement suffisant
101
+ if (!axisLocked.current) {
102
+ if (
103
+ Math.abs(deltaX) < SWIPE_AXIS_LOCK_THRESHOLD &&
104
+ Math.abs(deltaY) < SWIPE_AXIS_LOCK_THRESHOLD
105
+ ) {
106
+ return; // pas encore assez de mouvement pour trancher
107
+ }
108
+ axisLocked.current = Math.abs(deltaX) > Math.abs(deltaY) ? "x" : "y";
109
+ }
110
+
111
+ if (axisLocked.current !== "x") return; // c'est un scroll vertical, on laisse faire
112
+
113
+ // On gère le swipe horizontal nous-mêmes : on empêche le scroll natif
114
+ // et on bloque le bounce/scroll de la page pendant le drag
115
+ event.preventDefault();
116
+
117
+ setIsDragging(true);
118
+ // On ne permet que le swipe vers la droite (fermeture), pas de drag négatif
119
+ setDragX(Math.max(0, deltaX));
120
+ },
121
+ [isOpen],
122
+ );
123
+
124
+ const handleTouchEnd = useCallback(
125
+ (event: React.TouchEvent) => {
126
+ if (!isOpen) {
127
+ return;
128
+ }
129
+
130
+ if (axisLocked.current === "x") {
131
+ const touch = event.changedTouches[0];
132
+ const deltaX = touch.clientX - touchStartX.current;
133
+
134
+ if (deltaX > SWIPE_CLOSE_THRESHOLD) {
135
+ toggleSidebar();
136
+ }
137
+ }
138
+
139
+ setIsDragging(false);
140
+ setDragX(0);
141
+ axisLocked.current = null;
142
+ },
143
+ [isOpen, toggleSidebar],
144
+ );
145
+
146
+ const handleOverlayTouchEnd = useCallback(
147
+ (event: React.TouchEvent) => {
148
+ event.preventDefault();
149
+ toggleSidebar();
150
+ },
151
+ [toggleSidebar],
152
+ );
153
+
154
+ useEffect(() => {
155
+ if (isOpen) {
156
+ document.addEventListener("mousedown", handleClickOutside);
157
+ document.addEventListener("touchstart", handleClickOutside, {
158
+ passive: true,
159
+ });
160
+ } else {
161
+ document.removeEventListener("mousedown", handleClickOutside);
162
+ document.removeEventListener("touchstart", handleClickOutside);
163
+ }
164
+
165
+ return () => {
166
+ document.removeEventListener("mousedown", handleClickOutside);
167
+ document.removeEventListener("touchstart", handleClickOutside);
168
+ };
169
+ }, [isOpen, handleClickOutside]);
170
+
171
+ if (!rootRef.current) {
172
+ return <></>; // Ne pas tenter de rendre si le conteneur n'existe pas
173
+ }
174
+
175
+ return ReactDOM.createPortal(
176
+ <>
177
+ {isOpen && displayOverlay && (
178
+ <div
179
+ className="overlay"
180
+ onClick={toggleSidebar}
181
+ onTouchEnd={handleOverlayTouchEnd}
182
+ ></div>
183
+ )}
184
+ <div
185
+ ref={sidebarRef}
186
+ style={{
187
+ ...(width ? { width } : {}),
188
+ transform: dragX ? `translateX(${dragX}px)` : undefined,
189
+ transition: isDragging ? "none" : undefined,
190
+ }}
191
+ className={`sidebar ${isOpen ? "open" : "closed"} ${className ?? ""}`}
192
+ onTouchStart={handleTouchStart}
193
+ onTouchMove={handleTouchMove}
194
+ onTouchEnd={handleTouchEnd}
195
+ >
196
+ <div className="sidebar-header">
197
+ {title && <h2 className="flex-1">{title}</h2>}
198
+ <button className="toggle-btn" onClick={toggleSidebar}>
199
+ <Icon classIcon="bi-x-lg" />
200
+ </button>
201
+ </div>
202
+
203
+ {children && children}
204
+ </div>
205
+ </>,
206
+ rootRef.current,
207
+ );
208
+ };
@@ -0,0 +1,17 @@
1
+ import { useCallback, useState } from "react";
2
+ import { Button } from "../button/Button";
3
+ import { Sidebar } from "./Sidebar";
4
+
5
+ export const SidebarView = () => {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ const onClick = useCallback(() => {
8
+ setIsOpen(true);
9
+ }, [setIsOpen]);
10
+
11
+ return (
12
+ <>
13
+ <Button title={"open me"} onClick={onClick}></Button>
14
+ <Sidebar onClose={setIsOpen} title="Menu" open={isOpen} />
15
+ </>
16
+ );
17
+ };
@@ -0,0 +1,104 @@
1
+ .sidebar {
2
+ touch-action: pan-y;
3
+ /* laisse le scroll vertical natif, gère le X nous-même */
4
+ will-change: transform;
5
+ }
6
+
7
+ .overlay {
8
+ touch-action: none;
9
+ }
10
+
11
+ .toggle-btn {
12
+ -webkit-tap-highlight-color: transparent;
13
+ touch-action: manipulation;
14
+ }
15
+
16
+ .sidebar {
17
+ width: 200px;
18
+ height: 100vh;
19
+ background-color: var(--primary-color);
20
+ color: white;
21
+ transition: width 0.3s;
22
+ overflow: hidden;
23
+ position: fixed;
24
+ transition: transform 0.3s ease-in-out;
25
+ transform: translateX(0);
26
+ z-index: 2;
27
+
28
+
29
+
30
+ &:not(.full) {
31
+ inset: 0;
32
+ }
33
+
34
+ &.full {
35
+ width: 100%;
36
+ right: 0px;
37
+ top: 0;
38
+ }
39
+
40
+ .sidebar-header {
41
+ display: flex;
42
+ align-items: center;
43
+ padding: 1rem;
44
+ }
45
+
46
+ &.closed {
47
+ transform: translateX(-100vw);
48
+ }
49
+
50
+ .sidebar-items {
51
+ list-style-type: none;
52
+ padding: 0;
53
+
54
+ .sidebar-item {
55
+ padding: 10px;
56
+ cursor: pointer;
57
+ display: flex;
58
+ align-items: center;
59
+
60
+ .item-icon {
61
+ margin-right: 10px;
62
+ }
63
+
64
+ &:hover {
65
+ background-color: #34495e;
66
+ }
67
+ }
68
+ }
69
+
70
+ .toggle-btn {
71
+ display: flex;
72
+ width: 100%;
73
+ position: absolute;
74
+ right: 10px;
75
+ top: 10px;
76
+ justify-content: flex-end;
77
+ background-color: transparent;
78
+ border: none;
79
+ color: white;
80
+ cursor: pointer;
81
+ font-size: 18px;
82
+ margin: 0;
83
+ min-width: 10px;
84
+ width: auto;
85
+ padding: 0px;
86
+ }
87
+ }
88
+
89
+ .overlay {
90
+ background-color: #0006;
91
+ opacity: 30%;
92
+ width: 100%;
93
+ position: fixed;
94
+ inset: 0;
95
+ z-index: 1;
96
+ }
97
+
98
+ @media screen and (min-width: 992px) {
99
+ .sidebar {
100
+ &.full {
101
+ width: 40vw;
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,19 @@
1
+ import { Sidebar, SidebarProps } from "../sidebar/Sidebar";
2
+ import "./sidebarData.css";
3
+
4
+ interface SidebarDataProps extends SidebarProps {
5
+ children?: React.ReactNode;
6
+ classNameContent?: string;
7
+ }
8
+
9
+ export const SidebarData = (props: SidebarDataProps) => {
10
+ return (
11
+ <Sidebar {...props} className={`full sidebar-data ${props.className}`}>
12
+ {props.children && (
13
+ <div className={`sidebar-data-content ${props.classNameContent} `}>
14
+ {props.children}
15
+ </div>
16
+ )}
17
+ </Sidebar>
18
+ );
19
+ };
@@ -0,0 +1,19 @@
1
+ import { useCallback, useState } from "react";
2
+ import { Button } from "../button/Button";
3
+ import { SidebarData } from "./SidebarData";
4
+
5
+ export const SidebarDataView = () => {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ const onClick = useCallback(() => {
8
+ setIsOpen(true);
9
+ }, [setIsOpen]);
10
+ const onClose = useCallback(() => {
11
+ setIsOpen(false);
12
+ }, [setIsOpen]);
13
+ return (
14
+ <>
15
+ <Button title={"open me"} onClick={onClick}></Button>
16
+ <SidebarData onClose={onClose} width="20vw" title="Infos" open={isOpen} />
17
+ </>
18
+ );
19
+ };
@@ -0,0 +1,27 @@
1
+ .sidebar {
2
+ &.sidebar-data {
3
+ background-color: #fff;
4
+ box-shadow: -4px 0 15px rgba(0, 0, 0, 0.3);
5
+ color: #333;
6
+ display: flex;
7
+ flex-direction: column;
8
+ &.closed {
9
+ transform: translateX(100vw);
10
+ }
11
+
12
+ .toggle-btn {
13
+ color: #333;
14
+ }
15
+
16
+ .sidebar-data-content {
17
+ padding: 1rem;
18
+ }
19
+ }
20
+ }
21
+ @media screen and (max-width: 992px) {
22
+ .sidebar {
23
+ &.sidebar-data {
24
+ width: 100% !important;
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,90 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import "./tabs.css";
3
+ export type TabItem = {
4
+ key: string;
5
+ title: React.ReactNode;
6
+ panel: React.ReactNode;
7
+ disabled?: boolean;
8
+ };
9
+
10
+ export type TabsProps = {
11
+ items: TabItem[];
12
+ defaultIndex?: number;
13
+ index?: number; // if provided, component is controlled
14
+ onChange?: (nextIndex: number) => void;
15
+ className?: string;
16
+ tabClassName?: string;
17
+ panelClassName?: string;
18
+ };
19
+
20
+ export const Tabs = ({
21
+ items,
22
+ defaultIndex = 0,
23
+ index,
24
+ onChange,
25
+ className = "",
26
+ tabClassName = "",
27
+ panelClassName = "",
28
+ }: TabsProps) => {
29
+ const isControlled = typeof index === "number";
30
+ const [active, setActive] = useState<number>(defaultIndex);
31
+ const current = isControlled ? (index as number) : active;
32
+
33
+ useEffect(() => {
34
+ // clamp
35
+ if (!isControlled && (defaultIndex < 0 || defaultIndex >= items.length)) {
36
+ setActive(0);
37
+ }
38
+ }, [defaultIndex, items.length, isControlled]);
39
+
40
+ const tabsRef = useRef<Array<HTMLButtonElement | null>>([]);
41
+
42
+ const activate = (next: number) => {
43
+ if (items[next]?.disabled) return;
44
+ if (isControlled) {
45
+ onChange?.(next);
46
+ } else {
47
+ setActive(next);
48
+ onChange?.(next);
49
+ }
50
+ tabsRef.current[next]?.focus();
51
+ };
52
+
53
+ return (
54
+ <div className={`tabs-root ${className}`}>
55
+ <div role="tablist" className="tabs-header">
56
+ {items.map((t, i) => (
57
+ <button
58
+ key={`tab-${t.key}`}
59
+ ref={(r) => (tabsRef.current[i] = r)}
60
+ role="tab"
61
+ id={`tab-${t.key}`}
62
+ aria-selected={current === i}
63
+ aria-controls={`panel-${t.key}`}
64
+ tabIndex={current === i ? 0 : -1}
65
+ disabled={t.disabled}
66
+ onClick={() => activate(i)}
67
+ className={`${tabClassName} tab-button ${
68
+ current === i ? "tab-button--active" : ""
69
+ } ${t.disabled ? "tab-button--disabled" : ""}`}
70
+ >
71
+ {t.title}
72
+ </button>
73
+ ))}
74
+ </div>
75
+
76
+ {items.map((t, i) => (
77
+ <div
78
+ key={`panel-${t.key}`}
79
+ role="tabpanel"
80
+ id={`panel-${t.key}`}
81
+ aria-labelledby={`tab-${t.key}`}
82
+ hidden={current !== i}
83
+ className={`${panelClassName} mt-3`}
84
+ >
85
+ {current === i && t.panel}
86
+ </div>
87
+ ))}
88
+ </div>
89
+ );
90
+ };
@@ -0,0 +1,20 @@
1
+ import { TabItem, Tabs } from "./Tabs";
2
+
3
+ export const TabsView = () => {
4
+ const items: TabItem[] = [
5
+ { key: "first", title: "Info", panel: <div>Contenu 1</div> },
6
+ { key: "second", title: "Détails", panel: <div>Contenu 2</div> },
7
+ {
8
+ key: "third",
9
+ title: "Paramètres",
10
+ panel: <div>Contenu 3</div>,
11
+ disabled: false,
12
+ },
13
+ ];
14
+ return (
15
+ <div>
16
+ <h2 className="text-xl mb-4">Exemple de Tabs</h2>
17
+ <Tabs items={items} defaultIndex={0} />
18
+ </div>
19
+ );
20
+ };
@@ -0,0 +1,24 @@
1
+ .tabs-root {
2
+ height: 100%;
3
+ .tabs-header {
4
+ display: flex;
5
+ border-bottom: 1px solid #b2a1a1;
6
+ .tab-button {
7
+ padding: 0.5rem 0.75rem;
8
+ border: 0px;
9
+ outline: none;
10
+ background-color: transparent;
11
+ color: #1e293b; /* slate-800 */
12
+ cursor: pointer;
13
+
14
+ &.tab-button--active {
15
+ border-bottom: 2px solid var(--primary-color); /* slate-800 */
16
+ border-radius: 0px;
17
+ }
18
+ &.tab-button--disabled {
19
+ opacity: 0.5;
20
+ cursor: not-allowed;
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,55 @@
1
+ import { useCallback, useEffect } from "react";
2
+ import ReactDOM from "react-dom";
3
+ import { Icon } from "../icon/Icon";
4
+ import { useToasterStore } from "./store/useToasterStore";
5
+ import "./toaster.css";
6
+
7
+ export const Toaster = () => {
8
+ const {
9
+ title,
10
+ description,
11
+ severity = "info",
12
+ visible,
13
+ children,
14
+ duration,
15
+ hideToast,
16
+ close,
17
+ } = useToasterStore();
18
+ const onClose = useCallback(() => {
19
+ hideToast();
20
+ }, [hideToast]);
21
+ useEffect(() => {
22
+ if (!visible) return;
23
+ const timer = setTimeout(() => {
24
+ hideToast();
25
+ }, duration);
26
+ return () => clearTimeout(timer);
27
+ }, [visible, duration, hideToast]);
28
+
29
+ if (!visible) return null;
30
+
31
+ const toasterRoot = document.getElementById("toaster-root");
32
+ if (!toasterRoot) return null;
33
+
34
+ return ReactDOM.createPortal(
35
+ <div className={`toaster toaster-${severity}`}>
36
+ {close && (
37
+ <div className="toaster--close" onClick={onClose}>
38
+ <Icon classIcon="bi-x" />
39
+ </div>
40
+ )}
41
+ <div>
42
+ {severity === "info" && <Icon classIcon="bi-info-circle" />}
43
+ {severity === "success" && <Icon classIcon="bi-check-circle" />}
44
+ {severity === "error" && <Icon classIcon="bi-x-circle" />}
45
+ {severity === "warning" && <Icon classIcon="bi-exclamation-triangle" />}
46
+ </div>
47
+ <div>
48
+ <div className={`title title-${severity}`}>{title}</div>
49
+ {description && <div className="subtitle">{description}</div>}
50
+ {children}
51
+ </div>
52
+ </div>,
53
+ toasterRoot
54
+ );
55
+ };
@@ -0,0 +1,24 @@
1
+ import { useCallback } from "react";
2
+ import { Button } from "../button/Button";
3
+ import { useToasterStore } from "./store/useToasterStore";
4
+
5
+ export const ToasterView = (): JSX.Element => {
6
+ const { showToast } = useToasterStore();
7
+ const handleClickToast = useCallback(() => {
8
+ showToast("This is a toast message", {
9
+ description: "This is a description for the toast message.",
10
+ severity: "warning",
11
+ duration: 10000,
12
+ children: <div>Additional content can go here.</div>,
13
+ });
14
+ }, [showToast]);
15
+ return (
16
+ <div>
17
+ <Button
18
+ title="Show Toast"
19
+ onClick={handleClickToast}
20
+ className="btn btn-primary"
21
+ />
22
+ </div>
23
+ );
24
+ };
@@ -0,0 +1,41 @@
1
+ import { create } from "zustand";
2
+
3
+ type ToasterState = {
4
+ title: string;
5
+ description?: string;
6
+ severity?: "info" | "success" | "error" | "warning";
7
+ children?: React.ReactNode;
8
+ visible: boolean;
9
+ duration: number;
10
+ close?: boolean;
11
+ showToast: (
12
+ title: string,
13
+ options?: {
14
+ description?: string;
15
+ severity?: "info" | "success" | "error" | "warning";
16
+ duration?: number;
17
+ children?: React.ReactNode;
18
+ }
19
+ ) => void;
20
+ hideToast: () => void;
21
+ };
22
+
23
+ export const useToasterStore = create<ToasterState>((set) => ({
24
+ title: "",
25
+ description: "",
26
+ severity: "info",
27
+ visible: false,
28
+ duration: 2500,
29
+ close: true,
30
+ children: null,
31
+ showToast: (title, options = {}) =>
32
+ set(() => ({
33
+ title,
34
+ description: options.description,
35
+ severity: options.severity || "info",
36
+ duration: options.duration || 2500,
37
+ visible: true,
38
+ children: options.children || null,
39
+ })),
40
+ hideToast: () => set(() => ({ visible: false })),
41
+ }));