@@ -8,10 +8,23 @@ import { DockLayout, Widget } from '@lumino/widgets';
8
8
9
9
import { IVariableInspector } from './tokens' ;
10
10
11
+ import wildcardMatch from 'wildcard-match' ;
12
+
11
13
const TITLE_CLASS = 'jp-VarInspector-title' ;
12
14
const PANEL_CLASS = 'jp-VarInspector' ;
13
15
const TABLE_CLASS = 'jp-VarInspector-table' ;
14
16
const TABLE_BODY_CLASS = 'jp-VarInspector-content' ;
17
+ const TABLE_ROW_CLASS = 'jp-VarInspector-table-row' ;
18
+ const TABLE_ROW_HIDDEN_CLASS = 'jp-VarInspector-table-row-hidden' ;
19
+ const TABLE_TYPE_CLASS = 'jp-VarInspector-type' ;
20
+ const TABLE_NAME_CLASS = 'jp-VarInspector-varName' ;
21
+ const FILTER_TYPE_CLASS = 'filter-type' ;
22
+ const FILTER_INPUT_CLASS = 'filter-input' ;
23
+ const FILTER_BUTTON_CLASS = 'filter-button' ;
24
+ const FILTER_LIST_CLASS = 'filter-list' ;
25
+ const FILTERED_BUTTON_CLASS = 'filtered-variable-button' ;
26
+
27
+ type FILTER_TYPES = 'type' | 'name' ;
15
28
16
29
/**
17
30
* A panel that renders the variables
@@ -21,8 +34,10 @@ export class VariableInspectorPanel
21
34
implements IVariableInspector
22
35
{
23
36
private _source : IVariableInspector . IInspectable | null = null ;
37
+ private _filteredTable : HTMLDivElement ;
24
38
private _table : HTMLTableElement ;
25
39
private _title : HTMLElement ;
40
+ private _filtered : { type : Array < string > ; name : Array < string > } ;
26
41
27
42
constructor ( ) {
28
43
super ( ) ;
@@ -31,8 +46,136 @@ export class VariableInspectorPanel
31
46
this . _title . className = TITLE_CLASS ;
32
47
this . _table = Private . createTable ( ) ;
33
48
this . _table . className = TABLE_CLASS ;
49
+ this . _filteredTable = Private . createFilterTable ( ) ;
34
50
this . node . appendChild ( this . _title as HTMLElement ) ;
51
+ this . node . appendChild ( this . _filteredTable as HTMLElement ) ;
35
52
this . node . appendChild ( this . _table as HTMLElement ) ;
53
+ this . _filtered = { type : [ ] , name : [ ] } ;
54
+ this . intializeFilteredTable ( ) ;
55
+ }
56
+
57
+ //Sets up the filter table so when the filter button is pressed, a new filter is created
58
+ protected intializeFilteredTable ( ) {
59
+ const filterType = this . _filteredTable . querySelector (
60
+ '.' + FILTER_TYPE_CLASS
61
+ ) as HTMLSelectElement ;
62
+ const filterInput = this . _filteredTable . querySelector (
63
+ '.' + FILTER_INPUT_CLASS
64
+ ) as HTMLInputElement ;
65
+ const filterButton = this . _filteredTable . querySelector (
66
+ '.' + FILTER_BUTTON_CLASS
67
+ ) as HTMLButtonElement ;
68
+ filterButton . addEventListener ( 'click' , ( ) => {
69
+ this . onFilterChange (
70
+ filterType . value as FILTER_TYPES ,
71
+ filterInput . value ,
72
+ true
73
+ ) ;
74
+ } ) ;
75
+ }
76
+
77
+ // Checks if string is in the filtered array
78
+ protected stringInFilter ( string : string , filterType : FILTER_TYPES ) {
79
+ // console.log(this._filtered[filterType]);
80
+ for ( let i = 0 ; i < this . _filtered [ filterType ] . length ; i ++ ) {
81
+ const isMatch = wildcardMatch ( this . _filtered [ filterType ] [ i ] ) ;
82
+ if ( isMatch ( string ) ) {
83
+ return true ;
84
+ }
85
+ }
86
+ return false ;
87
+ }
88
+ /*
89
+ Either adds a new filter or removes a previously existing filter based
90
+ Params:
91
+ filterType: By what type the varName is filtering on
92
+ varName: The name of the variable we are trying to filter out
93
+ isAdding: If we are adding a new filter or removing a previous filter
94
+ */
95
+
96
+ protected onFilterChange (
97
+ filterType : FILTER_TYPES ,
98
+ varName : string ,
99
+ isAdding : boolean
100
+ ) {
101
+ if ( varName === '' ) {
102
+ return ;
103
+ }
104
+ if ( isAdding ) {
105
+ if ( this . _filtered [ filterType ] . includes ( varName ) ) {
106
+ return ;
107
+ }
108
+ this . _filtered [ filterType ] . push ( varName ) ;
109
+ const filterList = this . _filteredTable . querySelector (
110
+ '.' + FILTER_LIST_CLASS
111
+ ) as HTMLUListElement ;
112
+ const newFilteredButton = Private . createFilteredButton (
113
+ varName ,
114
+ filterType
115
+ ) ;
116
+ newFilteredButton . addEventListener ( 'click' , ( ) => {
117
+ const filterText = newFilteredButton . querySelector (
118
+ '.filtered-variable-button-text'
119
+ ) as HTMLDivElement ;
120
+ this . onFilterChange ( filterType , filterText . innerHTML , false ) ;
121
+ this . addFilteredOutRows ( ) ;
122
+ newFilteredButton . remove ( ) ;
123
+ } ) ;
124
+ filterList . appendChild ( newFilteredButton ) ;
125
+ this . filterOutTable ( ) ;
126
+ } else {
127
+ this . _filtered [ filterType ] = this . _filtered [ filterType ] . filter (
128
+ filter => filter !== varName
129
+ ) ;
130
+ }
131
+ }
132
+
133
+ /*
134
+ Goes through each filtered out row and checks if they should still be filtered
135
+ If not, the row becomes visible again
136
+ */
137
+ protected addFilteredOutRows ( ) {
138
+ const rows = this . _table . querySelectorAll (
139
+ '.' + TABLE_ROW_HIDDEN_CLASS
140
+ ) as NodeListOf < HTMLTableRowElement > ;
141
+ for ( let i = 0 ; i < rows . length ; i ++ ) {
142
+ const rowName = rows [ i ] . querySelector (
143
+ '.' + TABLE_NAME_CLASS
144
+ ) as HTMLTableCellElement ;
145
+ const rowType = rows [ i ] . querySelector (
146
+ '.' + TABLE_TYPE_CLASS
147
+ ) as HTMLTableCellElement ;
148
+ if (
149
+ ! this . stringInFilter ( rowName . innerHTML , 'name' ) &&
150
+ ! this . _filtered [ 'type' ] . includes ( rowType . innerHTML )
151
+ ) {
152
+ rows [ i ] . className = TABLE_ROW_CLASS ;
153
+ }
154
+ }
155
+ }
156
+
157
+ /*
158
+ Goes through each row and checks if the row should be filtered out
159
+ A row is filtered out if it matches any of the values in the _filtered object
160
+ */
161
+ protected filterOutTable ( ) {
162
+ const rows = this . _table . querySelectorAll (
163
+ '.' + TABLE_ROW_CLASS
164
+ ) as NodeListOf < HTMLTableRowElement > ;
165
+ for ( let i = 0 ; i < rows . length ; i ++ ) {
166
+ const rowName = rows [ i ] . querySelector (
167
+ '.' + TABLE_NAME_CLASS
168
+ ) as HTMLTableCellElement ;
169
+ const rowType = rows [ i ] . querySelector (
170
+ '.' + TABLE_TYPE_CLASS
171
+ ) as HTMLTableCellElement ;
172
+ if (
173
+ this . stringInFilter ( rowName . innerHTML , 'name' ) ||
174
+ this . _filtered [ 'type' ] . includes ( rowType . innerHTML )
175
+ ) {
176
+ rows [ i ] . className = TABLE_ROW_HIDDEN_CLASS ;
177
+ }
178
+ }
36
179
}
37
180
38
181
get source ( ) : IVariableInspector . IInspectable | null {
@@ -99,6 +242,12 @@ export class VariableInspectorPanel
99
242
const varType = item . varType ;
100
243
101
244
row = this . _table . tFoot ! . insertRow ( ) ;
245
+ row . className = TABLE_ROW_CLASS ;
246
+ if ( this . _filtered [ 'type' ] . includes ( varType ) ) {
247
+ row . className = TABLE_ROW_HIDDEN_CLASS ;
248
+ } else if ( this . stringInFilter ( name , 'name' ) ) {
249
+ row . className = TABLE_ROW_HIDDEN_CLASS ;
250
+ }
102
251
103
252
// Add delete icon and onclick event
104
253
let cell = row . insertCell ( 0 ) ;
@@ -130,12 +279,13 @@ export class VariableInspectorPanel
130
279
}
131
280
132
281
cell = row . insertCell ( 2 ) ;
133
- cell . className = 'jp-VarInspector-varName' ;
282
+ cell . className = TABLE_NAME_CLASS ;
134
283
cell . innerHTML = name ;
135
284
136
285
// Add remaining cells
137
286
cell = row . insertCell ( 3 ) ;
138
287
cell . innerHTML = varType ;
288
+ cell . className = TABLE_TYPE_CLASS ;
139
289
cell = row . insertCell ( 4 ) ;
140
290
cell . innerHTML = item . varSize ;
141
291
cell = row . insertCell ( 5 ) ;
@@ -233,4 +383,59 @@ namespace Private {
233
383
title . innerHTML = header ;
234
384
return title ;
235
385
}
386
+
387
+ export function createFilterTable ( ) : HTMLDivElement {
388
+ const container = document . createElement ( 'div' ) ;
389
+ container . className = 'filter-container' ;
390
+ const filterType = document . createElement ( 'select' ) ;
391
+ filterType . className = FILTER_TYPE_CLASS ;
392
+ filterType . selectedIndex = 0 ;
393
+ const varTypeOption = document . createElement ( 'option' ) ;
394
+ varTypeOption . value = 'type' ;
395
+ varTypeOption . innerHTML = 'Type' ;
396
+ const nameOption = document . createElement ( 'option' ) ;
397
+ nameOption . value = 'name' ;
398
+ nameOption . innerHTML = 'Name' ;
399
+ filterType . appendChild ( varTypeOption ) ;
400
+ filterType . appendChild ( nameOption ) ;
401
+ const searchContainer = document . createElement ( 'div' ) ;
402
+ searchContainer . className = 'jp-InputGroup filter-search-container' ;
403
+ const input = document . createElement ( 'input' ) ;
404
+ input . setAttribute ( 'type' , 'text' ) ;
405
+ input . setAttribute ( 'placeholder' , 'Filter out variable' ) ;
406
+ input . className = FILTER_INPUT_CLASS ;
407
+ const filterButton = document . createElement ( 'button' ) ;
408
+ const buttonText = document . createTextNode ( 'Filter' ) ;
409
+ filterButton . appendChild ( buttonText ) ;
410
+ filterButton . className = FILTER_BUTTON_CLASS ;
411
+ const list = document . createElement ( 'ul' ) ;
412
+ list . className = FILTER_LIST_CLASS ;
413
+
414
+ searchContainer . appendChild ( filterType ) ;
415
+ searchContainer . appendChild ( input ) ;
416
+ searchContainer . appendChild ( filterButton ) ;
417
+ container . appendChild ( searchContainer ) ;
418
+ container . appendChild ( list ) ;
419
+ return container ;
420
+ }
421
+
422
+ //Creates a button with given filter information displayed on the button
423
+ export function createFilteredButton (
424
+ filterName : string ,
425
+ filterType : FILTER_TYPES
426
+ ) : HTMLButtonElement {
427
+ const filteredButton = document . createElement ( 'button' ) ;
428
+ filteredButton . value = filterType ;
429
+ filteredButton . title = filterType ;
430
+ const buttonText = document . createElement ( 'div' ) ;
431
+ buttonText . className = 'filtered-variable-button-text' ;
432
+ buttonText . innerHTML = filterName ;
433
+ const icon = closeIcon . element ( {
434
+ container : filteredButton
435
+ } ) ;
436
+ filteredButton . appendChild ( buttonText ) ;
437
+ filteredButton . appendChild ( icon ) ;
438
+ filteredButton . className = FILTERED_BUTTON_CLASS ;
439
+ return filteredButton ;
440
+ }
236
441
}
0 commit comments