@@ -10,20 +10,27 @@ import { getContainerIdFromUrl } from "../utils";
10
10
import {
11
11
Button ,
12
12
createStyles ,
13
+ FormControlLabel ,
14
+ Grid ,
15
+ IconButton ,
16
+ Input ,
17
+ InputAdornment ,
13
18
makeStyles ,
14
19
Menu ,
15
20
MenuItem ,
16
- TextField ,
21
+ Switch ,
17
22
Theme
18
23
} from "@material-ui/core" ;
19
- import {
20
- PREFERRED_SOURCES_VIEW_ONLY ,
21
- useAnchor
22
- } from "../../../utils" ;
24
+ import { PREFERRED_SOURCES_VIEW_ONLY , useAnchor } from "../../../utils" ;
23
25
import { APISource } from "../../sources" ;
24
- import { AccountTreeOutlined , FolderOpen } from "@material-ui/icons" ;
25
- import { APIDictionary } from '../../dictionaries/types' ;
26
-
26
+ import {
27
+ AccountTreeOutlined ,
28
+ FolderOpen ,
29
+ Search as SearchIcon
30
+ } from "@material-ui/icons" ;
31
+ import { APIDictionary , Dictionary } from '../../dictionaries/types' ;
32
+ import { VerifiedSource } from "../../../components/VerifiedSource" ;
33
+ import InfiniteScroll from "react-infinite-scroll-component" ;
27
34
interface Props {
28
35
containerType : string ;
29
36
containerUrl ?: string ;
@@ -32,8 +39,14 @@ interface Props {
32
39
children ?: React . ReactNode [ ] ;
33
40
sources : APISource [ ] ;
34
41
dictionaries : APIDictionary [ ] ;
42
+ showOnlyVerified : boolean ;
43
+ toggleShowVerified : React . ChangeEventHandler < HTMLInputElement > ;
44
+ goTo : Function ;
45
+ initialSearch : string ;
46
+ pathUrl : Function ;
47
+ dictionaryMeta ?: { num_found ?: number } ;
48
+ sourcesMeta ?: { num_found ?: number } ;
35
49
}
36
-
37
50
const useStyles = makeStyles ( ( theme : Theme ) =>
38
51
createStyles ( {
39
52
lightColour : {
@@ -43,6 +56,9 @@ const useStyles = makeStyles((theme: Theme) =>
43
56
padding : "0.2rem 1rem" ,
44
57
cursor : "none"
45
58
} ,
59
+ sourcesDropdownHeader : {
60
+ padding : "0.5rem 1rem"
61
+ } ,
46
62
input : {
47
63
cursor : "pointer" ,
48
64
borderBottom : "1px dotted black" ,
@@ -60,30 +76,56 @@ const useStyles = makeStyles((theme: Theme) =>
60
76
marginRight : "0.2rem" ,
61
77
fill : "#8080809c"
62
78
} ,
79
+ searchInput : {
80
+ textAlign : "center" ,
81
+ fontSize : "larger"
82
+ }
63
83
} )
64
84
) ;
65
-
66
85
const ViewConceptsHeader : React . FC < Props > = ( {
67
86
containerType,
68
87
containerUrl,
69
88
gimmeAUrl,
70
89
addConceptToDictionary,
71
90
children,
72
91
sources,
73
- dictionaries
92
+ dictionaries,
93
+ goTo,
94
+ initialSearch,
95
+ pathUrl,
96
+ dictionaryMeta,
97
+ sourcesMeta
74
98
} ) => {
75
- const [ showSources , setShowSources ] = useState ( false ) ;
76
- const [ preferredSources , setPreferredSources ] = useState < { name : string ; url : string } [ ] > ( ) ;
99
+ const [ showAllSources , setShowAllSources ] = useState ( false ) ;
100
+ const [ queryString , setQueryString ] = useState ( initialSearch ) ;
101
+ const [ currentSources , setCurrentSources ] = useState <
102
+ { name : string ; url : string } [ ]
103
+ > ( ) ;
77
104
useEffect ( ( ) => {
78
- const defaultSources = Object . entries ( PREFERRED_SOURCES_VIEW_ONLY ) . map ( ( [ key , value ] ) => ( { name : key , url : value } ) ) ;
79
- if ( showSources ) {
80
- const allSources = defaultSources
105
+ const defaultSources = Object . entries (
106
+ PREFERRED_SOURCES_VIEW_ONLY
107
+ ) . map ( ( [ key , value ] ) => ( { name : key , url : value } ) ) ;
108
+ if ( showAllSources ) {
109
+ const allSources = defaultSources
81
110
. concat ( sources . map ( s => ( { name : s . name , url : s . url } ) ) )
82
111
. concat ( dictionaries . map ( d => ( { name : d . name , url : d . url } ) ) ) ;
83
- setPreferredSources ( allSources ) ;
84
- } else setPreferredSources ( defaultSources ) ;
85
- } , [ showSources , sources , dictionaries ] ) ;
86
-
112
+ setCurrentSources ( allSources ) ;
113
+ console . log ( allSources ) ;
114
+ } else setCurrentSources ( defaultSources ) ;
115
+ } , [ showAllSources , sources , dictionaries , initialSearch ] ) ;
116
+ // TODO - Check if this is correct
117
+ const fetchMoreData = ( ) => {
118
+ setTimeout ( ( ) => {
119
+ const previousSources = currentSources
120
+ ?. filter ( Boolean )
121
+ . concat ( currentSources . slice ( 11 ) ) ;
122
+ setCurrentSources ( previousSources ) ;
123
+ } , 1000 ) ;
124
+ } ;
125
+ const handleSearch = ( q : string ) => goTo ( pathUrl ( { q } ) ) ;
126
+ const handleShowSources = ( event : React . ChangeEvent < HTMLInputElement > ) => {
127
+ setShowAllSources ( event . target . checked ) ;
128
+ } ;
87
129
const classes = useStyles ( ) ;
88
130
const isSourceContainer = containerType === SOURCE_CONTAINER ;
89
131
const isAddToDictionary = isSourceContainer && ! ! addConceptToDictionary ;
@@ -92,14 +134,19 @@ const ViewConceptsHeader: React.FC<Props> = ({
92
134
handleSwitchSourceClick ,
93
135
handleSwitchSourceClose
94
136
] = useAnchor ( ) ;
95
-
96
137
const getTitleBasedOnContainerType = ( ) => {
97
138
return isAddToDictionary
98
139
? `Import existing concept from ${ getContainerIdFromUrl ( containerUrl ) } `
99
140
: `Concepts in ${
100
141
containerType === DICTIONARY_VERSION_CONTAINER ? "v" : ""
101
142
} ${ getContainerIdFromUrl ( containerUrl ) } `;
102
143
} ;
144
+ const dictionaryObj = dictionaryMeta ? dictionaryMeta :{ } ;
145
+ const dictionaryCount = dictionaryObj . num_found ? dictionaryObj . num_found :0 ;
146
+
147
+ const sourceObj = sourcesMeta ? sourcesMeta :{ } ;
148
+ const sourcesCount = sourceObj . num_found ? sourceObj . num_found :0 ;
149
+ const totalCount = sourcesCount + dictionaryCount
103
150
104
151
const showSwitchSourceBasedOnContainerType = ( ) => {
105
152
return ! isAddToDictionary ? null : (
@@ -123,46 +170,99 @@ const ViewConceptsHeader: React.FC<Props> = ({
123
170
} }
124
171
anchorEl = { switchSourceAnchor }
125
172
keepMounted
126
- open = { Boolean ( switchSourceAnchor ) }
173
+ open = { ! ! switchSourceAnchor }
127
174
onClose = { handleSwitchSourceClose }
128
175
>
129
- < TextField
130
- multiline
131
- className = { classes . textField }
132
- InputProps = { {
133
- className : classes . underline
134
- } }
135
- inputProps = { {
136
- className : classes . input
137
- } }
138
- value = {
139
- showSources ? "Choose a source/dictionary" : "Select a different source/dictionary"
140
- }
141
- onClick = { ( ) => setShowSources ( ! showSources ) }
142
- />
143
- { preferredSources ?. map ( ( { name, url } ) => (
144
- < MenuItem
145
- // replace because we want to keep the back button useful
146
- replace
147
- to = { gimmeAUrl ( { } , `${ url } concepts/` ) }
148
- key = { name }
149
- component = { Link }
150
- onClick = { handleSwitchSourceClose }
151
- data-testid = { name }
176
+ < Grid container direction = "column" className = { classes . sourcesDropdownHeader } >
177
+ < FormControlLabel
178
+ control = {
179
+ < Switch
180
+ checked = { showAllSources }
181
+ onChange = { handleShowSources }
182
+ color = "primary"
183
+ name = "displayVerified"
184
+ />
185
+ }
186
+ label = {
187
+ showAllSources
188
+ ? `Showing all Sources`
189
+ : `Show all Sources`
190
+ }
191
+ />
192
+ { showAllSources && < form
193
+ onSubmit = { ( e : React . SyntheticEvent ) => {
194
+ e . preventDefault ( ) ;
195
+ handleSearch ( queryString ) ;
196
+ } }
152
197
>
153
- { url ?. includes ( "/collection" ) ? (
154
- < FolderOpen className = { classes . sourceIcon } />
155
- ) : (
156
- < AccountTreeOutlined className = { classes . sourceIcon } />
157
- ) }
158
- { name }
159
- </ MenuItem >
160
- ) ) }
198
+ < Input
199
+ color = "primary"
200
+ type = "search"
201
+ fullWidth
202
+ placeholder = { "Select an alternative source" }
203
+ value = { queryString }
204
+ onChange = { e => setQueryString ( e . target . value ) }
205
+ endAdornment = {
206
+ < InputAdornment position = "end" >
207
+ < IconButton onClick = { ( ) => handleSearch ( queryString ) } >
208
+ < SearchIcon />
209
+ </ IconButton >
210
+ </ InputAdornment >
211
+ }
212
+ />
213
+ </ form > }
214
+ </ Grid >
215
+ { showAllSources ? (
216
+ < InfiniteScroll
217
+ dataLength = { totalCount }
218
+ next = { fetchMoreData }
219
+ hasMore = { true }
220
+ loader = { < h4 > Loading...</ h4 > }
221
+ endMessage = { < h4 > end</ h4 > }
222
+ scrollableTarget = "scrollableDiv"
223
+ >
224
+ { currentSources ?. map ( ( { name, url } ) => (
225
+ < MenuItem
226
+ // replace because we want to keep the back button useful
227
+ replace
228
+ to = { gimmeAUrl ( { } , `${ url } concepts/` ) }
229
+ key = { name }
230
+ component = { Link }
231
+ onClick = { handleSwitchSourceClose }
232
+ >
233
+ { url ?. includes ( "/collection" ) ? (
234
+ < FolderOpen className = { classes . sourceIcon } />
235
+ ) : (
236
+ < AccountTreeOutlined className = { classes . sourceIcon } />
237
+ ) }
238
+ { name }
239
+ </ MenuItem >
240
+ ) ) }
241
+ </ InfiniteScroll >
242
+ ) : (
243
+ currentSources ?. map ( ( { name, url } ) => (
244
+ < MenuItem
245
+ // replace because we want to keep the back button useful
246
+ replace
247
+ to = { gimmeAUrl ( { } , `${ url } concepts/` ) }
248
+ key = { name }
249
+ component = { Link }
250
+ onClick = { handleSwitchSourceClose }
251
+ data-testid = { name }
252
+ >
253
+ { url ?. includes ( "/collection" ) ? (
254
+ < FolderOpen className = { classes . sourceIcon } />
255
+ ) : (
256
+ < AccountTreeOutlined className = { classes . sourceIcon } />
257
+ ) }
258
+ { name }
259
+ </ MenuItem >
260
+ ) )
261
+ ) }
161
262
</ Menu >
162
263
</ >
163
264
) ;
164
265
} ;
165
-
166
266
return (
167
267
< Header
168
268
title = { getTitleBasedOnContainerType ( ) }
@@ -180,5 +280,4 @@ const ViewConceptsHeader: React.FC<Props> = ({
180
280
</ Header >
181
281
) ;
182
282
} ;
183
-
184
283
export default ViewConceptsHeader ;
0 commit comments