Table click and drag to select multiple rows?

Operators at a touchless screen with mouse only, no keyboard, are wanting to be able to more easily multi-select rows in a table. They're wanting to be able to just click and drag a selection to select all rows contained within the selection rectangle. I know it's not possible out of the box? but is this possible to implement via js injection?

its a bit janky, doesnt always seems to catch the start... but its something
its not drawing a sqaure though, that doesnt really work well with scrollable content

Summary
	
	code =  """<img style='display:none' src='/favicon.ico' onload=\"		
		let start;
		function onmousestart(ev){
			start = ev.target;
		};
		function onmousestop(ev){			
			let stop = ev.target;
			var clickEvent = new MouseEvent('click', {bubbles:true, shiftKey:true});						
			start.dispatchEvent(clickEvent);
			stop.dispatchEvent(clickEvent);
			start=null;
		};		
		const callbackTable = (mutationList, observer) => {		
			document.querySelectorAll('.tr-group.ia_table__rowGroup .content').forEach(e => {					
						e.onmouseup = onmousestop;
						e.onmousedown = onmousestart;										
					});	
		};
		const observerTable = new MutationObserver(callbackTable); 
		const tables = document.querySelectorAll('.ia_tableComponent .t.ia_table > .tb');
		tables.forEach(table => observerTable.observe(table, { attributes: false, childList: true, subtree: true }));	
		document.querySelectorAll('.tr-group.ia_table__rowGroup .content').forEach(e => {					
							e.onmouseup = onmousestop;
							e.onmousedown = onmousestart;													
						});	
				
	\"></img>""".replace("\n", "").replace("\t", "")
	return code
		

this adds in a cursor style too, to atleast see something while dragging.

Summary
	
	code =  """<img style='display:none' src='/favicon.ico' onload=\"		
		let start;
		const tables = document.querySelectorAll('.ia_tableComponent .t.ia_table > .tb');
		function onmousestart(ev){
			start = ev.target;
			tables.forEach(table=> table.style.cursor = 'row-resize');
		};
		function onmousestop(ev){			
			let stop = ev.target;
			var clickEvent = new MouseEvent('click', {bubbles:true, shiftKey:true});						
			start.dispatchEvent(clickEvent);
			stop.dispatchEvent(clickEvent);
			start=null;
			tables.forEach(table=> table.style.cursor = 'auto');
		};		
		const callbackTable = (mutationList, observer) => {		
			document.querySelectorAll('.tr-group.ia_table__rowGroup .content').forEach(e => {					
						e.onmouseup = onmousestop;
						e.onmousedown = onmousestart;										
					});	
		};
		const observerTable = new MutationObserver(callbackTable); 
		
		tables.forEach(table => observerTable.observe(table, { attributes: false, childList: true, subtree: true }));	
		document.querySelectorAll('.tr-group.ia_table__rowGroup .content').forEach(e => {					
							e.onmouseup = onmousestop;
							e.onmousedown = onmousestart;													
						});	
				
	\"></img>""".replace("\n", "").replace("\t", "")
	return code
		

Sorry Victor, I must have missed this! Thanks I'm looking at this today :mage:

It's pretty good! Without the selection rectangle though it's a bit confusing. Is it possible to update the selection as the mouse is dragged instead of right at the end?

good idea, thats a lot cleaner

	
	code =  """<img style='display:none' src='/favicon.ico' onload=\"		
		let start;
		const tables = document.querySelectorAll('.ia_tableComponent .t.ia_table > .tb');
		function onmousestart(ev){
			start = ev.target;
			tables.forEach(table=> table.style.cursor = 'row-resize');
			if(start){
				ev.target.dispatchEvent(new MouseEvent('click', {bubbles:true}));
			}
		};
		function onmousestop(ev){			
			let stop = ev.target;
			var clickEvent = new MouseEvent('click', {bubbles:true, shiftKey:true});						
			start.dispatchEvent(clickEvent);
			stop.dispatchEvent(clickEvent);
			start=null;
			tables.forEach(table=> table.style.cursor = 'auto');
		};	
		function onmouseenter(ev){
			if(start){
				ev.target.dispatchEvent(new MouseEvent('click', {bubbles:true, ctrlKey:true}));
			}
		};
		
		const callbackTable = (mutationList, observer) => {		
			document.querySelectorAll('.tr-group.ia_table__rowGroup .content').forEach(e => {					
						e.onmouseup = onmousestop;
						e.onmousedown = onmousestart;
						e.onmouseenter = onmouseenter;										
					});	
		};
		
		const observerTable = new MutationObserver(callbackTable); 
		
		tables.forEach(table => observerTable.observe(table, { attributes: false, childList: true, subtree: true }));	
		document.querySelectorAll('.tr-group.ia_table__rowGroup .content').forEach(e => {					
							e.onmouseup = onmousestop;
							e.onmousedown = onmousestart;
							e.onmouseenter = onmouseenter;													
						});	
				
	\"></img>""".replace("\n", "").replace("\t", "")
	return code
		
1 Like

Hmm, I'm finding it a bit flakey. Sometimes it works, other times it doesn't. Anything you can think of?

There aren't really any other solutions I've got on the table (pun intended) at the moment. The other option might have been a "select range" toggle which then let users select a start and end row and then selected all rows in between, but I can't even do that since there's no facility to enable it on the default table component :confused: (you can only force it to select a single row, not multiple)

yeah its pretty janky.
The perspective base click events are still on there somewhere. making it hard to trigger other clicks xd
i suppose i could dissable those, but i dont really know on which componts they are on and what other functionality that might break xD
i might be able to run some more tests later if i got some free time. but wont be this week

1 Like

I ended up implementing something a little different. Toggle to enable selecting multiple rows. User then clicks on the first row, then the second, and then the rows are selected in between. It actually works really well, and probably more useable that click and drag anyway, especially since there can be 100's of rows

1 Like

share code? :stuck_out_tongue:

1 Like

It's a bit hard to shrink up into something easy to share, but this is the essence of it:

The "Last" tick goes green so quickly that it basically doesn't show, so I will have a think about that bit..

The bindings on the other props are just bound to expression None to make sure they're empty when the page first loads.

PS. I actually didn't end up using the custom.startRow and endRow as they produced a race condition firing the script to "mark" the table rows.

2 Likes