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
					
				
			
			
				
	
			
			
			
						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