11import React , { PureComponent } from 'react' ;
22import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
3- import { vsSearch } from '@deephaven/icons' ;
3+ import { vsArrowLeft , vsArrowRight , vsSearch } from '@deephaven/icons' ;
44import classNames from 'classnames' ;
5+ import Button from './Button' ;
56import './SearchInput.scss' ;
7+ import { GLOBAL_SHORTCUTS } from './shortcuts' ;
8+ import { ContextActions } from './context-actions' ;
69
710interface SearchInputProps {
811 value : string ;
@@ -15,6 +18,10 @@ interface SearchInputProps {
1518 matchCount : number ;
1619 id : string ;
1720 'data-testid' ?: string ;
21+ cursor ?: {
22+ index : number | undefined ;
23+ next : ( direction : 'forward' | 'back' ) => void ;
24+ } ;
1825}
1926
2027class SearchInput extends PureComponent < SearchInputProps > {
@@ -27,19 +34,40 @@ class SearchInput extends PureComponent<SearchInputProps> {
2734 } ,
2835 id : '' ,
2936 'data-testid' : undefined ,
37+ cursor : undefined ,
3038 } ;
3139
3240 constructor ( props : SearchInputProps ) {
3341 super ( props ) ;
3442 this . inputField = React . createRef ( ) ;
43+ this . searchChangeSelection = React . createRef ( ) ;
3544 }
3645
37- inputField : React . RefObject < HTMLInputElement > ;
46+ componentDidMount ( ) : void {
47+ this . setInputPaddingRight ( ) ;
48+ }
49+
50+ componentDidUpdate ( ) : void {
51+ this . setInputPaddingRight ( ) ;
52+ }
3853
3954 focus ( ) : void {
4055 this . inputField . current ?. focus ( ) ;
4156 }
4257
58+ inputField : React . RefObject < HTMLInputElement > ;
59+
60+ searchChangeSelection : React . RefObject < HTMLDivElement > ;
61+
62+ setInputPaddingRight ( ) : void {
63+ const inputField = this . inputField . current ;
64+ const searchChangeSelection = this . searchChangeSelection . current ;
65+ if ( inputField && searchChangeSelection ) {
66+ const paddingRight = searchChangeSelection . getBoundingClientRect ( ) . width ;
67+ inputField . style . paddingRight = `${ paddingRight } px` ;
68+ }
69+ }
70+
4371 render ( ) : JSX . Element {
4472 const {
4573 value,
@@ -52,7 +80,56 @@ class SearchInput extends PureComponent<SearchInputProps> {
5280 id,
5381 onKeyDown,
5482 'data-testid' : dataTestId ,
83+ cursor,
5584 } = this . props ;
85+
86+ let matchCountSection ;
87+ const contextActions = [
88+ {
89+ action : ( ) => cursor ?. next ( 'forward' ) ,
90+ shortcut : GLOBAL_SHORTCUTS . NEXT ,
91+ } ,
92+ {
93+ action : ( ) => cursor ?. next ( 'back' ) ,
94+ shortcut : GLOBAL_SHORTCUTS . PREVIOUS ,
95+ } ,
96+ ] ;
97+
98+ if ( cursor && matchCount > 1 ) {
99+ matchCountSection = (
100+ < >
101+ < Button
102+ kind = "ghost"
103+ className = "search-change-button"
104+ type = "button"
105+ onClick = { ( ) => {
106+ cursor . next ( 'back' ) ;
107+ } }
108+ icon = { vsArrowLeft }
109+ tooltip = { `Previous match (${ GLOBAL_SHORTCUTS . PREVIOUS . getDisplayText ( ) } )` }
110+ />
111+ < span className = "search-change-text" >
112+ { cursor . index !== undefined && `${ cursor . index + 1 } of ` }
113+ { matchCount }
114+ </ span >
115+ < Button
116+ kind = "ghost"
117+ className = "search-change-button"
118+ type = "button"
119+ onClick = { ( ) => {
120+ cursor . next ( 'forward' ) ;
121+ } }
122+ icon = { vsArrowRight }
123+ tooltip = { `Next match (${ GLOBAL_SHORTCUTS . NEXT . getDisplayText ( ) } )` }
124+ />
125+ </ >
126+ ) ;
127+ } else {
128+ matchCountSection = matchCount > 0 && (
129+ < span className = "search-match" > { matchCount } </ span >
130+ ) ;
131+ }
132+
56133 return (
57134 < div className = { classNames ( 'search-group' , className ) } >
58135 < input
@@ -68,12 +145,22 @@ class SearchInput extends PureComponent<SearchInputProps> {
68145 id = { id }
69146 data-testid = { dataTestId }
70147 />
71- { matchCount != null && (
72- < span className = "search-match" > { matchCount } </ span >
148+
149+ { matchCount != null ? (
150+ < >
151+ < div
152+ className = "search-change-selection"
153+ ref = { this . searchChangeSelection }
154+ >
155+ { matchCountSection }
156+ </ div >
157+ < ContextActions actions = { contextActions } />
158+ </ >
159+ ) : (
160+ < span className = "search-icon" >
161+ < FontAwesomeIcon icon = { vsSearch } />
162+ </ span >
73163 ) }
74- < span className = "search-icon" >
75- < FontAwesomeIcon icon = { vsSearch } />
76- </ span >
77164 </ div >
78165 ) ;
79166 }
0 commit comments