@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.
- package/dist/cli/commands/add.d.ts +7 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/list.d.ts +5 -0
- package/dist/cli/commands/search.d.ts +1 -0
- package/dist/cli/index.cjs +4844 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/utils/config.d.ts +29 -0
- package/dist/cli/utils/logger.d.ts +20 -0
- package/dist/cli/utils/registry.d.ts +23 -0
- package/package.json +14 -3
- package/src/blocks/activity-timeline/index.tsx +586 -0
- package/src/blocks/calendar-view/index.tsx +756 -0
- package/src/blocks/chat/index.tsx +1018 -0
- package/src/blocks/chat/widget.tsx +504 -0
- package/src/blocks/dashboard/index.tsx +522 -0
- package/src/blocks/empty-states/index.tsx +452 -0
- package/src/blocks/error-pages/index.tsx +426 -0
- package/src/blocks/faq/index.tsx +479 -0
- package/src/blocks/file-manager/index.tsx +890 -0
- package/src/blocks/footer/index.tsx +133 -0
- package/src/blocks/header/index.tsx +357 -0
- package/src/blocks/headtab/index.tsx +139 -0
- package/src/blocks/i18n-editor/index.tsx +1016 -0
- package/src/blocks/index.ts +80 -0
- package/src/blocks/kanban-board/index.tsx +779 -0
- package/src/blocks/landing/index.tsx +677 -0
- package/src/blocks/language-selector/index.tsx +88 -0
- package/src/blocks/layout/index.tsx +159 -0
- package/src/blocks/login/index.tsx +339 -0
- package/src/blocks/login/types.ts +131 -0
- package/src/blocks/pricing/index.tsx +564 -0
- package/src/blocks/profile/index.tsx +746 -0
- package/src/blocks/settings/index.tsx +558 -0
- package/src/blocks/sidebar/index.tsx +713 -0
- package/src/blocks/theme-creator-block/index.tsx +835 -0
- package/src/blocks/user-management/index.tsx +1037 -0
- package/src/blocks/wizard/index.tsx +719 -0
- package/src/components/DataTable/DataTable.tsx +406 -0
- package/src/components/DataTable/DataTableAdvanced.tsx +720 -0
- package/src/components/DataTable/DataTableBody.tsx +216 -0
- package/src/components/DataTable/DataTableCell.tsx +172 -0
- package/src/components/DataTable/DataTableColumnResizer.tsx +62 -0
- package/src/components/DataTable/DataTableConflictResolver.tsx +478 -0
- package/src/components/DataTable/DataTableContextMenu.tsx +219 -0
- package/src/components/DataTable/DataTableEditCell.tsx +279 -0
- package/src/components/DataTable/DataTableFilterBuilder.tsx +519 -0
- package/src/components/DataTable/DataTableFilters.tsx +535 -0
- package/src/components/DataTable/DataTableGrouping.tsx +147 -0
- package/src/components/DataTable/DataTableHeader.tsx +172 -0
- package/src/components/DataTable/DataTablePagination.tsx +125 -0
- package/src/components/DataTable/DataTableSelection.tsx +269 -0
- package/src/components/DataTable/DataTableSyncStatus.tsx +281 -0
- package/src/components/DataTable/DataTableToolbar.tsx +262 -0
- package/src/components/DataTable/README.md +446 -0
- package/src/components/DataTable/__tests__/DataTableAdvanced.test.tsx +426 -0
- package/src/components/DataTable/__tests__/DataTableEdit.test.tsx +329 -0
- package/src/components/DataTable/__tests__/useDataTableAdvanced.test.ts +455 -0
- package/src/components/DataTable/examples/EditExample.tsx +166 -0
- package/src/components/DataTable/formatters/index.ts +335 -0
- package/src/components/DataTable/hooks/__tests__/useDataTableEdit.test.ts +239 -0
- package/src/components/DataTable/hooks/useDataTable.ts +145 -0
- package/src/components/DataTable/hooks/useDataTableAdvanced.ts +342 -0
- package/src/components/DataTable/hooks/useDataTableAdvancedFilters.ts +637 -0
- package/src/components/DataTable/hooks/useDataTableColumnTemplates.ts +186 -0
- package/src/components/DataTable/hooks/useDataTableEdit.ts +167 -0
- package/src/components/DataTable/hooks/useDataTableExport.ts +227 -0
- package/src/components/DataTable/hooks/useDataTableImport.ts +216 -0
- package/src/components/DataTable/hooks/useDataTableOffline.ts +481 -0
- package/src/components/DataTable/hooks/useDataTableTheme.ts +213 -0
- package/src/components/DataTable/hooks/useDataTableVirtualization.ts +99 -0
- package/src/components/DataTable/hooks/useTableLayout.ts +85 -0
- package/src/components/DataTable/index.ts +81 -0
- package/src/components/DataTable/services/IndexedDBService.ts +504 -0
- package/src/components/DataTable/templates/index.tsx +803 -0
- package/src/components/DataTable/types.ts +504 -0
- package/src/components/DataTable/utils.ts +164 -0
- package/src/components/DataTable/workers/exportWorker.ts +213 -0
- package/src/components/accordion/index.tsx +61 -0
- package/src/components/alert/index.tsx +61 -0
- package/src/components/alert-dialog/index.tsx +146 -0
- package/src/components/aspect-ratio/index.tsx +12 -0
- package/src/components/avatar/index.tsx +54 -0
- package/src/components/badge/Badge.stories.tsx +64 -0
- package/src/components/badge/index.tsx +38 -0
- package/src/components/button/Button.stories.tsx +173 -0
- package/src/components/button/index.tsx +56 -0
- package/src/components/calendar/index.tsx +73 -0
- package/src/components/card/index.tsx +78 -0
- package/src/components/checkbox/index.tsx +34 -0
- package/src/components/code/index.tsx +229 -0
- package/src/components/collapsible/index.tsx +16 -0
- package/src/components/command/index.tsx +162 -0
- package/src/components/context-menu/index.tsx +204 -0
- package/src/components/dialog/index.tsx +126 -0
- package/src/components/dropdown-menu/index.tsx +204 -0
- package/src/components/error-boundary/ErrorBoundary.tsx +281 -0
- package/src/components/error-boundary/index.ts +7 -0
- package/src/components/form/index.tsx +183 -0
- package/src/components/hover-card/index.tsx +33 -0
- package/src/components/index.ts +368 -0
- package/src/components/input/Input.stories.tsx +100 -0
- package/src/components/input/index.tsx +27 -0
- package/src/components/input-otp/index.tsx +277 -0
- package/src/components/label/index.tsx +30 -0
- package/src/components/language-selector/index.tsx +341 -0
- package/src/components/menubar/index.tsx +240 -0
- package/src/components/navigation-menu/index.tsx +134 -0
- package/src/components/popover/index.tsx +35 -0
- package/src/components/progress/index.tsx +32 -0
- package/src/components/radio-group/index.tsx +48 -0
- package/src/components/scroll-area/index.tsx +52 -0
- package/src/components/select/index.tsx +164 -0
- package/src/components/separator/index.tsx +35 -0
- package/src/components/sheet/index.tsx +147 -0
- package/src/components/skeleton/index.tsx +22 -0
- package/src/components/slider/index.tsx +32 -0
- package/src/components/switch/index.tsx +33 -0
- package/src/components/table/index.tsx +117 -0
- package/src/components/tabs/index.tsx +59 -0
- package/src/components/textarea/index.tsx +30 -0
- package/src/components/theme-selector/index.tsx +327 -0
- package/src/components/toast/index.tsx +133 -0
- package/src/components/toaster/index.tsx +34 -0
- package/src/components/toggle/index.tsx +49 -0
- package/src/components/tooltip/index.tsx +34 -0
- package/src/components/typography/index.tsx +276 -0
- package/src/components/waka-3d-pie-chart/index.tsx +486 -0
- package/src/components/waka-achievement-unlock/index.tsx +716 -0
- package/src/components/waka-activity-feed/index.tsx +686 -0
- package/src/components/waka-address-autocomplete/index.tsx +1202 -0
- package/src/components/waka-admincrumb/index.tsx +349 -0
- package/src/components/waka-alert-stack/index.tsx +827 -0
- package/src/components/waka-allocation-matrix/index.tsx +1278 -0
- package/src/components/waka-approval-chain/index.tsx +766 -0
- package/src/components/waka-audit-log/index.tsx +1475 -0
- package/src/components/waka-autocomplete/index.tsx +358 -0
- package/src/components/waka-badge-showcase/index.tsx +704 -0
- package/src/components/waka-barcode/index.tsx +260 -0
- package/src/components/waka-biometric-prompt/index.tsx +765 -0
- package/src/components/waka-bottom-sheet/index.tsx +495 -0
- package/src/components/waka-breadcrumb/index.tsx +376 -0
- package/src/components/waka-breadcrumb-path/index.tsx +513 -0
- package/src/components/waka-budget-burn/index.tsx +1234 -0
- package/src/components/waka-capacity-planner/index.tsx +1107 -0
- package/src/components/waka-carousel/index.tsx +893 -0
- package/src/components/waka-cart-summary/index.tsx +1055 -0
- package/src/components/waka-challenge-timer/index.tsx +1044 -0
- package/src/components/waka-charts/WakaAreaChart.tsx +251 -0
- package/src/components/waka-charts/WakaBarChart.tsx +222 -0
- package/src/components/waka-charts/WakaChart.tsx +124 -0
- package/src/components/waka-charts/WakaLineChart.tsx +219 -0
- package/src/components/waka-charts/WakaMiniChart.tsx +133 -0
- package/src/components/waka-charts/WakaPieChart.tsx +214 -0
- package/src/components/waka-charts/WakaSparkline.tsx +229 -0
- package/src/components/waka-charts/dataTableHelpers.ts +109 -0
- package/src/components/waka-charts/hooks/useChartTheme.ts +123 -0
- package/src/components/waka-charts/hooks/useRechartsLoader.ts +234 -0
- package/src/components/waka-charts/index.ts +90 -0
- package/src/components/waka-charts/types.ts +330 -0
- package/src/components/waka-chat-bubble/index.tsx +1060 -0
- package/src/components/waka-checklist/index.tsx +1067 -0
- package/src/components/waka-checkout-stepper/index.tsx +976 -0
- package/src/components/waka-cohort-table/index.tsx +1011 -0
- package/src/components/waka-color-picker/index.tsx +447 -0
- package/src/components/waka-combo-counter/index.tsx +864 -0
- package/src/components/waka-combobox/index.tsx +497 -0
- package/src/components/waka-command-bar/index.tsx +403 -0
- package/src/components/waka-compare-period/index.tsx +1230 -0
- package/src/components/waka-connection-matrix/index.tsx +1053 -0
- package/src/components/waka-contribution-graph/index.tsx +552 -0
- package/src/components/waka-cost-breakdown/index.tsx +1065 -0
- package/src/components/waka-coupon-input/index.tsx +592 -0
- package/src/components/waka-credit-card-input/index.tsx +982 -0
- package/src/components/waka-daily-reward/index.tsx +762 -0
- package/src/components/waka-date-range-picker/index.tsx +378 -0
- package/src/components/waka-datetime-picker/index.tsx +793 -0
- package/src/components/waka-datetime-picker.form-integration/index.tsx +402 -0
- package/src/components/waka-deployment-lane/index.tsx +673 -0
- package/src/components/waka-device-trust/index.tsx +1259 -0
- package/src/components/waka-dock/index.tsx +285 -0
- package/src/components/waka-drawer/index.tsx +319 -0
- package/src/components/waka-empty-state/index.tsx +545 -0
- package/src/components/waka-error-shake/index.tsx +398 -0
- package/src/components/waka-feature-announcement/index.tsx +991 -0
- package/src/components/waka-file-upload/index.tsx +437 -0
- package/src/components/waka-floating-nav/index.tsx +413 -0
- package/src/components/waka-flow-diagram/index.tsx +508 -0
- package/src/components/waka-funnel-chart/index.tsx +823 -0
- package/src/components/waka-glow-card/index.tsx +246 -0
- package/src/components/waka-goal-progress/index.tsx +1025 -0
- package/src/components/waka-haptic-button/index.tsx +388 -0
- package/src/components/waka-health-pulse/index.tsx +451 -0
- package/src/components/waka-heatmap/index.tsx +1026 -0
- package/src/components/waka-hotspot/index.tsx +682 -0
- package/src/components/waka-image/index.tsx +373 -0
- package/src/components/waka-incident-timeline/index.tsx +686 -0
- package/src/components/waka-invoice-preview/index.tsx +829 -0
- package/src/components/waka-kanban/index.tsx +646 -0
- package/src/components/waka-kpi-dashboard/index.tsx +755 -0
- package/src/components/waka-leaderboard/index.tsx +746 -0
- package/src/components/waka-level-progress/index.tsx +665 -0
- package/src/components/waka-liquid-button/index.tsx +520 -0
- package/src/components/waka-loading-orbit/index.tsx +478 -0
- package/src/components/waka-loot-box/index.tsx +1091 -0
- package/src/components/waka-magic-link/index.tsx +321 -0
- package/src/components/waka-magnetic-button/index.tsx +567 -0
- package/src/components/waka-mention-input/index.tsx +953 -0
- package/src/components/waka-metric-sparkline/index.tsx +627 -0
- package/src/components/waka-milestone-road/index.tsx +1064 -0
- package/src/components/waka-modal/index.tsx +374 -0
- package/src/components/waka-morph-button/index.tsx +495 -0
- package/src/components/waka-network-topology/index.tsx +801 -0
- package/src/components/waka-notifications/index.tsx +414 -0
- package/src/components/waka-number-input/index.tsx +373 -0
- package/src/components/waka-orbital-menu/index.tsx +445 -0
- package/src/components/waka-order-tracker/index.tsx +1041 -0
- package/src/components/waka-pagination/index.tsx +393 -0
- package/src/components/waka-password-strength/index.tsx +824 -0
- package/src/components/waka-payment-method-picker/index.tsx +715 -0
- package/src/components/waka-permission-matrix/index.tsx +1302 -0
- package/src/components/waka-phone-input/index.tsx +801 -0
- package/src/components/waka-pipeline-view/index.tsx +604 -0
- package/src/components/waka-player-card/index.tsx +691 -0
- package/src/components/waka-points-popup/index.tsx +366 -0
- package/src/components/waka-power-up/index.tsx +1155 -0
- package/src/components/waka-presence-indicator/index.tsx +1181 -0
- package/src/components/waka-pricing-table/index.tsx +755 -0
- package/src/components/waka-product-card/index.tsx +786 -0
- package/src/components/waka-progress-onboarding/index.tsx +878 -0
- package/src/components/waka-pull-to-refresh/index.tsx +451 -0
- package/src/components/waka-qrcode/index.tsx +232 -0
- package/src/components/waka-quest-card/index.tsx +1275 -0
- package/src/components/waka-quota-bar/index.tsx +693 -0
- package/src/components/waka-radar-score/index.tsx +512 -0
- package/src/components/waka-rank-badge/index.tsx +813 -0
- package/src/components/waka-rating-input/index.tsx +560 -0
- package/src/components/waka-reaction-picker/index.tsx +1062 -0
- package/src/components/waka-region-map/index.tsx +730 -0
- package/src/components/waka-resource-gauge/index.tsx +654 -0
- package/src/components/waka-resource-pool/index.tsx +1035 -0
- package/src/components/waka-rich-text-editor/index.tsx +594 -0
- package/src/components/waka-rollback-slider/index.tsx +891 -0
- package/src/components/waka-sankey-diagram/index.tsx +1032 -0
- package/src/components/waka-schedule-picker/index.tsx +1060 -0
- package/src/components/waka-scratch-card/index.tsx +914 -0
- package/src/components/waka-season-pass/index.tsx +886 -0
- package/src/components/waka-security-score/index.tsx +1126 -0
- package/src/components/waka-segmented-control/index.tsx +238 -0
- package/src/components/waka-server-rack/index.tsx +764 -0
- package/src/components/waka-session-manager/index.tsx +815 -0
- package/src/components/waka-signature-pad/index.tsx +744 -0
- package/src/components/waka-skeleton-wave/index.tsx +454 -0
- package/src/components/waka-skill-tree/index.tsx +1031 -0
- package/src/components/waka-sla-tracker/index.tsx +798 -0
- package/src/components/waka-slider-range/index.tsx +765 -0
- package/src/components/waka-spin-wheel/index.tsx +671 -0
- package/src/components/waka-spinner/index.tsx +284 -0
- package/src/components/waka-spotlight/index.tsx +410 -0
- package/src/components/waka-stat/index.tsx +428 -0
- package/src/components/waka-stats-hexagon/index.tsx +824 -0
- package/src/components/waka-status-matrix/index.tsx +565 -0
- package/src/components/waka-stepper/index.tsx +489 -0
- package/src/components/waka-streak-counter/index.tsx +334 -0
- package/src/components/waka-success-explosion/index.tsx +453 -0
- package/src/components/waka-swipe-card/index.tsx +574 -0
- package/src/components/waka-tabs-morph/index.tsx +509 -0
- package/src/components/waka-tag-input/index.tsx +877 -0
- package/src/components/waka-team-banner/index.tsx +1183 -0
- package/src/components/waka-terminal-output/index.tsx +836 -0
- package/src/components/waka-theme-creator/index.tsx +762 -0
- package/src/components/waka-theme-manager/index.tsx +654 -0
- package/src/components/waka-thread-view/index.tsx +874 -0
- package/src/components/waka-tilt-card/index.tsx +250 -0
- package/src/components/waka-time-picker/index.tsx +479 -0
- package/src/components/waka-timeline/index.tsx +385 -0
- package/src/components/waka-tooltip-tour/index.tsx +855 -0
- package/src/components/waka-tour-guide/index.tsx +920 -0
- package/src/components/waka-tournament-bracket/index.tsx +1276 -0
- package/src/components/waka-tree/index.tsx +557 -0
- package/src/components/waka-treemap-chart/index.tsx +1031 -0
- package/src/components/waka-two-factor-setup/index.tsx +995 -0
- package/src/components/waka-typewriter/index.tsx +566 -0
- package/src/components/waka-typing-indicator/index.tsx +649 -0
- package/src/components/waka-versus-card/index.tsx +1026 -0
- package/src/components/waka-video/index.tsx +557 -0
- package/src/components/waka-video-call/index.tsx +1087 -0
- package/src/components/waka-virtual-list/index.tsx +327 -0
- package/src/components/waka-voice-message/index.tsx +1019 -0
- package/src/components/waka-welcome-modal/index.tsx +790 -0
- 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, "&").replace(/</g, "<").replace(/>/g, ">")
|
|
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
|
+
}
|