@@ -5,6 +5,7 @@ import { ActionButton, AnimatedButton } from "../../components/button";
5
5
import { createDropdownItem , Dropdown , IDropdownItem } from "../../components/dropdown" ;
6
6
import { Icons } from "../../components/icons" ;
7
7
import { Input , Label } from "../../components/input" ;
8
+ import { twMerge } from "tailwind-merge" ;
8
9
9
10
export type IExploreStorageUnitWhereConditionFilter = {
10
11
field : string ;
@@ -13,58 +14,88 @@ export type IExploreStorageUnitWhereConditionFilter = {
13
14
}
14
15
15
16
type IExploreStorageUnitWhereConditionProps = {
17
+ defaultFilters ?: IExploreStorageUnitWhereConditionFilter [ ] ;
16
18
options : string [ ] ;
17
19
operators : string [ ] ;
18
20
onChange ?: ( filters : IExploreStorageUnitWhereConditionFilter [ ] ) => void ;
19
21
}
20
22
21
- export const ExploreStorageUnitWhereCondition : FC < IExploreStorageUnitWhereConditionProps > = ( { options, onChange, operators } ) => {
22
- const [ newFilter , setNewFilter ] = useState < IExploreStorageUnitWhereConditionFilter > ( { field : options [ 0 ] , operator : operators [ 0 ] , value : "" } ) ;
23
+ export const ExploreStorageUnitWhereCondition : FC < IExploreStorageUnitWhereConditionProps > = ( { defaultFilters , options, onChange, operators } ) => {
24
+ const [ currentFilter , setCurrentFilter ] = useState < IExploreStorageUnitWhereConditionFilter > ( { field : options [ 0 ] , operator : operators [ 0 ] , value : "" } ) ;
23
25
const [ filters , setFitlers ] = useState < IExploreStorageUnitWhereConditionFilter [ ] > ( [ ] ) ;
24
- const [ show , setShow ] = useState ( false ) ;
26
+ const [ newFilter , setNewFilter ] = useState ( false ) ;
27
+ const [ editingFilter , setEditingFilter ] = useState ( - 1 ) ;
25
28
26
29
const handleClick = useCallback ( ( ) => {
27
- setShow ( s => ! s ) ;
28
- } , [ ] ) ;
30
+ const shouldShow = ! newFilter ;
31
+ if ( shouldShow ) {
32
+ setEditingFilter ( - 1 ) ;
33
+ setCurrentFilter ( { field : currentFilter . field , operator : operators [ 0 ] , value : "" } ) ;
34
+ }
35
+ setNewFilter ( ! newFilter ) ;
36
+ } , [ currentFilter . field , operators , newFilter ] ) ;
29
37
30
38
const fieldsDropdownItems = useMemo ( ( ) => {
31
39
return options . map ( option => createDropdownItem ( option ) ) ;
32
40
} , [ options ] ) ;
33
41
34
42
const handleFieldSelect = useCallback ( ( item : IDropdownItem ) => {
35
- setNewFilter ( val => ( {
43
+ setCurrentFilter ( val => ( {
36
44
...val ,
37
45
field : item . id ,
38
46
} ) ) ;
39
47
} , [ ] ) ;
40
48
41
49
const handleOperatorSelector = useCallback ( ( item : IDropdownItem ) => {
42
- setNewFilter ( val => ( {
50
+ setCurrentFilter ( val => ( {
43
51
...val ,
44
52
operator : item . id ,
45
53
} ) ) ;
46
54
} , [ ] ) ;
47
55
48
56
const handleInputChange = useCallback ( ( newValue : string ) => {
49
- setNewFilter ( val => ( {
57
+ setCurrentFilter ( val => ( {
50
58
...val ,
51
59
value : newValue ,
52
60
} ) ) ;
53
61
} , [ ] ) ;
54
62
55
63
const handleAddFilter = useCallback ( ( ) => {
56
- const newFilters = [ ...filters , newFilter ] ;
64
+ const newFilters = [ ...filters , currentFilter ] ;
57
65
setFitlers ( newFilters ) ;
58
- setNewFilter ( { field : newFilter . field , operator : operators [ 0 ] , value : "" } ) ;
66
+ setCurrentFilter ( { field : currentFilter . field , operator : operators [ 0 ] , value : "" } ) ;
59
67
onChange ?.( newFilters ) ;
60
- } , [ filters , newFilter , onChange , operators ] ) ;
68
+ } , [ filters , currentFilter , onChange , operators ] ) ;
61
69
62
70
const handleRemove = useCallback ( ( index : number ) => {
63
- setFitlers ( oldFilters => oldFilters . filter ( ( _ , i ) => i !== index ) ) ;
64
- } , [ ] ) ;
71
+ setEditingFilter ( - 1 ) ;
72
+ const newFilters = filters . filter ( ( _ , i ) => i !== index )
73
+ setFitlers ( newFilters ) ;
74
+ onChange ?.( newFilters ) ;
75
+ } , [ filters , onChange ] ) ;
76
+
77
+ const handleEdit = useCallback ( ( index : number ) => {
78
+ if ( editingFilter === index ) {
79
+ setEditingFilter ( - 1 ) ;
80
+ setCurrentFilter ( { field : currentFilter . field , operator : operators [ 0 ] , value : "" } ) ;
81
+ return ;
82
+ }
83
+ setNewFilter ( false ) ;
84
+ setCurrentFilter ( filters [ index ] ) ;
85
+ setEditingFilter ( index ) ;
86
+ } , [ editingFilter , filters , currentFilter . field , operators ] ) ;
87
+
88
+ const handleSaveFilter = useCallback ( ( index : number ) => {
89
+ const newFilters = [ ...filters ] ;
90
+ newFilters [ index ] = { ...currentFilter } ;
91
+ setFitlers ( newFilters ) ;
92
+ setEditingFilter ( - 1 ) ;
93
+ setCurrentFilter ( { field : currentFilter . field , operator : operators [ 0 ] , value : "" } ) ;
94
+ onChange ?.( newFilters ) ;
95
+ } , [ currentFilter , filters , onChange , operators ] ) ;
65
96
66
97
useEffect ( ( ) => {
67
- setNewFilter ( f => ( {
98
+ setCurrentFilter ( f => ( {
68
99
...f ,
69
100
operator : operators [ 0 ] ,
70
101
} ) ) ;
@@ -73,28 +104,57 @@ export const ExploreStorageUnitWhereCondition: FC<IExploreStorageUnitWhereCondit
73
104
const validOperators = useMemo ( ( ) => {
74
105
return operators . map ( operator => createDropdownItem ( operator ) ) ;
75
106
} , [ operators ] ) ;
76
-
107
+
108
+ useEffect ( ( ) => {
109
+ setFitlers ( defaultFilters ?? [ ] ) ;
110
+ } , [ defaultFilters ] ) ;
111
+
77
112
return < div className = "flex flex-col gap-1 h-full relative" >
78
113
< Label label = "Where condition" />
79
114
< div className = "flex gap-1 items-center max-w-[min(500px,calc(100vw-20px))] flex-wrap" >
80
115
{
81
116
filters . map ( ( filter , i ) => (
82
- < div className = "group/filter-item flex gap-1 items-center text-xs px-2 py-1 rounded-2xl dark:bg-white/5 cursor-pointer relative overflow-hidden shadow-sm border border-neutral-100 dark:border-neutral-800"
83
- onClick = { ( ) => handleRemove ( i ) } >
84
- < div className = "max-w-[350px] truncate dark:text-neutral-300" >
117
+ < div key = { `explore-storage-unit-filter-${ i } ` } className = "group/filter-item flex gap-1 items-center text-xs rounded-2xl dark:bg-white/5 cursor-pointer relative shadow-sm border border-neutral-100 dark:border-neutral-800" >
118
+ < div className = { twMerge ( classNames ( "px-2 py-1 h-full max-w-[350px] truncate dark:text-neutral-300 rounded-2xl" , {
119
+ "dark:bg-white/10" : editingFilter === i ,
120
+ } ) ) } onClick = { ( ) => handleEdit ( i ) } >
85
121
{ filter . field } { filter . operator } { filter . value }
86
122
</ div >
87
- < ActionButton icon = { Icons . Cancel } containerClassName = "hover:scale-125 absolute right-0 top-1/2 -translate-y-1/2 z-10 h-4 w-4 opacity-0 group-hover/filter-item:opacity-100" />
123
+ < ActionButton icon = { Icons . Cancel } containerClassName = "hover:scale-125 absolute right-2 top-1/2 -translate-y-1/2 z-10 h-4 w-4 opacity-0 group-hover/filter-item:opacity-100"
124
+ onClick = { ( ) => handleRemove ( i ) } />
125
+ < AnimatePresence mode = "wait" >
126
+ {
127
+ editingFilter === i &&
128
+ < motion . div className = "flex gap-1 z-[5] py-2 px-4 absolute left-0 top-full mt-2 rounded-lg shadow-md border border-neutral-100 dark:border-white/5 dark:bg-white/20 dark:backdrop-blur-xl translate-y-full bg-white" initial = { {
129
+ y : - 10 ,
130
+ opacity : 0 ,
131
+ } } animate = { {
132
+ y : 0 ,
133
+ opacity : 1 ,
134
+ } } exit = { {
135
+ y : - 10 ,
136
+ opacity : 0 ,
137
+ } } >
138
+ < Dropdown noItemsLabel = "No fields found" className = "min-w-[100px]" value = { createDropdownItem ( currentFilter . field ) } items = { fieldsDropdownItems . length === 0 ? [ createDropdownItem ( currentFilter . field ) ] : fieldsDropdownItems } onChange = { handleFieldSelect } />
139
+ < Dropdown noItemsLabel = "No operators found" className = "min-w-20" value = { createDropdownItem ( currentFilter . operator ) } items = { validOperators } onChange = { handleOperatorSelector } />
140
+ < Input inputProps = { {
141
+ className : "min-w-[150px]"
142
+ } } placeholder = "Enter filter value" value = { currentFilter . value } setValue = { handleInputChange } />
143
+ < AnimatedButton className = "dark:bg-white/5" icon = { Icons . Cancel } label = "Cancel" onClick = { ( ) => handleEdit ( i ) } />
144
+ < AnimatedButton className = "dark:bg-white/5" icon = { Icons . CheckCircle } label = "Save" onClick = { ( ) => handleSaveFilter ( i ) } />
145
+ </ motion . div >
146
+ }
147
+ </ AnimatePresence >
88
148
</ div >
89
149
) )
90
150
}
91
151
< ActionButton className = { classNames ( "transition-all" , {
92
- "rotate-45" : show ,
152
+ "rotate-45" : newFilter ,
93
153
} ) } icon = { Icons . Add } containerClassName = "h-8 w-8" onClick = { handleClick } />
94
154
</ div >
95
155
< AnimatePresence mode = "wait" >
96
156
{
97
- show &&
157
+ newFilter &&
98
158
< motion . div className = "flex gap-1 z-[5] py-2 px-4 absolute top-full mt-1 rounded-lg shadow-md border border-neutral-100 dark:border-white/5 dark:bg-white/20 dark:backdrop-blur-xl translate-y-full bg-white" initial = { {
99
159
y : - 10 ,
100
160
opacity : 0 ,
@@ -105,11 +165,12 @@ export const ExploreStorageUnitWhereCondition: FC<IExploreStorageUnitWhereCondit
105
165
y : - 10 ,
106
166
opacity : 0 ,
107
167
} } >
108
- < Dropdown className = "min-w-[100px]" value = { createDropdownItem ( newFilter . field ) } items = { fieldsDropdownItems } onChange = { handleFieldSelect } />
109
- < Dropdown className = "min-w-20" value = { createDropdownItem ( newFilter . operator ) } items = { validOperators } onChange = { handleOperatorSelector } />
168
+ < Dropdown noItemsLabel = "No fields found" className = "min-w-[100px]" value = { createDropdownItem ( currentFilter . field ) } items = { fieldsDropdownItems } onChange = { handleFieldSelect } />
169
+ < Dropdown noItemsLabel = "No operators found" className = "min-w-20" value = { createDropdownItem ( currentFilter . operator ) } items = { validOperators } onChange = { handleOperatorSelector } />
110
170
< Input inputProps = { {
111
- className : "min-w-[150px]"
112
- } } placeholder = "Enter filter value" value = { newFilter . value } setValue = { handleInputChange } />
171
+ className : "min-w-[150px]" ,
172
+ } } placeholder = "Enter filter value" value = { currentFilter . value } setValue = { handleInputChange } />
173
+ < AnimatedButton className = "dark:bg-white/5" icon = { Icons . Cancel } label = "Cancel" onClick = { handleClick } />
113
174
< AnimatedButton className = "dark:bg-white/5" icon = { Icons . CheckCircle } label = "Add" onClick = { handleAddFilter } />
114
175
</ motion . div >
115
176
}
0 commit comments