@drivenets/design-system 0.3.0 → 0.4.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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/dist/components/ds-alert-banner/ds-alert-banner.d.cts +5 -5
- package/dist/components/ds-alert-banner/ds-alert-banner.d.cts.map +1 -1
- package/dist/components/ds-alert-banner/ds-alert-banner.d.ts +5 -5
- package/dist/components/ds-alert-banner/ds-alert-banner.d.ts.map +1 -1
- package/dist/components/ds-alert-banner/ds-alert-banner.types.d.cts.map +1 -1
- package/dist/components/ds-autocomplete/ds-autocomplete.cjs +218 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.d.cts +22 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.d.cts.map +1 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.d.ts +22 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.d.ts.map +1 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.js +217 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.js.map +1 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.module.cjs +27 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.module.js +27 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.module.js.map +1 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.types.d.cts +79 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.types.d.cts.map +1 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.types.d.ts +80 -0
- package/dist/components/ds-autocomplete/ds-autocomplete.types.d.ts.map +1 -0
- package/dist/components/ds-autocomplete/index.cjs +1 -0
- package/dist/components/ds-autocomplete/index.d.ts +2 -0
- package/dist/components/ds-autocomplete/index.js +1 -0
- package/dist/components/ds-avatar/ds-avatar.module.js.map +1 -1
- package/dist/components/ds-avatar-group/ds-avatar-group.module.js.map +1 -1
- package/dist/components/ds-button/ds-button.d.cts +2 -2
- package/dist/components/ds-button/ds-button.d.cts.map +1 -1
- package/dist/components/ds-confirmation/ds-confirmation.cjs +20 -221
- package/dist/components/ds-confirmation/ds-confirmation.d.cts +22 -27
- package/dist/components/ds-confirmation/ds-confirmation.d.cts.map +1 -1
- package/dist/components/ds-confirmation/ds-confirmation.d.ts +22 -27
- package/dist/components/ds-confirmation/ds-confirmation.d.ts.map +1 -1
- package/dist/components/ds-confirmation/ds-confirmation.js +21 -221
- package/dist/components/ds-confirmation/ds-confirmation.js.map +1 -1
- package/dist/components/ds-confirmation/ds-confirmation.types.d.cts +2 -1
- package/dist/components/ds-confirmation/ds-confirmation.types.d.cts.map +1 -1
- package/dist/components/ds-confirmation/ds-confirmation.types.d.ts +2 -1
- package/dist/components/ds-confirmation/ds-confirmation.types.d.ts.map +1 -1
- package/dist/components/ds-date-input/ds-date-input.cjs +1 -1
- package/dist/components/ds-date-input/ds-date-input.d.cts +2 -2
- package/dist/components/ds-date-input/ds-date-input.d.ts +2 -2
- package/dist/components/ds-date-input/ds-date-input.js +1 -1
- package/dist/components/ds-drawer/ds-drawer.cjs +1 -1
- package/dist/components/ds-drawer/ds-drawer.d.cts +9 -9
- package/dist/components/ds-drawer/ds-drawer.d.cts.map +1 -1
- package/dist/components/ds-drawer/ds-drawer.d.ts +9 -9
- package/dist/components/ds-drawer/ds-drawer.js +1 -1
- package/dist/components/ds-dropdown-menu/ds-dropdown-menu.cjs +1 -1
- package/dist/components/ds-dropdown-menu/ds-dropdown-menu.js +1 -1
- package/dist/components/ds-dropdown-menu/ds-dropdown-menu.module.cjs +20 -20
- package/dist/components/ds-dropdown-menu/ds-dropdown-menu.module.js +20 -20
- package/dist/components/ds-dropdown-menu/ds-dropdown-menu.module.js.map +1 -1
- package/dist/components/ds-expandable-text-input/ds-expandable-text-input.d.cts +2 -2
- package/dist/components/ds-expandable-text-input/ds-expandable-text-input.d.ts +2 -2
- package/dist/components/ds-form-control/ds-form-control.d.cts +8 -8
- package/dist/components/ds-form-control/ds-form-control.d.ts +8 -8
- package/dist/components/ds-loader/ds-loader.d.cts +2 -2
- package/dist/components/ds-loader/ds-loader.d.ts +2 -2
- package/dist/components/ds-modal/ds-modal.cjs +47 -48
- package/dist/components/ds-modal/ds-modal.d.cts +11 -10
- package/dist/components/ds-modal/ds-modal.d.cts.map +1 -1
- package/dist/components/ds-modal/ds-modal.d.ts +11 -10
- package/dist/components/ds-modal/ds-modal.d.ts.map +1 -1
- package/dist/components/ds-modal/ds-modal.js +47 -48
- package/dist/components/ds-modal/ds-modal.js.map +1 -1
- package/dist/components/ds-modal/ds-modal.module.cjs +24 -24
- package/dist/components/ds-modal/ds-modal.module.js +24 -24
- package/dist/components/ds-modal/ds-modal.module.js.map +1 -1
- package/dist/components/ds-modal/ds-modal.types.d.cts +10 -5
- package/dist/components/ds-modal/ds-modal.types.d.cts.map +1 -1
- package/dist/components/ds-modal/ds-modal.types.d.ts +10 -5
- package/dist/components/ds-modal/ds-modal.types.d.ts.map +1 -1
- package/dist/components/ds-panel/ds-panel.d.cts +2 -2
- package/dist/components/ds-panel/ds-panel.d.ts +2 -2
- package/dist/components/ds-select/ds-select.cjs +1 -1
- package/dist/components/ds-select/ds-select.d.cts +2 -2
- package/dist/components/ds-select/ds-select.js +1 -1
- package/dist/components/ds-skeleton/ds-skeleton-circle.d.cts +2 -2
- package/dist/components/ds-skeleton/ds-skeleton-circle.d.ts +2 -2
- package/dist/components/ds-skeleton/ds-skeleton-rect.d.cts +2 -2
- package/dist/components/ds-skeleton/ds-skeleton-rect.d.ts +2 -2
- package/dist/components/ds-skeleton/ds-skeleton-rect.d.ts.map +1 -1
- package/dist/components/ds-skeleton/ds-skeleton-text.d.cts +2 -2
- package/dist/components/ds-skeleton/ds-skeleton-text.d.ts +2 -2
- package/dist/components/ds-skeleton/ds-skeleton.d.cts +4 -4
- package/dist/components/ds-skeleton/ds-skeleton.d.ts +4 -4
- package/dist/components/ds-smart-tabs/ds-smart-tabs.d.cts +2 -2
- package/dist/components/ds-smart-tabs/ds-smart-tabs.d.ts +2 -2
- package/dist/components/ds-smart-tabs/ds-smart-tabs.d.ts.map +1 -1
- package/dist/components/ds-stepper/components/ds-step-content.d.cts +2 -2
- package/dist/components/ds-stepper/components/ds-step-content.d.ts +2 -2
- package/dist/components/ds-stepper/components/ds-step-next-button.d.cts +2 -2
- package/dist/components/ds-stepper/components/ds-step-next-button.d.ts +2 -2
- package/dist/components/ds-stepper/components/ds-step.d.cts +2 -2
- package/dist/components/ds-stepper/components/ds-step.d.ts +2 -2
- package/dist/components/ds-stepper/ds-stepper.d.cts +2 -2
- package/dist/components/ds-stepper/ds-stepper.d.ts +2 -2
- package/dist/components/ds-table/ds-table.d.ts +2 -2
- package/dist/components/ds-table/filters/hooks/index.d.ts +1 -1
- package/dist/components/ds-table/filters/hooks/use-table-filters.cjs +180 -185
- package/dist/components/ds-table/filters/hooks/use-table-filters.d.cts +58 -25
- package/dist/components/ds-table/filters/hooks/use-table-filters.d.cts.map +1 -1
- package/dist/components/ds-table/filters/hooks/use-table-filters.d.ts +58 -25
- package/dist/components/ds-table/filters/hooks/use-table-filters.d.ts.map +1 -1
- package/dist/components/ds-table/filters/hooks/use-table-filters.js +180 -185
- package/dist/components/ds-table/filters/hooks/use-table-filters.js.map +1 -1
- package/dist/components/ds-table/filters/index.d.ts +1 -1
- package/dist/components/ds-table/index.d.ts +1 -1
- package/dist/components/ds-tabs/components/ds-tabs-content/ds-tabs-content.module.cjs +2 -2
- package/dist/components/ds-tabs/components/ds-tabs-content/ds-tabs-content.module.js +2 -2
- package/dist/components/ds-tabs/components/ds-tabs-content/ds-tabs-content.module.js.map +1 -1
- package/dist/components/ds-tabs/ds-tabs.d.cts +5 -5
- package/dist/components/ds-tabs/ds-tabs.d.ts +5 -5
- package/dist/components/ds-tag/ds-tag.d.cts +2 -2
- package/dist/components/ds-tag/ds-tag.d.ts +2 -2
- package/dist/components/ds-tag-filter/ds-tag-filter.d.cts +2 -2
- package/dist/components/ds-tag-filter/ds-tag-filter.d.ts +2 -2
- package/dist/components/ds-toggle/ds-toggle.d.cts +2 -2
- package/dist/components/ds-toggle/ds-toggle.d.ts +2 -2
- package/dist/components/ds-vertical-tabs/ds-vertical-tabs.d.cts +5 -5
- package/dist/components/ds-vertical-tabs/ds-vertical-tabs.d.ts +5 -5
- package/dist/index.cjs +4 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/index.min.css +1 -1
- package/package.json +1 -1
- package/src/components/ds-autocomplete/ds-autocomplete.module.scss +257 -0
- package/src/components/ds-autocomplete/ds-autocomplete.tsx +130 -0
- package/src/components/ds-autocomplete/ds-autocomplete.types.tsx +76 -0
- package/src/components/ds-autocomplete/index.ts +2 -0
- package/src/components/ds-avatar/ds-avatar.module.scss +1 -1
- package/src/components/ds-avatar-group/ds-avatar-group.module.scss +1 -1
- package/src/components/ds-confirmation/ds-confirmation.tsx +11 -115
- package/src/components/ds-confirmation/ds-confirmation.types.tsx +2 -1
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +0 -1
- package/src/components/ds-modal/ds-modal.module.scss +123 -60
- package/src/components/ds-modal/ds-modal.tsx +5 -2
- package/src/components/ds-modal/ds-modal.types.tsx +14 -5
- package/src/components/ds-table/filters/hooks/index.ts +1 -1
- package/src/components/ds-table/filters/hooks/use-table-filters.ts +117 -116
- package/src/components/ds-tabs/components/ds-tabs-content/ds-tabs-content.module.scss +4 -0
- package/src/index.ts +1 -0
- package/src/styles/shared/_modal.scss +0 -123
- package/dist/components/ds-confirmation/ds-confirmation.module.cjs +0 -21
- package/dist/components/ds-confirmation/ds-confirmation.module.js +0 -21
- package/dist/components/ds-confirmation/ds-confirmation.module.js.map +0 -1
- package/src/components/ds-confirmation/ds-confirmation.module.scss +0 -58
|
@@ -1,20 +1,91 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
$pad-x: 24px;
|
|
4
|
-
$pad-y: 16px;
|
|
1
|
+
$pad-x: var(--spacing-lg);
|
|
2
|
+
$pad-y: var(--spacing-standard);
|
|
5
3
|
$max-width: 90vw;
|
|
6
|
-
$
|
|
4
|
+
$border-size: 1px;
|
|
5
|
+
$border: $border-size solid var(--color-border-secondary);
|
|
7
6
|
|
|
8
7
|
.overlay {
|
|
9
|
-
|
|
8
|
+
position: fixed;
|
|
9
|
+
inset: 0;
|
|
10
|
+
background: var(--color-background-brand);
|
|
11
|
+
opacity: 0.4;
|
|
12
|
+
z-index: 1199;
|
|
13
|
+
|
|
14
|
+
&[data-state='open'] {
|
|
15
|
+
animation: fade-in-overlay 0.2s ease-in-out both;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&[data-state='closed'] {
|
|
19
|
+
animation: fade-out-overlay 0.2s 0.1s ease-in-out both;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@keyframes fade-in-overlay {
|
|
24
|
+
0% {
|
|
25
|
+
opacity: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
100% {
|
|
29
|
+
opacity: 0.4;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@keyframes fade-out-overlay {
|
|
34
|
+
0% {
|
|
35
|
+
opacity: 0.4;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
100% {
|
|
39
|
+
opacity: 0;
|
|
40
|
+
}
|
|
10
41
|
}
|
|
11
42
|
|
|
12
43
|
.modal {
|
|
13
|
-
|
|
44
|
+
position: fixed;
|
|
45
|
+
inset: 50% auto auto 50%;
|
|
46
|
+
background: var(--color-background);
|
|
47
|
+
border-radius: 4px;
|
|
48
|
+
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.15);
|
|
49
|
+
z-index: 1200;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
inline-size: min(var(--col-span-6), $max-width);
|
|
52
|
+
|
|
53
|
+
&[data-state='open'] {
|
|
54
|
+
animation: scale-in-modal 0.2s 0.1s ease-in-out both;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&[data-state='closed'] {
|
|
58
|
+
animation: scale-out-modal 0.2s ease-in-out both;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@keyframes scale-in-modal {
|
|
63
|
+
0% {
|
|
64
|
+
opacity: 0;
|
|
65
|
+
transform: translate(-50%, -50%) scale(0.9);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
100% {
|
|
69
|
+
opacity: 1;
|
|
70
|
+
transform: translate(-50%, -50%) scale(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@keyframes scale-out-modal {
|
|
75
|
+
0% {
|
|
76
|
+
opacity: 1;
|
|
77
|
+
transform: translate(-50%, -50%) scale(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
100% {
|
|
81
|
+
opacity: 0;
|
|
82
|
+
transform: translate(-50%, -50%) scale(0.9);
|
|
83
|
+
}
|
|
14
84
|
}
|
|
15
85
|
|
|
16
86
|
.content {
|
|
17
|
-
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
18
89
|
height: 100%;
|
|
19
90
|
}
|
|
20
91
|
|
|
@@ -25,76 +96,68 @@ $max-height: 90vh;
|
|
|
25
96
|
}
|
|
26
97
|
|
|
27
98
|
.header {
|
|
28
|
-
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: var(--spacing-xs);
|
|
102
|
+
position: relative;
|
|
103
|
+
background: inherit;
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
|
|
106
|
+
&::after {
|
|
107
|
+
content: '';
|
|
108
|
+
position: absolute;
|
|
109
|
+
bottom: 0;
|
|
110
|
+
left: $pad-x;
|
|
111
|
+
right: $pad-x;
|
|
112
|
+
height: $border-size;
|
|
113
|
+
background: var(--color-border-secondary);
|
|
114
|
+
}
|
|
29
115
|
}
|
|
30
116
|
|
|
31
117
|
.footer {
|
|
32
|
-
@include shared.footer-base;
|
|
33
118
|
display: flex;
|
|
119
|
+
background: inherit;
|
|
120
|
+
flex-shrink: 0;
|
|
34
121
|
}
|
|
35
122
|
|
|
36
123
|
.title {
|
|
37
|
-
|
|
124
|
+
color: var(--color-font-main);
|
|
125
|
+
margin: 0;
|
|
126
|
+
flex: 1;
|
|
38
127
|
}
|
|
39
128
|
|
|
40
129
|
.body {
|
|
41
|
-
|
|
130
|
+
flex: 1;
|
|
131
|
+
overflow-y: auto;
|
|
132
|
+
overflow-x: hidden;
|
|
133
|
+
min-height: 0;
|
|
42
134
|
}
|
|
43
135
|
|
|
44
136
|
.actions {
|
|
45
|
-
|
|
137
|
+
display: flex;
|
|
138
|
+
gap: var(--spacing-xs);
|
|
139
|
+
justify-content: flex-end;
|
|
46
140
|
margin-left: auto;
|
|
47
141
|
}
|
|
48
142
|
|
|
49
|
-
// width
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.modal.cols-1 {
|
|
55
|
-
inline-size: min(var(--col-span-1), $max-width);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.modal.cols-2 {
|
|
59
|
-
inline-size: min(var(--col-span-2), $max-width);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.modal.cols-3 {
|
|
63
|
-
inline-size: min(var(--col-span-3), $max-width);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.modal.cols-4 {
|
|
67
|
-
inline-size: min(var(--col-span-4), $max-width);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.modal.cols-5 {
|
|
71
|
-
inline-size: min(var(--col-span-5), $max-width);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.modal.cols-6 {
|
|
75
|
-
inline-size: min(var(--col-span-6), $max-width);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.modal.cols-7 {
|
|
79
|
-
inline-size: min(var(--col-span-7), $max-width);
|
|
80
|
-
}
|
|
143
|
+
// Dividers mode: full-width borders on header and footer
|
|
144
|
+
.content[data-dividers] {
|
|
145
|
+
.header {
|
|
146
|
+
border-bottom: $border;
|
|
81
147
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.modal.cols-9 {
|
|
87
|
-
inline-size: min(var(--col-span-9), $max-width);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.modal.cols-10 {
|
|
91
|
-
inline-size: min(var(--col-span-10), $max-width);
|
|
92
|
-
}
|
|
148
|
+
&::after {
|
|
149
|
+
display: none;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
93
152
|
|
|
94
|
-
.
|
|
95
|
-
|
|
153
|
+
.footer {
|
|
154
|
+
border-top: $border;
|
|
155
|
+
}
|
|
96
156
|
}
|
|
97
157
|
|
|
98
|
-
|
|
99
|
-
|
|
158
|
+
// Width classes derived from DS grid
|
|
159
|
+
@for $i from 1 through 12 {
|
|
160
|
+
.modal.cols-#{$i} {
|
|
161
|
+
inline-size: min(var(--col-span-#{$i}), $max-width);
|
|
162
|
+
}
|
|
100
163
|
}
|
|
@@ -14,14 +14,15 @@ import { DsTypography } from '../ds-typography';
|
|
|
14
14
|
*/
|
|
15
15
|
const DsModal = ({
|
|
16
16
|
open,
|
|
17
|
-
onOpenChange,
|
|
18
17
|
columns = 6,
|
|
18
|
+
dividers = false,
|
|
19
19
|
style,
|
|
20
20
|
className,
|
|
21
21
|
modal = true,
|
|
22
22
|
closeOnEscape,
|
|
23
23
|
closeOnInteractOutside = false,
|
|
24
24
|
children,
|
|
25
|
+
onOpenChange,
|
|
25
26
|
}: DsModalProps) => {
|
|
26
27
|
const handleOpenChange = (details: { open: boolean }) => {
|
|
27
28
|
onOpenChange(details.open);
|
|
@@ -48,7 +49,9 @@ const DsModal = ({
|
|
|
48
49
|
styles[`cols-${columns}`],
|
|
49
50
|
)}
|
|
50
51
|
>
|
|
51
|
-
<div className={
|
|
52
|
+
<div className={styles.content} data-dividers={dividers || undefined}>
|
|
53
|
+
{children}
|
|
54
|
+
</div>
|
|
52
55
|
</Dialog.Content>
|
|
53
56
|
</Dialog.Positioner>
|
|
54
57
|
</Portal>
|
|
@@ -17,21 +17,24 @@ export interface DsModalProps extends Pick<
|
|
|
17
17
|
* Whether the modal is open
|
|
18
18
|
*/
|
|
19
19
|
open: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Callback when modal open state changes
|
|
22
|
-
* @param open - whether the modal is open
|
|
23
|
-
*/
|
|
24
|
-
onOpenChange: (open: boolean) => void;
|
|
25
20
|
|
|
26
21
|
/**
|
|
27
22
|
* Number of grid columns for modal width
|
|
28
23
|
* @default 6
|
|
29
24
|
*/
|
|
30
25
|
columns?: DsModalColumns;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Show full-width dividers between header, body, and footer sections
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
dividers?: boolean;
|
|
32
|
+
|
|
31
33
|
/**
|
|
32
34
|
* Optional inline styles to apply to the component
|
|
33
35
|
*/
|
|
34
36
|
style?: CSSProperties;
|
|
37
|
+
|
|
35
38
|
/**
|
|
36
39
|
* Additional CSS class name
|
|
37
40
|
*/
|
|
@@ -41,4 +44,10 @@ export interface DsModalProps extends Pick<
|
|
|
41
44
|
* Modal body content
|
|
42
45
|
*/
|
|
43
46
|
children: ReactNode;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Callback when modal open state changes
|
|
50
|
+
* @param open - whether the modal is open
|
|
51
|
+
*/
|
|
52
|
+
onOpenChange: (open: boolean) => void;
|
|
44
53
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { useTableFilters } from './use-table-filters';
|
|
2
|
-
export type { UseTableFiltersResult } from './use-table-filters';
|
|
2
|
+
export type { UseTableFiltersOptions, UseTableFiltersResult } from './use-table-filters';
|
|
@@ -9,24 +9,49 @@ import type {
|
|
|
9
9
|
FilterState,
|
|
10
10
|
} from '../types/filter-adapter.types';
|
|
11
11
|
|
|
12
|
+
export interface UseTableFiltersOptions<TData, TValue, TCellValue> {
|
|
13
|
+
/**
|
|
14
|
+
* Array of filter adapters that define filter behavior
|
|
15
|
+
*/
|
|
16
|
+
filterAdapters: FilterAdapter<TData, TValue, TCellValue>[] | AnyAdapter[];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Base column definitions
|
|
20
|
+
*/
|
|
21
|
+
baseColumns?: ColumnDef<TData>[];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Controlled mode: External applied filters (source of truth).
|
|
25
|
+
* When provided with onFiltersChange, the hook operates in controlled mode.
|
|
26
|
+
* Keys must match adapter IDs.
|
|
27
|
+
*/
|
|
28
|
+
appliedFilters?: FilterState<TValue>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Controlled mode: Callback when filters change.
|
|
32
|
+
* Called by applyFilters(), deleteChip(), clearAll().
|
|
33
|
+
*/
|
|
34
|
+
onFiltersChange?: (filters: FilterState<TValue>) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
12
37
|
export interface UseTableFiltersResult<TData, TValue> {
|
|
13
38
|
/**
|
|
14
|
-
* Current filter state
|
|
39
|
+
* Current draft filter state (uncommitted user selections)
|
|
15
40
|
*/
|
|
16
41
|
filterState: FilterState<TValue>;
|
|
17
42
|
|
|
18
43
|
/**
|
|
19
|
-
* Column filters
|
|
44
|
+
* Column filters (derived from applied state)
|
|
20
45
|
*/
|
|
21
46
|
columnFilters: ColumnFilterState<TValue>[];
|
|
22
47
|
|
|
23
48
|
/**
|
|
24
|
-
* Filter chips for display
|
|
49
|
+
* Filter chips for display (derived from applied state)
|
|
25
50
|
*/
|
|
26
51
|
filterChips: ChipItem[];
|
|
27
52
|
|
|
28
53
|
/**
|
|
29
|
-
* Filter navigation items with counts
|
|
54
|
+
* Filter navigation items with active counts
|
|
30
55
|
*/
|
|
31
56
|
filterNavItems: FilterNavItem[];
|
|
32
57
|
|
|
@@ -36,132 +61,128 @@ export interface UseTableFiltersResult<TData, TValue> {
|
|
|
36
61
|
enhancedColumns: ColumnDef<TData>[];
|
|
37
62
|
|
|
38
63
|
/**
|
|
39
|
-
* Handlers
|
|
64
|
+
* Handlers for filter operations
|
|
40
65
|
*/
|
|
41
66
|
handlers: {
|
|
42
|
-
/**
|
|
43
|
-
* Update a specific filter's value
|
|
44
|
-
*/
|
|
67
|
+
/** Update a specific filter's draft value */
|
|
45
68
|
updateFilter: (filterId: string, value: TValue) => void;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Apply all filters (converts to TanStack format)
|
|
49
|
-
*/
|
|
69
|
+
/** Commit draft state to applied state */
|
|
50
70
|
applyFilters: () => void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Clear all filters
|
|
54
|
-
*/
|
|
71
|
+
/** Clear all filters */
|
|
55
72
|
clearAll: () => void;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Delete a specific chip
|
|
59
|
-
*/
|
|
73
|
+
/** Delete a specific filter chip */
|
|
60
74
|
deleteChip: (chip: ChipItem) => void;
|
|
61
75
|
};
|
|
62
76
|
|
|
63
77
|
/**
|
|
64
|
-
* Render function for filter
|
|
78
|
+
* Render function for filter content
|
|
65
79
|
*/
|
|
66
80
|
renderFilterContent: (selectedFilter: FilterNavItem) => ReactNode;
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
/**
|
|
70
|
-
* Hook to orchestrate table filtering with adapters
|
|
71
|
-
* Manages filter state, generates chips, handles column definitions
|
|
84
|
+
* Hook to orchestrate table filtering with adapters.
|
|
85
|
+
* Manages filter state, generates chips, and handles column definitions.
|
|
86
|
+
*
|
|
87
|
+
* **Uncontrolled mode** (default): Filters are managed internally.
|
|
88
|
+
* **Controlled mode**: Filters are managed externally via appliedFilters/onFiltersChange.
|
|
72
89
|
*
|
|
73
|
-
* @
|
|
74
|
-
*
|
|
75
|
-
*
|
|
90
|
+
* @example
|
|
91
|
+
* // Uncontrolled mode
|
|
92
|
+
* const { filterChips, handlers } = useTableFilters({
|
|
93
|
+
* filterAdapters: adapters,
|
|
94
|
+
* baseColumns: columns,
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // Controlled mode (e.g., URL-driven filtering)
|
|
99
|
+
* const { filterChips, handlers } = useTableFilters({
|
|
100
|
+
* filterAdapters: adapters,
|
|
101
|
+
* baseColumns: columns,
|
|
102
|
+
* appliedFilters: filtersFromUrl,
|
|
103
|
+
* onFiltersChange: (filters) => updateUrlAndRefetch(filters),
|
|
104
|
+
* });
|
|
76
105
|
*/
|
|
77
|
-
export function useTableFilters<TData, TValue, TCellValue>(
|
|
78
|
-
filterAdapters
|
|
79
|
-
baseColumns
|
|
80
|
-
|
|
106
|
+
export function useTableFilters<TData, TValue, TCellValue>({
|
|
107
|
+
filterAdapters,
|
|
108
|
+
baseColumns,
|
|
109
|
+
appliedFilters: externalAppliedFilters,
|
|
110
|
+
onFiltersChange: setExternalAppliedFilters,
|
|
111
|
+
}: UseTableFiltersOptions<TData, TValue, TCellValue>): UseTableFiltersResult<TData, TValue> {
|
|
81
112
|
// Internal variable just to avoid assignment issues of `any`.
|
|
82
113
|
const _filterAdapters = filterAdapters as FilterAdapter<TData, TValue, TCellValue>[];
|
|
83
114
|
|
|
84
|
-
|
|
85
|
-
const initialState: FilterState<TValue> = {};
|
|
86
|
-
_filterAdapters.forEach((adapter) => {
|
|
87
|
-
initialState[adapter.id] = adapter.initialValue;
|
|
88
|
-
});
|
|
115
|
+
const isControlled = externalAppliedFilters !== undefined && setExternalAppliedFilters !== undefined;
|
|
89
116
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
117
|
+
const initialFilters = _filterAdapters.reduce<FilterState<TValue>>(
|
|
118
|
+
(state, adapter) => ({ ...state, [adapter.id]: adapter.initialValue }),
|
|
119
|
+
{},
|
|
120
|
+
);
|
|
93
121
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
122
|
+
const [internalAppliedFilters, setInternalAppliedFilters] = useState<FilterState<TValue>>({});
|
|
123
|
+
const [pendingFilters, setPendingFilters] = useState<FilterState<TValue>>({});
|
|
124
|
+
|
|
125
|
+
const appliedFilters = isControlled ? externalAppliedFilters : internalAppliedFilters;
|
|
126
|
+
const setAppliedFilters = isControlled ? setExternalAppliedFilters : setInternalAppliedFilters;
|
|
127
|
+
|
|
128
|
+
const draftFilters = { ...initialFilters, ...appliedFilters, ...pendingFilters };
|
|
129
|
+
|
|
130
|
+
const columnFilters: ColumnFilterState<TValue>[] = Object.entries(appliedFilters)
|
|
131
|
+
.filter(([id]) => {
|
|
132
|
+
const adapter = _filterAdapters.find((a) => a.id === id);
|
|
133
|
+
return adapter ? adapter.getActiveFiltersCount(appliedFilters[id] as TValue) > 0 : false;
|
|
134
|
+
})
|
|
135
|
+
.map(([id, value]) => ({ id, value }));
|
|
136
|
+
|
|
137
|
+
const filterChips = _filterAdapters.flatMap((adapter) => {
|
|
138
|
+
const value = appliedFilters[adapter.id];
|
|
139
|
+
return value !== undefined ? adapter.toChips(value) : [];
|
|
140
|
+
});
|
|
98
141
|
|
|
99
|
-
// Generate filter nav items with counts (updates as user changes filters in modal)
|
|
100
142
|
const filterNavItems: FilterNavItem[] = _filterAdapters.map((adapter) => ({
|
|
101
143
|
id: adapter.id,
|
|
102
144
|
label: adapter.label,
|
|
103
|
-
count: adapter.getActiveFiltersCount(
|
|
145
|
+
count: adapter.getActiveFiltersCount(draftFilters[adapter.id] as TValue),
|
|
104
146
|
}));
|
|
105
147
|
|
|
106
|
-
// Enhance column definitions with filter functions and renderers
|
|
107
148
|
const enhancedColumns: ColumnDef<TData>[] = !baseColumns
|
|
108
149
|
? []
|
|
109
150
|
: baseColumns.map((col) => {
|
|
110
151
|
const adapter = _filterAdapters.find((a) => a.id === col.id);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const cellRenderer = adapter.cellRenderer;
|
|
114
|
-
return {
|
|
115
|
-
...col,
|
|
116
|
-
filterFn: adapter.columnFilterFn,
|
|
117
|
-
...(cellRenderer && {
|
|
118
|
-
cell: (info: CellContext<TData, TCellValue>) => cellRenderer(info.getValue()),
|
|
119
|
-
}),
|
|
120
|
-
};
|
|
152
|
+
if (!adapter) {
|
|
153
|
+
return col;
|
|
121
154
|
}
|
|
122
155
|
|
|
123
|
-
|
|
156
|
+
const cellRenderer = adapter.cellRenderer;
|
|
157
|
+
return {
|
|
158
|
+
...col,
|
|
159
|
+
filterFn: adapter.columnFilterFn,
|
|
160
|
+
...(cellRenderer && {
|
|
161
|
+
cell: (info: CellContext<TData, TCellValue>) => cellRenderer(info.getValue()),
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
124
164
|
});
|
|
125
165
|
|
|
126
|
-
// Handlers
|
|
127
166
|
const updateFilter = (filterId: string, value: TValue) => {
|
|
128
|
-
|
|
129
|
-
...prev,
|
|
130
|
-
[filterId]: value,
|
|
131
|
-
}));
|
|
167
|
+
setPendingFilters((prev) => ({ ...prev, [filterId]: value }));
|
|
132
168
|
};
|
|
133
169
|
|
|
134
170
|
const applyFilters = () => {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
_filterAdapters.forEach((adapter) => {
|
|
139
|
-
const value = getValueByAdapterId(adapter.id);
|
|
140
|
-
|
|
171
|
+
const filtersToApply = _filterAdapters.reduce<FilterState<TValue>>((acc, adapter) => {
|
|
172
|
+
const value = draftFilters[adapter.id] as TValue;
|
|
141
173
|
if (adapter.getActiveFiltersCount(value) > 0) {
|
|
142
|
-
|
|
143
|
-
id: adapter.id,
|
|
144
|
-
value,
|
|
145
|
-
});
|
|
174
|
+
acc[adapter.id] = value;
|
|
146
175
|
}
|
|
176
|
+
return acc;
|
|
177
|
+
}, {});
|
|
147
178
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
chips.push(...adapterChips);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
setColumnFilters(filters);
|
|
154
|
-
setFilterChips(chips);
|
|
179
|
+
setAppliedFilters(filtersToApply);
|
|
180
|
+
setPendingFilters({});
|
|
155
181
|
};
|
|
156
182
|
|
|
157
183
|
const clearAll = () => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
resetState[adapter.id] = adapter.reset();
|
|
161
|
-
});
|
|
162
|
-
setFilterState(resetState);
|
|
163
|
-
setColumnFilters([]);
|
|
164
|
-
setFilterChips([]);
|
|
184
|
+
setPendingFilters({});
|
|
185
|
+
setAppliedFilters({});
|
|
165
186
|
};
|
|
166
187
|
|
|
167
188
|
const deleteChip = (chip: ChipItem) => {
|
|
@@ -175,39 +196,19 @@ export function useTableFilters<TData, TValue, TCellValue>(
|
|
|
175
196
|
return;
|
|
176
197
|
}
|
|
177
198
|
|
|
178
|
-
const currentValue =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// Regenerate chips to check if this was the last one
|
|
182
|
-
const chips: ChipItem[] = [];
|
|
183
|
-
_filterAdapters.forEach((adapter) => {
|
|
184
|
-
const value = adapter.id === filterKey ? newValue : getValueByAdapterId(adapter.id);
|
|
185
|
-
const adapterChips = adapter.toChips(value);
|
|
186
|
-
chips.push(...adapterChips);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// If no chips left, clear all filters
|
|
190
|
-
if (chips.length === 0) {
|
|
191
|
-
clearAll();
|
|
199
|
+
const currentValue = appliedFilters[filterKey];
|
|
200
|
+
if (currentValue === undefined) {
|
|
192
201
|
return;
|
|
193
202
|
}
|
|
194
203
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// Update column filters
|
|
202
|
-
if (adapter.getActiveFiltersCount(newValue) === 0) {
|
|
203
|
-
setColumnFilters((prev) => prev.filter((cf) => cf.id !== filterKey));
|
|
204
|
-
} else {
|
|
205
|
-
setColumnFilters((prev) =>
|
|
206
|
-
prev.map((cf) => (cf.id === filterKey ? { id: filterKey, value: newValue } : cf)),
|
|
207
|
-
);
|
|
208
|
-
}
|
|
204
|
+
const newValue = adapter.fromChip(chip, currentValue);
|
|
205
|
+
|
|
206
|
+
const newFilters =
|
|
207
|
+
adapter.getActiveFiltersCount(newValue) === 0
|
|
208
|
+
? Object.fromEntries(Object.entries(appliedFilters).filter(([key]) => key !== filterKey))
|
|
209
|
+
: { ...appliedFilters, [filterKey]: newValue };
|
|
209
210
|
|
|
210
|
-
|
|
211
|
+
setAppliedFilters(newFilters);
|
|
211
212
|
};
|
|
212
213
|
|
|
213
214
|
const renderFilterContent = (selectedFilter: FilterNavItem) => {
|
|
@@ -216,14 +217,14 @@ export function useTableFilters<TData, TValue, TCellValue>(
|
|
|
216
217
|
return null;
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
const value =
|
|
220
|
+
const value = draftFilters[adapter.id] as TValue;
|
|
220
221
|
const onChange = (newValue: TValue) => updateFilter(adapter.id, newValue);
|
|
221
222
|
|
|
222
223
|
return adapter.renderFilter(value, onChange);
|
|
223
224
|
};
|
|
224
225
|
|
|
225
226
|
return {
|
|
226
|
-
filterState,
|
|
227
|
+
filterState: draftFilters,
|
|
227
228
|
columnFilters,
|
|
228
229
|
filterChips,
|
|
229
230
|
filterNavItems,
|