Open dekelb opened 7 years ago
I'll consider to implement it but actually, it's a good idea to have some ways to trigger selection, I will also consider to implement on react-bootstrap-table2
thanks
@AllenFang Was this implemented in react-bootstrap-table2?
@AllenFang how can this be implemented?
@AllenFang, I have found that this is a very important feature for wide adoption on list and select libraries. I find that the majority of the community libraries have skimped on this one. I've just implemented this functionality on my own dropdown library (in beta) successfully, and would be glad to contribute here, or share my own code as a starting place, or even just write out the detailed technical requirements if that will help.
This feature took lots of careful testing because handling keyup & keydown for shifts w/ arrows up/down or even changed mid-select is a lot of conditions while allowing for mouse click and shift-click to interoperate with the selections w/o issue. Add to that, managing the transitions across rows which were pre-selected before the shift-selection was begun, etc. I also found that the checkbox state boolean is not always the same as the row state boolean, depending on which elements are being used to construct the row and its wrapper(s). There are also some choices to consider as possible props for what happens when a selection is in place and a non-shift-selection occurs, which methods to clear all are supported or defaulted, etc. For larger datasets, performance becomes a consideration since certain points of selection state can only be understood or verified by searching the whole set. I also implemented a SelectionGroup dropdown which allowed for multiple list boxes to be configured. I wouldn't suggest that this was productive for most use cases or library weight, but it was very useful for me to learn that several issues that happen when multiple list boxes are deployed to keep state integrity between each, and to leave hooks for handling z-index layering, etc.
I am in process of migrating from react-bootstrap-table to -table2 on a smallish project where I must have shift-select, so I will need to go a different direction if I can't find a way to "hack it" in or else find the feature added.
Please let me know if I can help. thx
Dear, pls advice. I use react-bootstrap-table I can't select few checkboxes when holding shift. It's select only one.
How to set up to be able select checkboxes according click start and finish holding shift ? Thank you in advance ! Oleg
@olegshepel It would take me some time to do a complete write-up on this, but I will do a quick brain dump here. Maybe that will be useful. The essential concept is to define and manage the user's "shift-selection" operation as a mini-lifecycle, and then commit their "selection-groups" to the underlying component's props and state only where and when needed to cause a re-render. You could do what I'm about to describe by creating your own component to wrap and implement the underlying table component along with your shift-select handling. This would keep things very tidy and away from your own app's implementation. I have done this, but only for specific applications which have too much other stuff going on to share here. For now, I'm going to explain the basics in a much more "hacky" manner, so that you might get a mental image of the concept requirements. From there, you could compose and implement much more deeply or elegantly as your case may need, or as your own application separation or quality concerns are to be met. (you might also be fine with the hack :)
Here are some basic ideas to consider:
this.myShiftArray[clickPositionValue]
You will have to determine your own requirements for what a shift+click would mean to your user or implementation, but probably some conditional logic in the handler like...:
event.shiftKey = false
event.shiftKey = false
would mean the user simply clicked a different row and is not currently performing a shift+click operation. In this case, simply overwrite this.myShiftArray = [clickPositionValue]
. This leaves the shift-selection-group's "start" value as the position last clicked before a shift+click event occurs.event.shiftKey = true
, your handler would then check to see that this.myShiftArray.length === 1
, so it can know that a prior click has occurred and the user is now performing a shift+click to complete the selection from that point (use min/max points to allow upward/downward selection). This is when you would perform a for loop to update your component's row selections to match the group of rows between the two points which the user has just "shift-selected".This could mean a number of possibilities for you:
a. setState() on your "selected" array which would pass down as props after render
b. or maybe you do a dom.click() on each node to simulate the user's action (using something like event.target.parentElement.querySelectorAll("div")
, etc.) to find those nodes
c. or updating just the style or class value for those particular nodes if you are not passing selected items down to the component. You might find that passing the selections down to the underlying component could get very costly in render time if you are dealing with table's re-rendering hundreds of dom nodes. With react-bootstrap-table2 for instance, I let it handle the background style operation internally via its native row click, but I do not use the underlying onSelect or selected props, because the render time is way to long for a user who is in the middle of shifting and selecting, etc. With other libraries, you may have different options.
d. your own invention...
Whether a, b, or c, You may want to skip rows which are already selected on the dom (could use another this.alreadySelected for this or else check the dom nodes for a "selection-row" class, etc. ) or are marked as nonSelectable by the underlying component (I use a this.state.nonSelectableIds
array which gets updated as relevant).
NOTE: With b or c you will want/need to skip component updates via shouldComponentUpdate to avoid re-renders... again, it depends on the lib onto which you are bolting this functionality. You can set a boolean like this.skipShouldUpdateCase = true
when you perform b or c. Then in shouldComponentUpdate, you will want to do something like if ( this.skipShouldUpdateCase ) { this.skipShouldUpdateCase = false; return false; }
.
...here is only slightly better than pseudo-code to look at for some idea:
handleClick = ( event, dataRow, dataRowIndex ) => {
const divNodes = event.target.parentElement.querySelectorAll("div")
, isSelected = divNodes[dataRowIndex].classList.contains("selection-row")
, isShift = event.shiftKey
if ( this.myShiftArray === undefined ) this.myShiftArray = []
if ( !isShift ) {
if ( !isSelected ) {
//normal select, overwrite this.myShiftArray
this.myShiftArray = [dataRowIndex]
} else {
//DeSelect, remove from this.myShiftArray
this.myShiftArray.length && this.myShiftArray.splice( 0, 1 )
}
} else if ( isShift ) {
if ( !isSelected ) {
if ( !this.myShiftArray.length ) {
this.myShiftArray = [dataRowIndex]
} else {
this.myShiftArray.push( dataRowIndex )
const rowPosArray = this.doShiftClickForLoop( divNodes, Math.min(...this.myShiftArray), Math.max(...this.myShiftArray) )
this.refreshSelectionArray( divNodes, rowPosArray, true ) //push = true, splice = false, etc.
}
} else {
this.myShiftArray.length && this.myShiftArray.splice( 0, 1 )
}
}
}
Armed with the above information, you would simply extend your event listening and handling to include keyUp events. The keyboard gets a bit more tricky as well, because you must handle selection while the user arrows up/down, and potentially across selected or nonSelectable rows, etc. This really isn't more difficult, just more tedious. It helps if you create background colors to distinguish between already-selected, shift-selection-about-to-be-selected and hovering-over-selected-item, etc.
Good luck, I hope this helps. If you get your prototype in place in a repo somewhere, I could certainly help a bit within reasonable time constraints.
It would be great if we could use the shift key (in the keyboard) to select multiple rows. For example - click on the "select" of row#1, press down the shift key, click on the "select" of row#5 -> result is that rows 1,2,3,4,5 are selected.
Same with unselect.