@wakastellar/ui 2.0.0 → 2.1.1
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/README.md +71 -8
- 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 +6014 -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,637 @@
|
|
|
1
|
+
import { useState, useMemo, useCallback, useEffect } from "react"
|
|
2
|
+
import type {
|
|
3
|
+
FilterOperator,
|
|
4
|
+
FilterColumnType,
|
|
5
|
+
FilterRule,
|
|
6
|
+
FilterGroup,
|
|
7
|
+
FilterPreset,
|
|
8
|
+
FilterableColumn,
|
|
9
|
+
AdvancedFiltersConfig,
|
|
10
|
+
AdvancedFiltersState,
|
|
11
|
+
AdvancedFiltersActions,
|
|
12
|
+
AdvancedFiltersResult,
|
|
13
|
+
} from "../types"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Génère un ID unique
|
|
17
|
+
*/
|
|
18
|
+
function generateId(): string {
|
|
19
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Opérateurs par type de colonne
|
|
24
|
+
*/
|
|
25
|
+
const OPERATORS_BY_TYPE: Record<FilterColumnType, FilterOperator[]> = {
|
|
26
|
+
string: [
|
|
27
|
+
"equals",
|
|
28
|
+
"notEquals",
|
|
29
|
+
"contains",
|
|
30
|
+
"notContains",
|
|
31
|
+
"startsWith",
|
|
32
|
+
"endsWith",
|
|
33
|
+
"isEmpty",
|
|
34
|
+
"isNotEmpty",
|
|
35
|
+
"isNull",
|
|
36
|
+
"isNotNull",
|
|
37
|
+
],
|
|
38
|
+
number: [
|
|
39
|
+
"equals",
|
|
40
|
+
"notEquals",
|
|
41
|
+
"greaterThan",
|
|
42
|
+
"greaterThanOrEqual",
|
|
43
|
+
"lessThan",
|
|
44
|
+
"lessThanOrEqual",
|
|
45
|
+
"between",
|
|
46
|
+
"isNull",
|
|
47
|
+
"isNotNull",
|
|
48
|
+
],
|
|
49
|
+
date: [
|
|
50
|
+
"equals",
|
|
51
|
+
"notEquals",
|
|
52
|
+
"greaterThan",
|
|
53
|
+
"greaterThanOrEqual",
|
|
54
|
+
"lessThan",
|
|
55
|
+
"lessThanOrEqual",
|
|
56
|
+
"between",
|
|
57
|
+
"isNull",
|
|
58
|
+
"isNotNull",
|
|
59
|
+
],
|
|
60
|
+
datetime: [
|
|
61
|
+
"equals",
|
|
62
|
+
"notEquals",
|
|
63
|
+
"greaterThan",
|
|
64
|
+
"greaterThanOrEqual",
|
|
65
|
+
"lessThan",
|
|
66
|
+
"lessThanOrEqual",
|
|
67
|
+
"between",
|
|
68
|
+
"isNull",
|
|
69
|
+
"isNotNull",
|
|
70
|
+
],
|
|
71
|
+
boolean: ["equals", "notEquals", "isNull", "isNotNull"],
|
|
72
|
+
enum: ["equals", "notEquals", "in", "notIn", "isNull", "isNotNull"],
|
|
73
|
+
array: ["contains", "notContains", "isEmpty", "isNotEmpty", "isNull", "isNotNull"],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Labels des opérateurs pour l'affichage
|
|
78
|
+
*/
|
|
79
|
+
export const OPERATOR_LABELS: Record<FilterOperator, string> = {
|
|
80
|
+
equals: "est égal à",
|
|
81
|
+
notEquals: "est différent de",
|
|
82
|
+
contains: "contient",
|
|
83
|
+
notContains: "ne contient pas",
|
|
84
|
+
startsWith: "commence par",
|
|
85
|
+
endsWith: "finit par",
|
|
86
|
+
greaterThan: "supérieur à",
|
|
87
|
+
greaterThanOrEqual: "supérieur ou égal à",
|
|
88
|
+
lessThan: "inférieur à",
|
|
89
|
+
lessThanOrEqual: "inférieur ou égal à",
|
|
90
|
+
between: "entre",
|
|
91
|
+
in: "est dans",
|
|
92
|
+
notIn: "n'est pas dans",
|
|
93
|
+
isEmpty: "est vide",
|
|
94
|
+
isNotEmpty: "n'est pas vide",
|
|
95
|
+
isNull: "est null",
|
|
96
|
+
isNotNull: "n'est pas null",
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Évalue une règle de filtre sur une ligne de données
|
|
101
|
+
*/
|
|
102
|
+
function evaluateRule<TData>(
|
|
103
|
+
row: TData,
|
|
104
|
+
rule: FilterRule<TData>,
|
|
105
|
+
columns: FilterableColumn<TData>[]
|
|
106
|
+
): boolean {
|
|
107
|
+
const column = columns.find((c) => c.key === rule.column)
|
|
108
|
+
|
|
109
|
+
// Si la colonne a une fonction de filtre personnalisée
|
|
110
|
+
if (column?.filterFn) {
|
|
111
|
+
return column.filterFn(row, rule)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const value = (row as Record<string, unknown>)[rule.column as string]
|
|
115
|
+
const filterValue = rule.value
|
|
116
|
+
const filterValueTo = rule.valueTo
|
|
117
|
+
|
|
118
|
+
// Opérateurs de nullité
|
|
119
|
+
if (rule.operator === "isNull") return value === null || value === undefined
|
|
120
|
+
if (rule.operator === "isNotNull") return value !== null && value !== undefined
|
|
121
|
+
if (rule.operator === "isEmpty") {
|
|
122
|
+
if (Array.isArray(value)) return value.length === 0
|
|
123
|
+
if (typeof value === "string") return value.trim() === ""
|
|
124
|
+
return value === null || value === undefined
|
|
125
|
+
}
|
|
126
|
+
if (rule.operator === "isNotEmpty") {
|
|
127
|
+
if (Array.isArray(value)) return value.length > 0
|
|
128
|
+
if (typeof value === "string") return value.trim() !== ""
|
|
129
|
+
return value !== null && value !== undefined
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Si la valeur est null/undefined, les autres opérateurs retournent false
|
|
133
|
+
if (value === null || value === undefined) return false
|
|
134
|
+
|
|
135
|
+
// Comparaisons de chaînes
|
|
136
|
+
if (typeof value === "string" || column?.type === "string") {
|
|
137
|
+
const strValue = String(value).toLowerCase()
|
|
138
|
+
const strFilter = String(filterValue ?? "").toLowerCase()
|
|
139
|
+
|
|
140
|
+
switch (rule.operator) {
|
|
141
|
+
case "equals":
|
|
142
|
+
return strValue === strFilter
|
|
143
|
+
case "notEquals":
|
|
144
|
+
return strValue !== strFilter
|
|
145
|
+
case "contains":
|
|
146
|
+
return strValue.includes(strFilter)
|
|
147
|
+
case "notContains":
|
|
148
|
+
return !strValue.includes(strFilter)
|
|
149
|
+
case "startsWith":
|
|
150
|
+
return strValue.startsWith(strFilter)
|
|
151
|
+
case "endsWith":
|
|
152
|
+
return strValue.endsWith(strFilter)
|
|
153
|
+
default:
|
|
154
|
+
return true
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Comparaisons numériques
|
|
159
|
+
if (typeof value === "number" || column?.type === "number") {
|
|
160
|
+
const numValue = Number(value)
|
|
161
|
+
const numFilter = Number(filterValue)
|
|
162
|
+
const numFilterTo = filterValueTo !== undefined ? Number(filterValueTo) : undefined
|
|
163
|
+
|
|
164
|
+
switch (rule.operator) {
|
|
165
|
+
case "equals":
|
|
166
|
+
return numValue === numFilter
|
|
167
|
+
case "notEquals":
|
|
168
|
+
return numValue !== numFilter
|
|
169
|
+
case "greaterThan":
|
|
170
|
+
return numValue > numFilter
|
|
171
|
+
case "greaterThanOrEqual":
|
|
172
|
+
return numValue >= numFilter
|
|
173
|
+
case "lessThan":
|
|
174
|
+
return numValue < numFilter
|
|
175
|
+
case "lessThanOrEqual":
|
|
176
|
+
return numValue <= numFilter
|
|
177
|
+
case "between":
|
|
178
|
+
return numFilterTo !== undefined && numValue >= numFilter && numValue <= numFilterTo
|
|
179
|
+
default:
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Comparaisons de dates
|
|
185
|
+
if (value instanceof Date || column?.type === "date" || column?.type === "datetime") {
|
|
186
|
+
const dateValue = value instanceof Date ? value : new Date(value as string)
|
|
187
|
+
const dateFilter = filterValue instanceof Date ? filterValue : new Date(filterValue as string)
|
|
188
|
+
const dateFilterTo =
|
|
189
|
+
filterValueTo instanceof Date ? filterValueTo : filterValueTo ? new Date(filterValueTo as string) : undefined
|
|
190
|
+
|
|
191
|
+
switch (rule.operator) {
|
|
192
|
+
case "equals":
|
|
193
|
+
return dateValue.getTime() === dateFilter.getTime()
|
|
194
|
+
case "notEquals":
|
|
195
|
+
return dateValue.getTime() !== dateFilter.getTime()
|
|
196
|
+
case "greaterThan":
|
|
197
|
+
return dateValue.getTime() > dateFilter.getTime()
|
|
198
|
+
case "greaterThanOrEqual":
|
|
199
|
+
return dateValue.getTime() >= dateFilter.getTime()
|
|
200
|
+
case "lessThan":
|
|
201
|
+
return dateValue.getTime() < dateFilter.getTime()
|
|
202
|
+
case "lessThanOrEqual":
|
|
203
|
+
return dateValue.getTime() <= dateFilter.getTime()
|
|
204
|
+
case "between":
|
|
205
|
+
return dateFilterTo !== undefined && dateValue >= dateFilter && dateValue <= dateFilterTo
|
|
206
|
+
default:
|
|
207
|
+
return true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Comparaisons booléennes
|
|
212
|
+
if (typeof value === "boolean" || column?.type === "boolean") {
|
|
213
|
+
const boolValue = Boolean(value)
|
|
214
|
+
const boolFilter = filterValue === true || filterValue === "true"
|
|
215
|
+
|
|
216
|
+
switch (rule.operator) {
|
|
217
|
+
case "equals":
|
|
218
|
+
return boolValue === boolFilter
|
|
219
|
+
case "notEquals":
|
|
220
|
+
return boolValue !== boolFilter
|
|
221
|
+
default:
|
|
222
|
+
return true
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Comparaisons de tableaux et enum (in/notIn)
|
|
227
|
+
if (rule.operator === "in") {
|
|
228
|
+
if (Array.isArray(filterValue)) {
|
|
229
|
+
return filterValue.includes(value)
|
|
230
|
+
}
|
|
231
|
+
return value === filterValue
|
|
232
|
+
}
|
|
233
|
+
if (rule.operator === "notIn") {
|
|
234
|
+
if (Array.isArray(filterValue)) {
|
|
235
|
+
return !filterValue.includes(value)
|
|
236
|
+
}
|
|
237
|
+
return value !== filterValue
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Contient pour les tableaux
|
|
241
|
+
if (Array.isArray(value)) {
|
|
242
|
+
switch (rule.operator) {
|
|
243
|
+
case "contains":
|
|
244
|
+
return value.includes(filterValue)
|
|
245
|
+
case "notContains":
|
|
246
|
+
return !value.includes(filterValue)
|
|
247
|
+
default:
|
|
248
|
+
return true
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return true
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Évalue un groupe de filtres (récursif)
|
|
257
|
+
*/
|
|
258
|
+
function evaluateGroup<TData>(
|
|
259
|
+
row: TData,
|
|
260
|
+
group: FilterGroup<TData>,
|
|
261
|
+
columns: FilterableColumn<TData>[]
|
|
262
|
+
): boolean {
|
|
263
|
+
const enabledRules = group.rules.filter((r) => r.enabled !== false)
|
|
264
|
+
const results: boolean[] = []
|
|
265
|
+
|
|
266
|
+
// Évaluer les règles
|
|
267
|
+
for (const rule of enabledRules) {
|
|
268
|
+
results.push(evaluateRule(row, rule, columns))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Évaluer les sous-groupes
|
|
272
|
+
for (const subGroup of group.groups) {
|
|
273
|
+
results.push(evaluateGroup(row, subGroup, columns))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Si pas de résultats, considérer comme match
|
|
277
|
+
if (results.length === 0) return true
|
|
278
|
+
|
|
279
|
+
// Appliquer le connecteur
|
|
280
|
+
if (group.connector === "AND") {
|
|
281
|
+
return results.every(Boolean)
|
|
282
|
+
} else {
|
|
283
|
+
return results.some(Boolean)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Trouve une règle par ID dans un groupe (récursif)
|
|
289
|
+
*/
|
|
290
|
+
function findRuleById<TData>(
|
|
291
|
+
group: FilterGroup<TData>,
|
|
292
|
+
ruleId: string
|
|
293
|
+
): { rule: FilterRule<TData>; parent: FilterGroup<TData> } | null {
|
|
294
|
+
for (const rule of group.rules) {
|
|
295
|
+
if (rule.id === ruleId) {
|
|
296
|
+
return { rule, parent: group }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
for (const subGroup of group.groups) {
|
|
300
|
+
const found = findRuleById(subGroup, ruleId)
|
|
301
|
+
if (found) return found
|
|
302
|
+
}
|
|
303
|
+
return null
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Trouve un groupe par ID (récursif)
|
|
308
|
+
*/
|
|
309
|
+
function findGroupById<TData>(
|
|
310
|
+
group: FilterGroup<TData>,
|
|
311
|
+
groupId: string,
|
|
312
|
+
parent: FilterGroup<TData> | null = null
|
|
313
|
+
): { group: FilterGroup<TData>; parent: FilterGroup<TData> | null } | null {
|
|
314
|
+
if (group.id === groupId) {
|
|
315
|
+
return { group, parent }
|
|
316
|
+
}
|
|
317
|
+
for (const subGroup of group.groups) {
|
|
318
|
+
const found = findGroupById(subGroup, groupId, group)
|
|
319
|
+
if (found) return found
|
|
320
|
+
}
|
|
321
|
+
return null
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Clone profondément un filtre
|
|
326
|
+
*/
|
|
327
|
+
function deepCloneFilter<TData>(filter: FilterGroup<TData>): FilterGroup<TData> {
|
|
328
|
+
return JSON.parse(JSON.stringify(filter))
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Hook pour gérer les filtres avancés du DataTable
|
|
333
|
+
*/
|
|
334
|
+
export function useDataTableAdvancedFilters<TData extends Record<string, unknown>>({
|
|
335
|
+
data,
|
|
336
|
+
config,
|
|
337
|
+
}: {
|
|
338
|
+
data: TData[]
|
|
339
|
+
config: AdvancedFiltersConfig<TData>
|
|
340
|
+
}): AdvancedFiltersResult<TData> {
|
|
341
|
+
// État initial
|
|
342
|
+
const createEmptyFilter = useCallback((): FilterGroup<TData> => ({
|
|
343
|
+
id: generateId(),
|
|
344
|
+
connector: "AND",
|
|
345
|
+
rules: [],
|
|
346
|
+
groups: [],
|
|
347
|
+
}), [])
|
|
348
|
+
|
|
349
|
+
const [state, setState] = useState<AdvancedFiltersState<TData>>(() => ({
|
|
350
|
+
currentFilter: createEmptyFilter(),
|
|
351
|
+
presets: config.presets || [],
|
|
352
|
+
activePresetId: null,
|
|
353
|
+
isEditing: false,
|
|
354
|
+
isDirty: false,
|
|
355
|
+
}))
|
|
356
|
+
|
|
357
|
+
// Charger les presets depuis localStorage
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
if (!config.persistPresets || typeof window === "undefined") return
|
|
360
|
+
|
|
361
|
+
const storageKey = config.storageKey || "datatable-filter-presets"
|
|
362
|
+
try {
|
|
363
|
+
const stored = localStorage.getItem(storageKey)
|
|
364
|
+
if (stored) {
|
|
365
|
+
const presets = JSON.parse(stored) as FilterPreset<TData>[]
|
|
366
|
+
setState((s) => ({
|
|
367
|
+
...s,
|
|
368
|
+
presets: [...(config.presets || []), ...presets.filter(
|
|
369
|
+
(p) => !config.presets?.some((cp) => cp.id === p.id)
|
|
370
|
+
)],
|
|
371
|
+
}))
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
// Ignore errors
|
|
375
|
+
}
|
|
376
|
+
}, [config.persistPresets, config.storageKey, config.presets])
|
|
377
|
+
|
|
378
|
+
// Sauvegarder les presets dans localStorage
|
|
379
|
+
const savePresetsToStorage = useCallback(
|
|
380
|
+
(presets: FilterPreset<TData>[]) => {
|
|
381
|
+
if (!config.persistPresets || typeof window === "undefined") return
|
|
382
|
+
const storageKey = config.storageKey || "datatable-filter-presets"
|
|
383
|
+
try {
|
|
384
|
+
// Ne sauvegarder que les presets créés par l'utilisateur
|
|
385
|
+
const userPresets = presets.filter(
|
|
386
|
+
(p) => !config.presets?.some((cp) => cp.id === p.id)
|
|
387
|
+
)
|
|
388
|
+
localStorage.setItem(storageKey, JSON.stringify(userPresets))
|
|
389
|
+
} catch {
|
|
390
|
+
// Ignore errors
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
[config.persistPresets, config.storageKey, config.presets]
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
// Opérateurs disponibles pour une colonne
|
|
397
|
+
const getOperatorsForColumn = useCallback(
|
|
398
|
+
(columnKey: keyof TData | string): FilterOperator[] => {
|
|
399
|
+
const column = config.columns.find((c) => c.key === columnKey)
|
|
400
|
+
if (!column) return OPERATORS_BY_TYPE.string
|
|
401
|
+
return column.operators || OPERATORS_BY_TYPE[column.type] || OPERATORS_BY_TYPE.string
|
|
402
|
+
},
|
|
403
|
+
[config.columns]
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
// Validation d'un filtre
|
|
407
|
+
const validateFilter = useCallback(
|
|
408
|
+
(filter: FilterGroup<TData>): { valid: boolean; errors: string[] } => {
|
|
409
|
+
const errors: string[] = []
|
|
410
|
+
|
|
411
|
+
const validateGroup = (group: FilterGroup<TData>, depth = 0) => {
|
|
412
|
+
// Vérifier la profondeur max
|
|
413
|
+
if (config.maxNestingDepth !== undefined && depth > config.maxNestingDepth) {
|
|
414
|
+
errors.push(`Profondeur maximale d'imbrication dépassée (${config.maxNestingDepth})`)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Vérifier le nombre de règles
|
|
418
|
+
if (config.maxRulesPerGroup !== undefined && group.rules.length > config.maxRulesPerGroup) {
|
|
419
|
+
errors.push(`Nombre maximum de règles par groupe dépassé (${config.maxRulesPerGroup})`)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Valider les règles
|
|
423
|
+
for (const rule of group.rules) {
|
|
424
|
+
const column = config.columns.find((c) => c.key === rule.column)
|
|
425
|
+
if (!column) {
|
|
426
|
+
errors.push(`Colonne inconnue: ${String(rule.column)}`)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const allowedOperators = getOperatorsForColumn(rule.column)
|
|
430
|
+
if (!allowedOperators.includes(rule.operator)) {
|
|
431
|
+
errors.push(`Opérateur invalide pour ${String(rule.column)}: ${rule.operator}`)
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Récursif pour les sous-groupes
|
|
436
|
+
for (const subGroup of group.groups) {
|
|
437
|
+
validateGroup(subGroup, depth + 1)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
validateGroup(filter)
|
|
442
|
+
return { valid: errors.length === 0, errors }
|
|
443
|
+
},
|
|
444
|
+
[config.columns, config.maxNestingDepth, config.maxRulesPerGroup, getOperatorsForColumn]
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
// Actions
|
|
448
|
+
const actions: AdvancedFiltersActions<TData> = useMemo(() => ({
|
|
449
|
+
addRule: (groupId, rule) => {
|
|
450
|
+
setState((s) => {
|
|
451
|
+
const newFilter = deepCloneFilter(s.currentFilter)
|
|
452
|
+
const found = findGroupById(newFilter, groupId)
|
|
453
|
+
if (found) {
|
|
454
|
+
found.group.rules.push({
|
|
455
|
+
...rule,
|
|
456
|
+
id: generateId(),
|
|
457
|
+
enabled: true,
|
|
458
|
+
} as FilterRule<TData>)
|
|
459
|
+
}
|
|
460
|
+
return { ...s, currentFilter: newFilter, isDirty: true }
|
|
461
|
+
})
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
updateRule: (ruleId, updates) => {
|
|
465
|
+
setState((s) => {
|
|
466
|
+
const newFilter = deepCloneFilter(s.currentFilter)
|
|
467
|
+
const found = findRuleById(newFilter, ruleId)
|
|
468
|
+
if (found) {
|
|
469
|
+
Object.assign(found.rule, updates)
|
|
470
|
+
}
|
|
471
|
+
return { ...s, currentFilter: newFilter, isDirty: true }
|
|
472
|
+
})
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
removeRule: (ruleId) => {
|
|
476
|
+
setState((s) => {
|
|
477
|
+
const newFilter = deepCloneFilter(s.currentFilter)
|
|
478
|
+
const found = findRuleById(newFilter, ruleId)
|
|
479
|
+
if (found) {
|
|
480
|
+
found.parent.rules = found.parent.rules.filter((r) => r.id !== ruleId)
|
|
481
|
+
}
|
|
482
|
+
return { ...s, currentFilter: newFilter, isDirty: true }
|
|
483
|
+
})
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
addGroup: (parentGroupId, connector = "AND") => {
|
|
487
|
+
setState((s) => {
|
|
488
|
+
const newFilter = deepCloneFilter(s.currentFilter)
|
|
489
|
+
const found = findGroupById(newFilter, parentGroupId)
|
|
490
|
+
if (found) {
|
|
491
|
+
found.group.groups.push({
|
|
492
|
+
id: generateId(),
|
|
493
|
+
connector,
|
|
494
|
+
rules: [],
|
|
495
|
+
groups: [],
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
return { ...s, currentFilter: newFilter, isDirty: true }
|
|
499
|
+
})
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
removeGroup: (groupId) => {
|
|
503
|
+
setState((s) => {
|
|
504
|
+
const newFilter = deepCloneFilter(s.currentFilter)
|
|
505
|
+
const found = findGroupById(newFilter, groupId)
|
|
506
|
+
if (found && found.parent) {
|
|
507
|
+
found.parent.groups = found.parent.groups.filter((g) => g.id !== groupId)
|
|
508
|
+
}
|
|
509
|
+
return { ...s, currentFilter: newFilter, isDirty: true }
|
|
510
|
+
})
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
setGroupConnector: (groupId, connector) => {
|
|
514
|
+
setState((s) => {
|
|
515
|
+
const newFilter = deepCloneFilter(s.currentFilter)
|
|
516
|
+
const found = findGroupById(newFilter, groupId)
|
|
517
|
+
if (found) {
|
|
518
|
+
found.group.connector = connector
|
|
519
|
+
}
|
|
520
|
+
return { ...s, currentFilter: newFilter, isDirty: true }
|
|
521
|
+
})
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
resetFilters: () => {
|
|
525
|
+
setState((s) => ({
|
|
526
|
+
...s,
|
|
527
|
+
currentFilter: createEmptyFilter(),
|
|
528
|
+
activePresetId: null,
|
|
529
|
+
isDirty: false,
|
|
530
|
+
}))
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
applyFilters: () => {
|
|
534
|
+
setState((s) => {
|
|
535
|
+
config.onFilterChange?.(s.currentFilter)
|
|
536
|
+
return { ...s, isEditing: false }
|
|
537
|
+
})
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
saveAsPreset: (name, description) => {
|
|
541
|
+
setState((s) => {
|
|
542
|
+
const preset: FilterPreset<TData> = {
|
|
543
|
+
id: generateId(),
|
|
544
|
+
name,
|
|
545
|
+
description,
|
|
546
|
+
filter: deepCloneFilter(s.currentFilter),
|
|
547
|
+
createdAt: new Date(),
|
|
548
|
+
}
|
|
549
|
+
const newPresets = [...s.presets, preset]
|
|
550
|
+
savePresetsToStorage(newPresets)
|
|
551
|
+
config.onPresetSave?.(preset)
|
|
552
|
+
return {
|
|
553
|
+
...s,
|
|
554
|
+
presets: newPresets,
|
|
555
|
+
activePresetId: preset.id,
|
|
556
|
+
isDirty: false,
|
|
557
|
+
}
|
|
558
|
+
})
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
applyPreset: (presetId) => {
|
|
562
|
+
setState((s) => {
|
|
563
|
+
const preset = s.presets.find((p) => p.id === presetId)
|
|
564
|
+
if (!preset) return s
|
|
565
|
+
const newFilter = deepCloneFilter(preset.filter)
|
|
566
|
+
config.onFilterChange?.(newFilter)
|
|
567
|
+
return {
|
|
568
|
+
...s,
|
|
569
|
+
currentFilter: newFilter,
|
|
570
|
+
activePresetId: presetId,
|
|
571
|
+
isDirty: false,
|
|
572
|
+
}
|
|
573
|
+
})
|
|
574
|
+
},
|
|
575
|
+
|
|
576
|
+
deletePreset: (presetId) => {
|
|
577
|
+
setState((s) => {
|
|
578
|
+
const newPresets = s.presets.filter((p) => p.id !== presetId)
|
|
579
|
+
savePresetsToStorage(newPresets)
|
|
580
|
+
config.onPresetDelete?.(presetId)
|
|
581
|
+
return {
|
|
582
|
+
...s,
|
|
583
|
+
presets: newPresets,
|
|
584
|
+
activePresetId: s.activePresetId === presetId ? null : s.activePresetId,
|
|
585
|
+
}
|
|
586
|
+
})
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
importFilters: (json) => {
|
|
590
|
+
try {
|
|
591
|
+
const filter = JSON.parse(json) as FilterGroup<TData>
|
|
592
|
+
const validation = validateFilter(filter)
|
|
593
|
+
if (!validation.valid) {
|
|
594
|
+
console.error("Invalid filter:", validation.errors)
|
|
595
|
+
return
|
|
596
|
+
}
|
|
597
|
+
setState((s) => ({
|
|
598
|
+
...s,
|
|
599
|
+
currentFilter: filter,
|
|
600
|
+
activePresetId: null,
|
|
601
|
+
isDirty: true,
|
|
602
|
+
}))
|
|
603
|
+
} catch (e) {
|
|
604
|
+
console.error("Failed to import filters:", e)
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
|
|
608
|
+
exportFilters: () => {
|
|
609
|
+
return JSON.stringify(state.currentFilter, null, 2)
|
|
610
|
+
},
|
|
611
|
+
}), [config, createEmptyFilter, savePresetsToStorage, validateFilter, state.currentFilter])
|
|
612
|
+
|
|
613
|
+
// Données filtrées
|
|
614
|
+
const filteredData = useMemo(() => {
|
|
615
|
+
if (!config.enabled) return data
|
|
616
|
+
|
|
617
|
+
// Si le filtre est vide, retourner toutes les données
|
|
618
|
+
if (state.currentFilter.rules.length === 0 && state.currentFilter.groups.length === 0) {
|
|
619
|
+
return data
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return data.filter((row) => evaluateGroup(row, state.currentFilter, config.columns))
|
|
623
|
+
}, [data, config.enabled, config.columns, state.currentFilter])
|
|
624
|
+
|
|
625
|
+
return {
|
|
626
|
+
state,
|
|
627
|
+
actions,
|
|
628
|
+
filteredData,
|
|
629
|
+
filteredCount: filteredData.length,
|
|
630
|
+
getOperatorsForColumn,
|
|
631
|
+
createEmptyFilter,
|
|
632
|
+
validateFilter,
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export { OPERATORS_BY_TYPE }
|
|
637
|
+
export default useDataTableAdvancedFilters
|