ui: add search box to DataStore content
which searches the whole tree (name & owner) we do this by traversing the tree and marking elements as matches, then afterwards make a simple filter that matches on a boolean worst case cost of this is O(2n) since we have to traverse the tree (in the worst) case one time, and the filter function does it again Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
		
				
					committed by
					
						 Dietmar Maurer
						Dietmar Maurer
					
				
			
			
				
	
			
			
			
						parent
						
							3e395378bc
						
					
				
				
					commit
					6d55603dcc
				
			| @ -35,7 +35,12 @@ Ext.define('pbs-data-store-snapshots', { | |||||||
|  |  | ||||||
| 		return PBS.Utils.calculateCryptMode(crypt); | 		return PBS.Utils.calculateCryptMode(crypt); | ||||||
| 	    } | 	    } | ||||||
| 	} | 	}, | ||||||
|  | 	{ | ||||||
|  | 	    name: 'matchesFilter', | ||||||
|  | 	    type: 'boolean', | ||||||
|  | 	    defaultValue: true, | ||||||
|  | 	}, | ||||||
|     ] |     ] | ||||||
| }); | }); | ||||||
|  |  | ||||||
| @ -126,6 +131,7 @@ Ext.define('PBS.DataStoreContent', { | |||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
| 	onLoad: function(store, records, success, operation) { | 	onLoad: function(store, records, success, operation) { | ||||||
|  | 	    let me = this; | ||||||
| 	    let view = this.getView(); | 	    let view = this.getView(); | ||||||
|  |  | ||||||
| 	    if (!success) { | 	    if (!success) { | ||||||
| @ -144,12 +150,14 @@ Ext.define('PBS.DataStoreContent', { | |||||||
| 		data.text = group + '/' + PBS.Utils.render_datetime_utc(data["backup-time"]); | 		data.text = group + '/' + PBS.Utils.render_datetime_utc(data["backup-time"]); | ||||||
| 		data.leaf = false; | 		data.leaf = false; | ||||||
| 		data.cls = 'no-leaf-icons'; | 		data.cls = 'no-leaf-icons'; | ||||||
|  | 		data.matchesFilter = true; | ||||||
|  |  | ||||||
| 		data.children = []; | 		data.children = []; | ||||||
| 		for (const file of data.files) { | 		for (const file of data.files) { | ||||||
| 		    file.text = file.filename, | 		    file.text = file.filename, | ||||||
| 		    file['crypt-mode'] = PBS.Utils.cryptmap.indexOf(file['crypt-mode']); | 		    file['crypt-mode'] = PBS.Utils.cryptmap.indexOf(file['crypt-mode']); | ||||||
| 		    file.leaf = true; | 		    file.leaf = true; | ||||||
|  | 		    file.matchesFilter = true; | ||||||
|  |  | ||||||
| 		    data.children.push(file); | 		    data.children.push(file); | ||||||
| 		} | 		} | ||||||
| @ -178,6 +186,7 @@ Ext.define('PBS.DataStoreContent', { | |||||||
|  |  | ||||||
| 		} | 		} | ||||||
| 		group.count = group.children.length; | 		group.count = group.children.length; | ||||||
|  | 		group.matchesFilter = true; | ||||||
| 		crypt.count = group.count; | 		crypt.count = group.count; | ||||||
| 		group['crypt-mode'] = PBS.Utils.calculateCryptMode(crypt); | 		group['crypt-mode'] = PBS.Utils.calculateCryptMode(crypt); | ||||||
| 		children.push(group); | 		children.push(group); | ||||||
| @ -188,6 +197,11 @@ Ext.define('PBS.DataStoreContent', { | |||||||
| 		children: children | 		children: children | ||||||
| 	    }); | 	    }); | ||||||
| 	    Proxmox.Utils.setErrorMask(view, false); | 	    Proxmox.Utils.setErrorMask(view, false); | ||||||
|  | 	    if (view.getStore().getFilters().length > 0) { | ||||||
|  | 		let searchBox = me.lookup("searchbox"); | ||||||
|  | 		let searchvalue = searchBox.getValue();; | ||||||
|  | 		me.search(searchBox, searchvalue); | ||||||
|  | 	    } | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
| 	onPrune: function(view, rI, cI, item, e, rec) { | 	onPrune: function(view, rI, cI, item, e, rec) { | ||||||
| @ -331,7 +345,71 @@ Ext.define('PBS.DataStoreContent', { | |||||||
| 		'backup-type': type, | 		'backup-type': type, | ||||||
| 		archive: rec.data.filename, | 		archive: rec.data.filename, | ||||||
| 	    }).show(); | 	    }).show(); | ||||||
| 	} | 	}, | ||||||
|  |  | ||||||
|  | 	filter: function(item, value) { | ||||||
|  | 	    if (item.data.text.indexOf(value) !== -1) { | ||||||
|  | 		return true; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	    if (item.data.owner && item.data.owner.indexOf(value) !== -1) { | ||||||
|  | 		return true; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	    return false; | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	search: function(tf, value) { | ||||||
|  | 	    let me = this; | ||||||
|  | 	    let view = me.getView(); | ||||||
|  | 	    let store = view.getStore(); | ||||||
|  | 	    if (!value && value !== 0) { | ||||||
|  | 		store.clearFilter(); | ||||||
|  | 		store.getRoot().collapseChildren(true); | ||||||
|  | 		tf.triggers.clear.setVisible(false); | ||||||
|  | 		return; | ||||||
|  | 	    } | ||||||
|  | 	    tf.triggers.clear.setVisible(true); | ||||||
|  | 	    if (value.length < 2) return; | ||||||
|  | 	    Proxmox.Utils.setErrorMask(view, true); | ||||||
|  | 	    // we do it a little bit later for the error mask to work | ||||||
|  | 	    setTimeout(function() { | ||||||
|  | 		store.clearFilter(); | ||||||
|  | 		store.getRoot().collapseChildren(true); | ||||||
|  |  | ||||||
|  | 		store.beginUpdate(); | ||||||
|  | 		store.getRoot().cascadeBy({ | ||||||
|  | 		    before: function(item) { | ||||||
|  | 			if(me.filter(item, value)) { | ||||||
|  | 			    item.set('matchesFilter', true); | ||||||
|  | 			    if (item.parentNode && item.parentNode.id !== 'root') { | ||||||
|  | 				item.parentNode.childmatches = true; | ||||||
|  | 			    } | ||||||
|  | 			    return false; | ||||||
|  | 			} | ||||||
|  | 			return true; | ||||||
|  | 		    }, | ||||||
|  | 		    after: function(item) { | ||||||
|  | 			if (me.filter(item, value) || item.id === 'root' || item.childmatches) { | ||||||
|  | 			    item.set('matchesFilter', true); | ||||||
|  | 			    if (item.parentNode && item.parentNode.id !== 'root') { | ||||||
|  | 				item.parentNode.childmatches = true; | ||||||
|  | 			    } | ||||||
|  | 			    if (item.childmatches) { | ||||||
|  | 				item.expand(); | ||||||
|  | 			    } | ||||||
|  | 			} else { | ||||||
|  | 			    item.set('matchesFilter', false); | ||||||
|  | 			} | ||||||
|  | 			delete item.childmatches; | ||||||
|  | 		    }, | ||||||
|  | 		}); | ||||||
|  | 		store.endUpdate(); | ||||||
|  |  | ||||||
|  | 		store.filter((item) => !!item.get('matchesFilter')); | ||||||
|  | 		Proxmox.Utils.setErrorMask(view, false); | ||||||
|  | 	    }, 10); | ||||||
|  | 	}, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     columns: [ |     columns: [ | ||||||
| @ -448,5 +526,31 @@ Ext.define('PBS.DataStoreContent', { | |||||||
| 	    iconCls: 'fa fa-refresh', | 	    iconCls: 'fa fa-refresh', | ||||||
| 	    handler: 'reload', | 	    handler: 'reload', | ||||||
| 	}, | 	}, | ||||||
|  | 	'->', | ||||||
|  | 	{ | ||||||
|  | 	    xtype: 'tbtext', | ||||||
|  | 	    html: gettext('Search'), | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 	    xtype: 'textfield', | ||||||
|  | 	    reference: 'searchbox', | ||||||
|  | 	    triggers: { | ||||||
|  | 		clear: { | ||||||
|  | 		    cls: 'pmx-clear-trigger', | ||||||
|  | 		    weight: -1, | ||||||
|  | 		    hidden: true, | ||||||
|  | 		    handler: function() { | ||||||
|  | 			this.triggers.clear.setVisible(false); | ||||||
|  | 			this.setValue(''); | ||||||
|  | 		    }, | ||||||
|  | 		} | ||||||
|  | 	    }, | ||||||
|  | 	    listeners: { | ||||||
|  | 		change: { | ||||||
|  | 		    fn: 'search', | ||||||
|  | 		    buffer: 500, | ||||||
|  | 		}, | ||||||
|  | 	    }, | ||||||
|  | 	} | ||||||
|     ], |     ], | ||||||
| }); | }); | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user