@wakastellar/ui 2.0.0 → 2.1.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 (290) hide show
  1. package/dist/cli/commands/add.d.ts +7 -0
  2. package/dist/cli/commands/init.d.ts +6 -0
  3. package/dist/cli/commands/list.d.ts +5 -0
  4. package/dist/cli/commands/search.d.ts +1 -0
  5. package/dist/cli/index.cjs +4844 -0
  6. package/dist/cli/index.d.ts +1 -0
  7. package/dist/cli/utils/config.d.ts +29 -0
  8. package/dist/cli/utils/logger.d.ts +20 -0
  9. package/dist/cli/utils/registry.d.ts +23 -0
  10. package/package.json +14 -3
  11. package/src/blocks/activity-timeline/index.tsx +586 -0
  12. package/src/blocks/calendar-view/index.tsx +756 -0
  13. package/src/blocks/chat/index.tsx +1018 -0
  14. package/src/blocks/chat/widget.tsx +504 -0
  15. package/src/blocks/dashboard/index.tsx +522 -0
  16. package/src/blocks/empty-states/index.tsx +452 -0
  17. package/src/blocks/error-pages/index.tsx +426 -0
  18. package/src/blocks/faq/index.tsx +479 -0
  19. package/src/blocks/file-manager/index.tsx +890 -0
  20. package/src/blocks/footer/index.tsx +133 -0
  21. package/src/blocks/header/index.tsx +357 -0
  22. package/src/blocks/headtab/index.tsx +139 -0
  23. package/src/blocks/i18n-editor/index.tsx +1016 -0
  24. package/src/blocks/index.ts +80 -0
  25. package/src/blocks/kanban-board/index.tsx +779 -0
  26. package/src/blocks/landing/index.tsx +677 -0
  27. package/src/blocks/language-selector/index.tsx +88 -0
  28. package/src/blocks/layout/index.tsx +159 -0
  29. package/src/blocks/login/index.tsx +339 -0
  30. package/src/blocks/login/types.ts +131 -0
  31. package/src/blocks/pricing/index.tsx +564 -0
  32. package/src/blocks/profile/index.tsx +746 -0
  33. package/src/blocks/settings/index.tsx +558 -0
  34. package/src/blocks/sidebar/index.tsx +713 -0
  35. package/src/blocks/theme-creator-block/index.tsx +835 -0
  36. package/src/blocks/user-management/index.tsx +1037 -0
  37. package/src/blocks/wizard/index.tsx +719 -0
  38. package/src/components/DataTable/DataTable.tsx +406 -0
  39. package/src/components/DataTable/DataTableAdvanced.tsx +720 -0
  40. package/src/components/DataTable/DataTableBody.tsx +216 -0
  41. package/src/components/DataTable/DataTableCell.tsx +172 -0
  42. package/src/components/DataTable/DataTableColumnResizer.tsx +62 -0
  43. package/src/components/DataTable/DataTableConflictResolver.tsx +478 -0
  44. package/src/components/DataTable/DataTableContextMenu.tsx +219 -0
  45. package/src/components/DataTable/DataTableEditCell.tsx +279 -0
  46. package/src/components/DataTable/DataTableFilterBuilder.tsx +519 -0
  47. package/src/components/DataTable/DataTableFilters.tsx +535 -0
  48. package/src/components/DataTable/DataTableGrouping.tsx +147 -0
  49. package/src/components/DataTable/DataTableHeader.tsx +172 -0
  50. package/src/components/DataTable/DataTablePagination.tsx +125 -0
  51. package/src/components/DataTable/DataTableSelection.tsx +269 -0
  52. package/src/components/DataTable/DataTableSyncStatus.tsx +281 -0
  53. package/src/components/DataTable/DataTableToolbar.tsx +262 -0
  54. package/src/components/DataTable/README.md +446 -0
  55. package/src/components/DataTable/__tests__/DataTableAdvanced.test.tsx +426 -0
  56. package/src/components/DataTable/__tests__/DataTableEdit.test.tsx +329 -0
  57. package/src/components/DataTable/__tests__/useDataTableAdvanced.test.ts +455 -0
  58. package/src/components/DataTable/examples/EditExample.tsx +166 -0
  59. package/src/components/DataTable/formatters/index.ts +335 -0
  60. package/src/components/DataTable/hooks/__tests__/useDataTableEdit.test.ts +239 -0
  61. package/src/components/DataTable/hooks/useDataTable.ts +145 -0
  62. package/src/components/DataTable/hooks/useDataTableAdvanced.ts +342 -0
  63. package/src/components/DataTable/hooks/useDataTableAdvancedFilters.ts +637 -0
  64. package/src/components/DataTable/hooks/useDataTableColumnTemplates.ts +186 -0
  65. package/src/components/DataTable/hooks/useDataTableEdit.ts +167 -0
  66. package/src/components/DataTable/hooks/useDataTableExport.ts +227 -0
  67. package/src/components/DataTable/hooks/useDataTableImport.ts +216 -0
  68. package/src/components/DataTable/hooks/useDataTableOffline.ts +481 -0
  69. package/src/components/DataTable/hooks/useDataTableTheme.ts +213 -0
  70. package/src/components/DataTable/hooks/useDataTableVirtualization.ts +99 -0
  71. package/src/components/DataTable/hooks/useTableLayout.ts +85 -0
  72. package/src/components/DataTable/index.ts +81 -0
  73. package/src/components/DataTable/services/IndexedDBService.ts +504 -0
  74. package/src/components/DataTable/templates/index.tsx +803 -0
  75. package/src/components/DataTable/types.ts +504 -0
  76. package/src/components/DataTable/utils.ts +164 -0
  77. package/src/components/DataTable/workers/exportWorker.ts +213 -0
  78. package/src/components/accordion/index.tsx +61 -0
  79. package/src/components/alert/index.tsx +61 -0
  80. package/src/components/alert-dialog/index.tsx +146 -0
  81. package/src/components/aspect-ratio/index.tsx +12 -0
  82. package/src/components/avatar/index.tsx +54 -0
  83. package/src/components/badge/Badge.stories.tsx +64 -0
  84. package/src/components/badge/index.tsx +38 -0
  85. package/src/components/button/Button.stories.tsx +173 -0
  86. package/src/components/button/index.tsx +56 -0
  87. package/src/components/calendar/index.tsx +73 -0
  88. package/src/components/card/index.tsx +78 -0
  89. package/src/components/checkbox/index.tsx +34 -0
  90. package/src/components/code/index.tsx +229 -0
  91. package/src/components/collapsible/index.tsx +16 -0
  92. package/src/components/command/index.tsx +162 -0
  93. package/src/components/context-menu/index.tsx +204 -0
  94. package/src/components/dialog/index.tsx +126 -0
  95. package/src/components/dropdown-menu/index.tsx +204 -0
  96. package/src/components/error-boundary/ErrorBoundary.tsx +281 -0
  97. package/src/components/error-boundary/index.ts +7 -0
  98. package/src/components/form/index.tsx +183 -0
  99. package/src/components/hover-card/index.tsx +33 -0
  100. package/src/components/index.ts +368 -0
  101. package/src/components/input/Input.stories.tsx +100 -0
  102. package/src/components/input/index.tsx +27 -0
  103. package/src/components/input-otp/index.tsx +277 -0
  104. package/src/components/label/index.tsx +30 -0
  105. package/src/components/language-selector/index.tsx +341 -0
  106. package/src/components/menubar/index.tsx +240 -0
  107. package/src/components/navigation-menu/index.tsx +134 -0
  108. package/src/components/popover/index.tsx +35 -0
  109. package/src/components/progress/index.tsx +32 -0
  110. package/src/components/radio-group/index.tsx +48 -0
  111. package/src/components/scroll-area/index.tsx +52 -0
  112. package/src/components/select/index.tsx +164 -0
  113. package/src/components/separator/index.tsx +35 -0
  114. package/src/components/sheet/index.tsx +147 -0
  115. package/src/components/skeleton/index.tsx +22 -0
  116. package/src/components/slider/index.tsx +32 -0
  117. package/src/components/switch/index.tsx +33 -0
  118. package/src/components/table/index.tsx +117 -0
  119. package/src/components/tabs/index.tsx +59 -0
  120. package/src/components/textarea/index.tsx +30 -0
  121. package/src/components/theme-selector/index.tsx +327 -0
  122. package/src/components/toast/index.tsx +133 -0
  123. package/src/components/toaster/index.tsx +34 -0
  124. package/src/components/toggle/index.tsx +49 -0
  125. package/src/components/tooltip/index.tsx +34 -0
  126. package/src/components/typography/index.tsx +276 -0
  127. package/src/components/waka-3d-pie-chart/index.tsx +486 -0
  128. package/src/components/waka-achievement-unlock/index.tsx +716 -0
  129. package/src/components/waka-activity-feed/index.tsx +686 -0
  130. package/src/components/waka-address-autocomplete/index.tsx +1202 -0
  131. package/src/components/waka-admincrumb/index.tsx +349 -0
  132. package/src/components/waka-alert-stack/index.tsx +827 -0
  133. package/src/components/waka-allocation-matrix/index.tsx +1278 -0
  134. package/src/components/waka-approval-chain/index.tsx +766 -0
  135. package/src/components/waka-audit-log/index.tsx +1475 -0
  136. package/src/components/waka-autocomplete/index.tsx +358 -0
  137. package/src/components/waka-badge-showcase/index.tsx +704 -0
  138. package/src/components/waka-barcode/index.tsx +260 -0
  139. package/src/components/waka-biometric-prompt/index.tsx +765 -0
  140. package/src/components/waka-bottom-sheet/index.tsx +495 -0
  141. package/src/components/waka-breadcrumb/index.tsx +376 -0
  142. package/src/components/waka-breadcrumb-path/index.tsx +513 -0
  143. package/src/components/waka-budget-burn/index.tsx +1234 -0
  144. package/src/components/waka-capacity-planner/index.tsx +1107 -0
  145. package/src/components/waka-carousel/index.tsx +893 -0
  146. package/src/components/waka-cart-summary/index.tsx +1055 -0
  147. package/src/components/waka-challenge-timer/index.tsx +1044 -0
  148. package/src/components/waka-charts/WakaAreaChart.tsx +251 -0
  149. package/src/components/waka-charts/WakaBarChart.tsx +222 -0
  150. package/src/components/waka-charts/WakaChart.tsx +124 -0
  151. package/src/components/waka-charts/WakaLineChart.tsx +219 -0
  152. package/src/components/waka-charts/WakaMiniChart.tsx +133 -0
  153. package/src/components/waka-charts/WakaPieChart.tsx +214 -0
  154. package/src/components/waka-charts/WakaSparkline.tsx +229 -0
  155. package/src/components/waka-charts/dataTableHelpers.ts +109 -0
  156. package/src/components/waka-charts/hooks/useChartTheme.ts +123 -0
  157. package/src/components/waka-charts/hooks/useRechartsLoader.ts +234 -0
  158. package/src/components/waka-charts/index.ts +90 -0
  159. package/src/components/waka-charts/types.ts +330 -0
  160. package/src/components/waka-chat-bubble/index.tsx +1060 -0
  161. package/src/components/waka-checklist/index.tsx +1067 -0
  162. package/src/components/waka-checkout-stepper/index.tsx +976 -0
  163. package/src/components/waka-cohort-table/index.tsx +1011 -0
  164. package/src/components/waka-color-picker/index.tsx +447 -0
  165. package/src/components/waka-combo-counter/index.tsx +864 -0
  166. package/src/components/waka-combobox/index.tsx +497 -0
  167. package/src/components/waka-command-bar/index.tsx +403 -0
  168. package/src/components/waka-compare-period/index.tsx +1230 -0
  169. package/src/components/waka-connection-matrix/index.tsx +1053 -0
  170. package/src/components/waka-contribution-graph/index.tsx +552 -0
  171. package/src/components/waka-cost-breakdown/index.tsx +1065 -0
  172. package/src/components/waka-coupon-input/index.tsx +592 -0
  173. package/src/components/waka-credit-card-input/index.tsx +982 -0
  174. package/src/components/waka-daily-reward/index.tsx +762 -0
  175. package/src/components/waka-date-range-picker/index.tsx +378 -0
  176. package/src/components/waka-datetime-picker/index.tsx +793 -0
  177. package/src/components/waka-datetime-picker.form-integration/index.tsx +402 -0
  178. package/src/components/waka-deployment-lane/index.tsx +673 -0
  179. package/src/components/waka-device-trust/index.tsx +1259 -0
  180. package/src/components/waka-dock/index.tsx +285 -0
  181. package/src/components/waka-drawer/index.tsx +319 -0
  182. package/src/components/waka-empty-state/index.tsx +545 -0
  183. package/src/components/waka-error-shake/index.tsx +398 -0
  184. package/src/components/waka-feature-announcement/index.tsx +991 -0
  185. package/src/components/waka-file-upload/index.tsx +437 -0
  186. package/src/components/waka-floating-nav/index.tsx +413 -0
  187. package/src/components/waka-flow-diagram/index.tsx +508 -0
  188. package/src/components/waka-funnel-chart/index.tsx +823 -0
  189. package/src/components/waka-glow-card/index.tsx +246 -0
  190. package/src/components/waka-goal-progress/index.tsx +1025 -0
  191. package/src/components/waka-haptic-button/index.tsx +388 -0
  192. package/src/components/waka-health-pulse/index.tsx +451 -0
  193. package/src/components/waka-heatmap/index.tsx +1026 -0
  194. package/src/components/waka-hotspot/index.tsx +682 -0
  195. package/src/components/waka-image/index.tsx +373 -0
  196. package/src/components/waka-incident-timeline/index.tsx +686 -0
  197. package/src/components/waka-invoice-preview/index.tsx +829 -0
  198. package/src/components/waka-kanban/index.tsx +646 -0
  199. package/src/components/waka-kpi-dashboard/index.tsx +755 -0
  200. package/src/components/waka-leaderboard/index.tsx +746 -0
  201. package/src/components/waka-level-progress/index.tsx +665 -0
  202. package/src/components/waka-liquid-button/index.tsx +520 -0
  203. package/src/components/waka-loading-orbit/index.tsx +478 -0
  204. package/src/components/waka-loot-box/index.tsx +1091 -0
  205. package/src/components/waka-magic-link/index.tsx +321 -0
  206. package/src/components/waka-magnetic-button/index.tsx +567 -0
  207. package/src/components/waka-mention-input/index.tsx +953 -0
  208. package/src/components/waka-metric-sparkline/index.tsx +627 -0
  209. package/src/components/waka-milestone-road/index.tsx +1064 -0
  210. package/src/components/waka-modal/index.tsx +374 -0
  211. package/src/components/waka-morph-button/index.tsx +495 -0
  212. package/src/components/waka-network-topology/index.tsx +801 -0
  213. package/src/components/waka-notifications/index.tsx +414 -0
  214. package/src/components/waka-number-input/index.tsx +373 -0
  215. package/src/components/waka-orbital-menu/index.tsx +445 -0
  216. package/src/components/waka-order-tracker/index.tsx +1041 -0
  217. package/src/components/waka-pagination/index.tsx +393 -0
  218. package/src/components/waka-password-strength/index.tsx +824 -0
  219. package/src/components/waka-payment-method-picker/index.tsx +715 -0
  220. package/src/components/waka-permission-matrix/index.tsx +1302 -0
  221. package/src/components/waka-phone-input/index.tsx +801 -0
  222. package/src/components/waka-pipeline-view/index.tsx +604 -0
  223. package/src/components/waka-player-card/index.tsx +691 -0
  224. package/src/components/waka-points-popup/index.tsx +366 -0
  225. package/src/components/waka-power-up/index.tsx +1155 -0
  226. package/src/components/waka-presence-indicator/index.tsx +1181 -0
  227. package/src/components/waka-pricing-table/index.tsx +755 -0
  228. package/src/components/waka-product-card/index.tsx +786 -0
  229. package/src/components/waka-progress-onboarding/index.tsx +878 -0
  230. package/src/components/waka-pull-to-refresh/index.tsx +451 -0
  231. package/src/components/waka-qrcode/index.tsx +232 -0
  232. package/src/components/waka-quest-card/index.tsx +1275 -0
  233. package/src/components/waka-quota-bar/index.tsx +693 -0
  234. package/src/components/waka-radar-score/index.tsx +512 -0
  235. package/src/components/waka-rank-badge/index.tsx +813 -0
  236. package/src/components/waka-rating-input/index.tsx +560 -0
  237. package/src/components/waka-reaction-picker/index.tsx +1062 -0
  238. package/src/components/waka-region-map/index.tsx +730 -0
  239. package/src/components/waka-resource-gauge/index.tsx +654 -0
  240. package/src/components/waka-resource-pool/index.tsx +1035 -0
  241. package/src/components/waka-rich-text-editor/index.tsx +594 -0
  242. package/src/components/waka-rollback-slider/index.tsx +891 -0
  243. package/src/components/waka-sankey-diagram/index.tsx +1032 -0
  244. package/src/components/waka-schedule-picker/index.tsx +1060 -0
  245. package/src/components/waka-scratch-card/index.tsx +914 -0
  246. package/src/components/waka-season-pass/index.tsx +886 -0
  247. package/src/components/waka-security-score/index.tsx +1126 -0
  248. package/src/components/waka-segmented-control/index.tsx +238 -0
  249. package/src/components/waka-server-rack/index.tsx +764 -0
  250. package/src/components/waka-session-manager/index.tsx +815 -0
  251. package/src/components/waka-signature-pad/index.tsx +744 -0
  252. package/src/components/waka-skeleton-wave/index.tsx +454 -0
  253. package/src/components/waka-skill-tree/index.tsx +1031 -0
  254. package/src/components/waka-sla-tracker/index.tsx +798 -0
  255. package/src/components/waka-slider-range/index.tsx +765 -0
  256. package/src/components/waka-spin-wheel/index.tsx +671 -0
  257. package/src/components/waka-spinner/index.tsx +284 -0
  258. package/src/components/waka-spotlight/index.tsx +410 -0
  259. package/src/components/waka-stat/index.tsx +428 -0
  260. package/src/components/waka-stats-hexagon/index.tsx +824 -0
  261. package/src/components/waka-status-matrix/index.tsx +565 -0
  262. package/src/components/waka-stepper/index.tsx +489 -0
  263. package/src/components/waka-streak-counter/index.tsx +334 -0
  264. package/src/components/waka-success-explosion/index.tsx +453 -0
  265. package/src/components/waka-swipe-card/index.tsx +574 -0
  266. package/src/components/waka-tabs-morph/index.tsx +509 -0
  267. package/src/components/waka-tag-input/index.tsx +877 -0
  268. package/src/components/waka-team-banner/index.tsx +1183 -0
  269. package/src/components/waka-terminal-output/index.tsx +836 -0
  270. package/src/components/waka-theme-creator/index.tsx +762 -0
  271. package/src/components/waka-theme-manager/index.tsx +654 -0
  272. package/src/components/waka-thread-view/index.tsx +874 -0
  273. package/src/components/waka-tilt-card/index.tsx +250 -0
  274. package/src/components/waka-time-picker/index.tsx +479 -0
  275. package/src/components/waka-timeline/index.tsx +385 -0
  276. package/src/components/waka-tooltip-tour/index.tsx +855 -0
  277. package/src/components/waka-tour-guide/index.tsx +920 -0
  278. package/src/components/waka-tournament-bracket/index.tsx +1276 -0
  279. package/src/components/waka-tree/index.tsx +557 -0
  280. package/src/components/waka-treemap-chart/index.tsx +1031 -0
  281. package/src/components/waka-two-factor-setup/index.tsx +995 -0
  282. package/src/components/waka-typewriter/index.tsx +566 -0
  283. package/src/components/waka-typing-indicator/index.tsx +649 -0
  284. package/src/components/waka-versus-card/index.tsx +1026 -0
  285. package/src/components/waka-video/index.tsx +557 -0
  286. package/src/components/waka-video-call/index.tsx +1087 -0
  287. package/src/components/waka-virtual-list/index.tsx +327 -0
  288. package/src/components/waka-voice-message/index.tsx +1019 -0
  289. package/src/components/waka-welcome-modal/index.tsx +790 -0
  290. package/src/components/waka-xp-bar/index.tsx +799 -0
@@ -0,0 +1,186 @@
1
+ import { useMemo, useCallback } from "react"
2
+ import { ColumnDef, CellContext } from "@tanstack/react-table"
3
+ import { templatesMap, createActionsTemplate } from "../templates"
4
+ import { formatters } from "../formatters"
5
+ import type {
6
+ ColumnTemplateName,
7
+ ColumnTemplateDefinition,
8
+ ColumnTemplatesConfig,
9
+ ColumnTemplatesResult,
10
+ ColumnTemplateOptions,
11
+ ColumnTemplate,
12
+ } from "../types"
13
+
14
+ /**
15
+ * Hook pour créer des colonnes DataTable à partir de templates prédéfinis
16
+ *
17
+ * @template TData - Type des données de la table
18
+ * @param config - Configuration des templates (locale, currency, etc.)
19
+ * @returns Fonctions pour créer des colonnes et accéder aux formatters
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * const { createColumns, getFormatter } = useDataTableColumnTemplates({
24
+ * locale: 'fr-FR',
25
+ * currency: 'EUR',
26
+ * })
27
+ *
28
+ * const columns = createColumns<User>([
29
+ * { accessorKey: 'email', template: 'email' },
30
+ * { accessorKey: 'salary', template: 'currency' },
31
+ * { accessorKey: 'status', template: 'status', options: {
32
+ * templateOptions: {
33
+ * statusColors: { active: 'bg-green-100 text-green-800' }
34
+ * }
35
+ * }},
36
+ * ])
37
+ * ```
38
+ */
39
+ export function useDataTableColumnTemplates<TData extends Record<string, unknown>>(
40
+ config?: ColumnTemplatesConfig
41
+ ): ColumnTemplatesResult<TData> {
42
+ // Configuration par défaut
43
+ const defaultConfig = useMemo(() => ({
44
+ locale: "fr-FR",
45
+ currency: "EUR",
46
+ dateFormat: "PP",
47
+ datetimeFormat: "PPpp",
48
+ timeFormat: "HH:mm",
49
+ ...config,
50
+ }), [config])
51
+
52
+ /**
53
+ * Récupère un template par son nom avec typage correct pour les actions
54
+ */
55
+ const getTemplateForData = useCallback(<T>(
56
+ template: ColumnTemplateName | ColumnTemplate<T, unknown>
57
+ ): ColumnTemplate<T, unknown> | undefined => {
58
+ // Si c'est déjà un objet template, le retourner directement
59
+ if (typeof template === "object" && template !== null) {
60
+ return template
61
+ }
62
+
63
+ // Sinon, chercher dans les templates prédéfinis
64
+ const templateName = template as ColumnTemplateName
65
+ if (templateName === "actions") {
66
+ return createActionsTemplate<T>() as ColumnTemplate<T, unknown>
67
+ }
68
+ return templatesMap[templateName] as ColumnTemplate<T, unknown> | undefined
69
+ }, [])
70
+
71
+ /**
72
+ * Crée une ColumnDef à partir d'une définition de template
73
+ */
74
+ const getColumnFromTemplate = useCallback(<T extends Record<string, unknown>>(
75
+ definition: ColumnTemplateDefinition<T>
76
+ ): ColumnDef<T, unknown> => {
77
+ const { template, accessorKey, header, options = {} } = definition
78
+ const templateConfig = getTemplateForData<T>(template)
79
+
80
+ if (!templateConfig) {
81
+ console.warn(`Template "${String(template)}" not found, falling back to text template`)
82
+ return {
83
+ accessorKey: accessorKey as string,
84
+ header: header || String(accessorKey),
85
+ cell: ({ getValue }) => String(getValue() ?? ""),
86
+ }
87
+ }
88
+
89
+ // Déterminer le nom du template pour les options par défaut
90
+ const templateName = typeof template === "string" ? template as ColumnTemplateName : undefined
91
+
92
+ // Merge des options globales avec les options locales
93
+ const mergedOptions: Record<string, unknown> = {
94
+ // Options globales
95
+ locale: defaultConfig.locale,
96
+ currency: defaultConfig.currency,
97
+ format: templateName === "datetime"
98
+ ? defaultConfig.datetimeFormat
99
+ : templateName === "time"
100
+ ? defaultConfig.timeFormat
101
+ : defaultConfig.dateFormat,
102
+ // Options du template par défaut
103
+ ...(templateName && config?.defaultTemplateOptions?.[templateName] ? config.defaultTemplateOptions[templateName] : {}),
104
+ // Options spécifiques à la colonne
105
+ ...(options.templateOptions || {}),
106
+ }
107
+
108
+ // Créer la cellule avec le renderer du template
109
+ const cell = (info: CellContext<T, unknown>) => {
110
+ const value = info.getValue()
111
+ return templateConfig.cell(value, info.row.original, mergedOptions as ColumnTemplateOptions["templateOptions"])
112
+ }
113
+
114
+ // Construire la ColumnDef
115
+ const columnDef: ColumnDef<T, unknown> = {
116
+ accessorKey: accessorKey as string,
117
+ header: header || String(accessorKey),
118
+ cell,
119
+ size: options.size || templateConfig.size,
120
+ minSize: options.minSize || templateConfig.minSize,
121
+ maxSize: options.maxSize || templateConfig.maxSize,
122
+ enableSorting: options.enableSorting ?? templateConfig.enableSorting,
123
+ enableColumnFilter: options.enableFiltering ?? templateConfig.enableFiltering,
124
+ enableHiding: options.enableHiding ?? templateConfig.enableHiding,
125
+ meta: {
126
+ ...templateConfig.meta,
127
+ templateName: templateName,
128
+ },
129
+ }
130
+
131
+ // Ajouter la fonction de tri si définie (en tant que string pour les built-in fns)
132
+ if (templateConfig.sortingFn) {
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ columnDef.sortingFn = templateConfig.sortingFn as any
135
+ }
136
+
137
+ // Ajouter la fonction de filtre si définie
138
+ if (templateConfig.filterFn) {
139
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
+ columnDef.filterFn = templateConfig.filterFn as any
141
+ }
142
+
143
+ return columnDef
144
+ }, [defaultConfig, config?.defaultTemplateOptions, getTemplateForData])
145
+
146
+ /**
147
+ * Crée plusieurs colonnes à partir de définitions de templates
148
+ */
149
+ const createColumns = useCallback(<T extends Record<string, unknown>>(
150
+ definitions: ColumnTemplateDefinition<T>[]
151
+ ): ColumnDef<T, unknown>[] => {
152
+ return definitions.map((def) => getColumnFromTemplate<T>(def))
153
+ }, [getColumnFromTemplate])
154
+
155
+ /**
156
+ * Récupère un formatter par son nom
157
+ */
158
+ const getFormatter = useCallback((name: keyof typeof formatters): ((...args: unknown[]) => string) => {
159
+ return formatters[name] as (...args: unknown[]) => string
160
+ }, [])
161
+
162
+ /**
163
+ * Vérifie si un template existe
164
+ */
165
+ const hasTemplate = useCallback((name: string): name is ColumnTemplateName => {
166
+ return name in templatesMap
167
+ }, [])
168
+
169
+ /**
170
+ * Liste des templates disponibles
171
+ */
172
+ const availableTemplates = useMemo(() => {
173
+ return Object.keys(templatesMap) as ColumnTemplateName[]
174
+ }, [])
175
+
176
+ return {
177
+ getColumnFromTemplate,
178
+ createColumns,
179
+ getFormatter,
180
+ hasTemplate,
181
+ availableTemplates,
182
+ formatters: formatters as unknown as Record<string, (...args: unknown[]) => string>,
183
+ }
184
+ }
185
+
186
+ export default useDataTableColumnTemplates
@@ -0,0 +1,167 @@
1
+ "use client"
2
+
3
+ import React, { useState, useCallback, useMemo } from "react"
4
+ import { Table } from "@tanstack/react-table"
5
+
6
+ export interface EditState<TData> {
7
+ editingRowId: string | null
8
+ editedData: Partial<TData>
9
+ hasChanges: boolean
10
+ isDirty: boolean
11
+ }
12
+
13
+ export interface EditActions<TData> {
14
+ startEdit: (rowId: string, initialData: TData) => void
15
+ cancelEdit: () => void
16
+ saveEdit: () => Promise<void>
17
+ updateField: (field: keyof TData, value: any) => void
18
+ resetEdit: () => void
19
+ }
20
+
21
+ export interface UseDataTableEditOptions<TData> {
22
+ onSave?: (rowId: string, data: Partial<TData>) => Promise<void>
23
+ onCancel?: (rowId: string, originalData: TData) => void
24
+ validateField?: (field: keyof TData, value: any) => string | null
25
+ autoSave?: boolean
26
+ autoSaveDelay?: number
27
+ }
28
+
29
+ export function useDataTableEdit<TData>(
30
+ table: Table<TData>,
31
+ options: UseDataTableEditOptions<TData> = {}
32
+ ): [EditState<TData>, EditActions<TData>] {
33
+ const {
34
+ onSave,
35
+ onCancel,
36
+ validateField,
37
+ autoSave = false,
38
+ autoSaveDelay = 2000,
39
+ } = options
40
+
41
+ const [editingRowId, setEditingRowId] = useState<string | null>(null)
42
+ const [editedData, setEditedData] = useState<Partial<TData>>({})
43
+ const [originalData, setOriginalData] = useState<TData | null>(null)
44
+ const [validationErrors, setValidationErrors] = useState<Record<string, string>>({})
45
+ const [isSaving, setIsSaving] = useState(false)
46
+
47
+ // Calculer si il y a des changements
48
+ const hasChanges = useMemo(() => {
49
+ if (!originalData || !editingRowId) return false
50
+ return Object.keys(editedData).some(key => {
51
+ const field = key as keyof TData
52
+ return editedData[field] !== originalData[field]
53
+ })
54
+ }, [editedData, originalData, editingRowId])
55
+
56
+ // Calculer si les données sont sales (modifiées mais pas sauvegardées)
57
+ const isDirty = hasChanges && editingRowId !== null
58
+
59
+ const startEdit = useCallback((rowId: string, initialData: TData) => {
60
+ setEditingRowId(rowId)
61
+ setEditedData({ ...initialData })
62
+ setOriginalData(initialData)
63
+ setValidationErrors({})
64
+ }, [])
65
+
66
+ const cancelEdit = useCallback(() => {
67
+ if (!editingRowId || !originalData) return
68
+
69
+ // Appeler le callback d'annulation si fourni
70
+ onCancel?.(editingRowId, originalData)
71
+
72
+ setEditingRowId(null)
73
+ setEditedData({})
74
+ setOriginalData(null)
75
+ setValidationErrors({})
76
+ }, [editingRowId, originalData, onCancel])
77
+
78
+ const updateField = useCallback((field: keyof TData, value: any) => {
79
+ if (!editingRowId) return
80
+
81
+ // Validation du champ si une fonction de validation est fournie
82
+ if (validateField) {
83
+ const error = validateField(field, value)
84
+ if (error) {
85
+ setValidationErrors(prev => ({ ...prev, [field as string]: error }))
86
+ return
87
+ } else {
88
+ setValidationErrors(prev => {
89
+ const newErrors = { ...prev }
90
+ delete newErrors[field as string]
91
+ return newErrors
92
+ })
93
+ }
94
+ }
95
+
96
+ setEditedData(prev => ({ ...prev, [field]: value }))
97
+ }, [editingRowId, validateField])
98
+
99
+ const saveEdit = useCallback(async () => {
100
+ if (!editingRowId || !onSave || isSaving) return
101
+
102
+ // Vérifier qu'il n'y a pas d'erreurs de validation
103
+ if (Object.keys(validationErrors).length > 0) {
104
+ throw new Error("Validation errors present")
105
+ }
106
+
107
+ setIsSaving(true)
108
+ try {
109
+ await onSave(editingRowId, editedData)
110
+
111
+ // Réinitialiser l'état après sauvegarde réussie
112
+ setEditingRowId(null)
113
+ setEditedData({})
114
+ setOriginalData(null)
115
+ setValidationErrors({})
116
+ } catch (error) {
117
+ console.error("Error saving edit:", error)
118
+ throw error
119
+ } finally {
120
+ setIsSaving(false)
121
+ }
122
+ }, [editingRowId, onSave, editedData, validationErrors, isSaving])
123
+
124
+ const resetEdit = useCallback(() => {
125
+ if (originalData) {
126
+ setEditedData({ ...originalData })
127
+ setValidationErrors({})
128
+ }
129
+ }, [originalData])
130
+
131
+ // Auto-save si activé
132
+ const autoSaveTimeoutRef = useMemo(() => {
133
+ if (!autoSave || !isDirty) return null
134
+
135
+ return setTimeout(() => {
136
+ if (isDirty && !isSaving) {
137
+ saveEdit().catch(console.error)
138
+ }
139
+ }, autoSaveDelay)
140
+ }, [autoSave, isDirty, isSaving, saveEdit, autoSaveDelay])
141
+
142
+ // Nettoyer le timeout à la destruction
143
+ React.useEffect(() => {
144
+ return () => {
145
+ if (autoSaveTimeoutRef) {
146
+ clearTimeout(autoSaveTimeoutRef)
147
+ }
148
+ }
149
+ }, [autoSaveTimeoutRef])
150
+
151
+ const editState: EditState<TData> = {
152
+ editingRowId,
153
+ editedData,
154
+ hasChanges,
155
+ isDirty,
156
+ }
157
+
158
+ const editActions: EditActions<TData> = {
159
+ startEdit,
160
+ cancelEdit,
161
+ saveEdit,
162
+ updateField,
163
+ resetEdit,
164
+ }
165
+
166
+ return [editState, editActions]
167
+ }
@@ -0,0 +1,227 @@
1
+ import { useCallback, useMemo } from "react"
2
+ import { ExportConfig } from "../types"
3
+
4
+ /**
5
+ * Hook pour gérer l'export des données du DataTable
6
+ */
7
+ export function useDataTableExport<TData>({
8
+ data,
9
+ columns,
10
+ exportConfig,
11
+ }: {
12
+ data: TData[]
13
+ columns: any[]
14
+ exportConfig?: ExportConfig
15
+ }) {
16
+ // Configuration par défaut
17
+ const config = useMemo(() => ({
18
+ formats: ["csv", "xlsx", "json"] as const,
19
+ filename: "export",
20
+ includeHeaders: true,
21
+ ...exportConfig,
22
+ }), [exportConfig])
23
+
24
+ // Fonction d'export CSV
25
+ const exportToCSV = useCallback((data: TData[], filename?: string) => {
26
+ if (!data.length) return
27
+
28
+ const headers = columns
29
+ .filter(col => col.accessorKey || col.id)
30
+ .map(col => col.header || col.accessorKey || col.id)
31
+ .join(",")
32
+
33
+ const rows = data.map(row => {
34
+ return columns
35
+ .filter(col => col.accessorKey || col.id)
36
+ .map(col => {
37
+ const value = col.accessorKey ? (row as any)[col.accessorKey] : (row as any)[col.id]
38
+ const formattedValue = col.formatter ? col.formatter(value) : value
39
+ // Échapper les virgules et guillemets
40
+ const stringValue = String(formattedValue || "")
41
+ if (stringValue.includes(",") || stringValue.includes('"') || stringValue.includes("\n")) {
42
+ return `"${stringValue.replace(/"/g, '""')}"`
43
+ }
44
+ return stringValue
45
+ })
46
+ .join(",")
47
+ })
48
+
49
+ const csvContent = [headers, ...rows].join("\n")
50
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" })
51
+ const link = document.createElement("a")
52
+ link.href = URL.createObjectURL(blob)
53
+ link.download = `${filename || config.filename}.csv`
54
+ link.click()
55
+ URL.revokeObjectURL(link.href)
56
+ }, [columns, config.filename])
57
+
58
+ // Fonction d'export JSON
59
+ const exportToJSON = useCallback((data: TData[], filename?: string) => {
60
+ const jsonContent = JSON.stringify(data, null, 2)
61
+ const blob = new Blob([jsonContent], { type: "application/json" })
62
+ const link = document.createElement("a")
63
+ link.href = URL.createObjectURL(blob)
64
+ link.download = `${filename || config.filename}.json`
65
+ link.click()
66
+ URL.revokeObjectURL(link.href)
67
+ }, [config.filename])
68
+
69
+ // Fonction d'export XLSX (nécessite une librairie externe)
70
+ const exportToXLSX = useCallback(async (data: TData[], filename?: string) => {
71
+ try {
72
+ // Vérifier si la librairie xlsx est disponible
73
+ if (typeof window === 'undefined') {
74
+ throw new Error("XLSX export not available in server environment")
75
+ }
76
+
77
+ // Import dynamique pour éviter d'augmenter la taille du bundle
78
+ // @ts-ignore - Module importé dynamiquement
79
+ const XLSX = await import("xlsx")
80
+
81
+ const headers = columns
82
+ .filter(col => col.accessorKey || col.id)
83
+ .map(col => col.header || col.accessorKey || col.id)
84
+
85
+ const worksheetData = [
86
+ headers,
87
+ ...data.map(row =>
88
+ columns
89
+ .filter(col => col.accessorKey || col.id)
90
+ .map(col => {
91
+ const value = col.accessorKey ? (row as any)[col.accessorKey] : (row as any)[col.id]
92
+ return col.formatter ? col.formatter(value) : value
93
+ })
94
+ )
95
+ ]
96
+
97
+ const worksheet = XLSX.utils.aoa_to_sheet(worksheetData)
98
+ const workbook = XLSX.utils.book_new()
99
+ XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1")
100
+
101
+ XLSX.writeFile(workbook, `${filename || config.filename}.xlsx`)
102
+ } catch (error) {
103
+ console.error("Erreur lors de l'export XLSX:", error)
104
+ throw new Error("La librairie xlsx n'est pas disponible")
105
+ }
106
+ }, [columns, config.filename])
107
+
108
+ // Fonction d'export PDF (nécessite une librairie externe)
109
+ const exportToPDF = useCallback(async (data: TData[], filename?: string) => {
110
+ try {
111
+ // Vérifier si on est côté client
112
+ if (typeof window === 'undefined') {
113
+ throw new Error("PDF export not available in server environment")
114
+ }
115
+
116
+ // @ts-ignore - Module importé dynamiquement
117
+ const jsPDF = await import("jspdf")
118
+ // @ts-ignore - Module importé dynamiquement
119
+ const autoTable = await import("jspdf-autotable")
120
+
121
+ const doc = new jsPDF.default()
122
+
123
+ const headers = columns
124
+ .filter(col => col.accessorKey || col.id)
125
+ .map(col => col.header || col.accessorKey || col.id)
126
+
127
+ const rows = data.map(row =>
128
+ columns
129
+ .filter(col => col.accessorKey || col.id)
130
+ .map(col => {
131
+ const value = col.accessorKey ? (row as any)[col.accessorKey] : (row as any)[col.id]
132
+ return col.formatter ? col.formatter(value) : value
133
+ })
134
+ )
135
+
136
+ doc.autoTable({
137
+ head: [headers],
138
+ body: rows,
139
+ startY: 20,
140
+ styles: { fontSize: 8 },
141
+ headStyles: { fillColor: [66, 139, 202] },
142
+ })
143
+
144
+ doc.save(`${filename || config.filename}.pdf`)
145
+ } catch (error) {
146
+ console.error("Erreur lors de l'export PDF:", error)
147
+ throw new Error("Les librairies jsPDF et jspdf-autotable ne sont pas disponibles")
148
+ }
149
+ }, [columns, config.filename])
150
+
151
+ // Fonction d'export XML
152
+ const exportToXML = useCallback((data: TData[], filename?: string) => {
153
+ const headers = columns
154
+ .filter(col => col.accessorKey || col.id)
155
+ .map(col => col.accessorKey || col.id)
156
+
157
+ let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n'
158
+
159
+ data.forEach((row, index) => {
160
+ xmlContent += ` <row id="${index}">\n`
161
+ headers.forEach(header => {
162
+ const value = (row as any)[header]
163
+ const stringValue = String(value || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
164
+ xmlContent += ` <${header}>${stringValue}</${header}>\n`
165
+ })
166
+ xmlContent += " </row>\n"
167
+ })
168
+
169
+ xmlContent += "</data>"
170
+
171
+ const blob = new Blob([xmlContent], { type: "application/xml" })
172
+ const link = document.createElement("a")
173
+ link.href = URL.createObjectURL(blob)
174
+ link.download = `${filename || config.filename}.xml`
175
+ link.click()
176
+ URL.revokeObjectURL(link.href)
177
+ }, [columns, config.filename])
178
+
179
+ // Fonction d'export principal
180
+ const exportData = useCallback(async (format: string, customData?: TData[], customFilename?: string) => {
181
+ const dataToExport = customData || data
182
+ const filename = customFilename || config.filename
183
+
184
+ if (config.onExport) {
185
+ config.onExport(dataToExport, format)
186
+ return
187
+ }
188
+
189
+ switch (format.toLowerCase()) {
190
+ case "csv":
191
+ exportToCSV(dataToExport, filename)
192
+ break
193
+ case "json":
194
+ exportToJSON(dataToExport, filename)
195
+ break
196
+ case "xlsx":
197
+ await exportToXLSX(dataToExport, filename)
198
+ break
199
+ case "pdf":
200
+ await exportToPDF(dataToExport, filename)
201
+ break
202
+ case "xml":
203
+ exportToXML(dataToExport, filename)
204
+ break
205
+ default:
206
+ throw new Error(`Format d'export non supporté: ${format}`)
207
+ }
208
+ }, [data, config, exportToCSV, exportToJSON, exportToXLSX, exportToPDF, exportToXML])
209
+
210
+ // Fonction d'export de toutes les données
211
+ const exportAll = useCallback(async (format: string) => {
212
+ return exportData(format)
213
+ }, [exportData])
214
+
215
+ // Fonction d'export des données sélectionnées
216
+ const exportSelected = useCallback(async (format: string, selectedData: TData[]) => {
217
+ return exportData(format, selectedData)
218
+ }, [exportData])
219
+
220
+ return {
221
+ exportData,
222
+ exportAll,
223
+ exportSelected,
224
+ supportedFormats: config.formats,
225
+ isExportSupported: (format: string) => config.formats.includes(format as any),
226
+ }
227
+ }