@@ -11,8 +11,10 @@ import { Constraints } from 'lib/Filters';
1111import DateTimeEntry from 'components/DateTimeEntry/DateTimeEntry.react' ;
1212import Icon from 'components/Icon/Icon.react' ;
1313import Parse from 'parse' ;
14+ import Popover from 'components/Popover/Popover.react' ;
15+ import Position from 'lib/Position' ;
1416import PropTypes from 'lib/PropTypes' ;
15- import React , { useCallback } from 'react' ;
17+ import React , { useCallback , useState , useRef } from 'react' ;
1618import styles from 'components/BrowserFilter/BrowserFilter.scss' ;
1719import validateNumeric from 'lib/validateNumeric' ;
1820
@@ -21,6 +23,153 @@ for (const c in Constraints) {
2123 constraintLookup [ Constraints [ c ] . name ] = c ;
2224}
2325
26+ const RegexOptionsButton = ( { modifiers, onChangeModifiers } ) => {
27+ const [ showOptions , setShowOptions ] = useState ( false ) ;
28+ const buttonRef = useRef ( null ) ;
29+ const dropdownRef = useRef ( null ) ;
30+
31+ // Parse modifiers string into individual flags
32+ const modifiersArray = modifiers ? modifiers . split ( '' ) : [ ] ;
33+ const hasI = modifiersArray . includes ( 'i' ) ;
34+ const hasU = modifiersArray . includes ( 'u' ) ;
35+ const hasM = modifiersArray . includes ( 'm' ) ;
36+ const hasX = modifiersArray . includes ( 'x' ) ;
37+ const hasS = modifiersArray . includes ( 's' ) ;
38+
39+ const toggleModifier = ( modifier ) => {
40+ let newModifiers = [ ...modifiersArray ] ;
41+ if ( newModifiers . includes ( modifier ) ) {
42+ newModifiers = newModifiers . filter ( m => m !== modifier ) ;
43+ } else {
44+ newModifiers . push ( modifier ) ;
45+ }
46+ onChangeModifiers ( newModifiers . join ( '' ) ) ;
47+ } ;
48+
49+ React . useEffect ( ( ) => {
50+ const handleClickOutside = ( event ) => {
51+ if (
52+ dropdownRef . current &&
53+ ! dropdownRef . current . contains ( event . target ) &&
54+ buttonRef . current &&
55+ ! buttonRef . current . contains ( event . target )
56+ ) {
57+ setShowOptions ( false ) ;
58+ }
59+ } ;
60+
61+ if ( showOptions ) {
62+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
63+ return ( ) => {
64+ document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
65+ } ;
66+ }
67+ } , [ showOptions ] ) ;
68+
69+ const optionsDropdown = showOptions ? (
70+ < Popover
71+ fixed = { true }
72+ position = { Position . inDocument ( buttonRef . current ) }
73+ data-popover-type = "inner"
74+ >
75+ < div
76+ ref = { dropdownRef }
77+ style = { {
78+ background : '#1e1e2e' ,
79+ border : '1px solid #66637A' ,
80+ borderRadius : '5px' ,
81+ padding : '8px' ,
82+ minWidth : '150px' ,
83+ color : 'white' ,
84+ fontSize : '14px' ,
85+ boxShadow : '0 4px 6px rgba(0, 0, 0, 0.3)'
86+ } }
87+ >
88+ < div style = { { marginBottom : '4px' , fontWeight : 'bold' , paddingBottom : '4px' , borderBottom : '1px solid rgba(255,255,255,0.2)' } } >
89+ Regex Options
90+ </ div >
91+ < label
92+ style = { { display : 'flex' , alignItems : 'center' , padding : '4px 0' , cursor : 'pointer' } }
93+ onClick = { ( ) => toggleModifier ( 'i' ) }
94+ >
95+ < input
96+ type = "checkbox"
97+ checked = { hasI }
98+ readOnly
99+ style = { { marginRight : '8px' , cursor : 'pointer' } }
100+ />
101+ < span > Case insensitive (i)</ span >
102+ </ label >
103+ < label
104+ style = { { display : 'flex' , alignItems : 'center' , padding : '4px 0' , cursor : 'pointer' } }
105+ onClick = { ( ) => toggleModifier ( 'u' ) }
106+ >
107+ < input
108+ type = "checkbox"
109+ checked = { hasU }
110+ readOnly
111+ style = { { marginRight : '8px' , cursor : 'pointer' } }
112+ />
113+ < span > Unicode (u)</ span >
114+ </ label >
115+ < label
116+ style = { { display : 'flex' , alignItems : 'center' , padding : '4px 0' , cursor : 'pointer' } }
117+ onClick = { ( ) => toggleModifier ( 'm' ) }
118+ >
119+ < input
120+ type = "checkbox"
121+ checked = { hasM }
122+ readOnly
123+ style = { { marginRight : '8px' , cursor : 'pointer' } }
124+ />
125+ < span > Multiline (m)</ span >
126+ </ label >
127+ < label
128+ style = { { display : 'flex' , alignItems : 'center' , padding : '4px 0' , cursor : 'pointer' } }
129+ onClick = { ( ) => toggleModifier ( 'x' ) }
130+ >
131+ < input
132+ type = "checkbox"
133+ checked = { hasX }
134+ readOnly
135+ style = { { marginRight : '8px' , cursor : 'pointer' } }
136+ />
137+ < span > Extended (x)</ span >
138+ </ label >
139+ < label
140+ style = { { display : 'flex' , alignItems : 'center' , padding : '4px 0' , cursor : 'pointer' } }
141+ onClick = { ( ) => toggleModifier ( 's' ) }
142+ >
143+ < input
144+ type = "checkbox"
145+ checked = { hasS }
146+ readOnly
147+ style = { { marginRight : '8px' , cursor : 'pointer' } }
148+ />
149+ < span > Dotall (s)</ span >
150+ </ label >
151+ </ div >
152+ </ Popover >
153+ ) : null ;
154+
155+ return (
156+ < >
157+ < button
158+ ref = { buttonRef }
159+ type = "button"
160+ className = { styles . remove }
161+ onClick = { ( ) => {
162+ setShowOptions ( ! showOptions ) ;
163+ } }
164+ title = "Regex options"
165+ >
166+ < Icon name = "gear-solid" width = { 14 } height = { 14 } fill = "rgba(0,0,0,0.4)" />
167+ </ button >
168+ { optionsDropdown }
169+ </ >
170+ ) ;
171+ } ;
172+
24173function compareValue (
25174 info ,
26175 value ,
@@ -29,7 +178,9 @@ function compareValue(
29178 active ,
30179 parentContentId ,
31180 setFocus ,
32- currentConstraint
181+ currentConstraint ,
182+ modifiers ,
183+ onChangeModifiers
33184) {
34185 if ( currentConstraint === 'containedIn' ) {
35186 return (
@@ -60,6 +211,21 @@ function compareValue(
60211 return null ;
61212 case 'Object' :
62213 case 'String' :
214+ if ( currentConstraint === 'matches' ) {
215+ return (
216+ < div style = { { display : 'flex' , alignItems : 'center' , gap : '4px' } } >
217+ < input
218+ type = "text"
219+ value = { value }
220+ onChange = { e => onChangeCompareTo ( e . target . value ) }
221+ onKeyDown = { onKeyDown }
222+ ref = { setFocus }
223+ style = { { width : '106px' } }
224+ />
225+ < RegexOptionsButton modifiers = { modifiers } onChangeModifiers = { onChangeModifiers } />
226+ </ div >
227+ ) ;
228+ }
63229 return (
64230 < input
65231 type = "text"
@@ -85,6 +251,7 @@ function compareValue(
85251 case 'Boolean' :
86252 return (
87253 < ChromeDropdown
254+ width = "140"
88255 color = { active ? 'blue' : 'purple' }
89256 value = { value ? 'True' : 'False' }
90257 options = { [ 'True' , 'False' ] }
@@ -131,10 +298,12 @@ const FilterRow = ({
131298 currentField,
132299 currentConstraint,
133300 compareTo,
301+ modifiers,
134302 onChangeClass,
135303 onChangeField,
136304 onChangeConstraint,
137305 onChangeCompareTo,
306+ onChangeModifiers,
138307 onKeyDown,
139308 onDeleteRow,
140309 active,
@@ -234,13 +403,14 @@ const FilterRow = ({
234403 buildSuggestions = { buildFieldSuggestions }
235404 buildLabel = { ( ) => '' }
236405 />
237- < ChromeDropdown
238- width = { compareInfo . type ? '175' : '325' }
239- color = { active ? 'blue' : 'purple' }
240- value = { Constraints [ currentConstraint ] . name }
241- options = { constraints . map ( c => Constraints [ c ] . name ) }
242- onChange = { c => onChangeConstraint ( constraintLookup [ c ] , compareTo ) }
243- />
406+ < div style = { { flex : 1 } } >
407+ < ChromeDropdown
408+ color = { active ? 'blue' : 'purple' }
409+ value = { Constraints [ currentConstraint ] . name }
410+ options = { constraints . map ( c => Constraints [ c ] . name ) }
411+ onChange = { c => onChangeConstraint ( constraintLookup [ c ] , compareTo ) }
412+ />
413+ </ div >
244414 { compareValue (
245415 compareInfo ,
246416 compareTo ,
@@ -249,7 +419,9 @@ const FilterRow = ({
249419 active ,
250420 parentContentId ,
251421 setFocus ,
252- currentConstraint
422+ currentConstraint ,
423+ modifiers ,
424+ onChangeModifiers
253425 ) }
254426 < button type = "button" className = { styles . remove } onClick = { onDeleteRow } >
255427 < Icon name = "minus-solid" width = { 14 } height = { 14 } fill = "rgba(0,0,0,0.4)" />
@@ -267,4 +439,6 @@ FilterRow.propTypes = {
267439 currentConstraint : PropTypes . string . isRequired ,
268440 compareTo : PropTypes . any ,
269441 compareInfo : PropTypes . object ,
442+ modifiers : PropTypes . string ,
443+ onChangeModifiers : PropTypes . func ,
270444} ;
0 commit comments