Skip to content

Commit 92fb5bc

Browse files
authored
Add ListView multiselect support (#40)
1 parent 035156d commit 92fb5bc

File tree

1 file changed

+103
-20
lines changed

1 file changed

+103
-20
lines changed

src/components/ListView.js

Lines changed: 103 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ const createView = props => {
4040

4141
const cols = (paneIndex) => (row, rowIndex) => {
4242
const col = row.columns[paneIndex] || {};
43-
const selected = props.selectedIndex === rowIndex;
4443
const colIcon = col.icon ? h(Icon, col.icon) : null;
4544
const children = [h('span', {}, [typeof col === 'object' ? col.label : col])];
45+
const selected = props.multiselect
46+
? props.selectedIndex.indexOf(rowIndex) !== -1
47+
: props.selectedIndex === rowIndex;
4648

4749
if (colIcon) {
4850
children.unshift(colIcon);
@@ -52,11 +54,11 @@ const createView = props => {
5254
key: row.key,
5355
'data-has-icon': col.icon ? true : undefined,
5456
class: 'osjs-gui-list-view-cell' + (selected ? ' osjs__active' : ''),
55-
ontouchstart: (ev) => tapper(ev, () => props.onactivate({data: row.data, index: rowIndex, ev})),
56-
ondblclick: (ev) => props.onactivate({data: row.data, index: rowIndex, ev}),
57-
onclick: (ev) => props.onselect({data: row.data, index: rowIndex, ev}),
58-
oncontextmenu: (ev) => props.oncontextmenu({data: row.data, index: rowIndex, ev}),
59-
oncreate: (el) => props.oncreate({data: row.data, index: rowIndex, el})
57+
ontouchstart: (ev) => tapper(ev, () => props.onactivate({index: rowIndex, ev})),
58+
ondblclick: (ev) => props.onactivate({index: rowIndex, ev}),
59+
onclick: (ev) => props.onselect({index: rowIndex, ev}),
60+
oncontextmenu: (ev) => props.oncontextmenu({index: rowIndex, ev}),
61+
oncreate: (el) => props.oncreate({index: rowIndex, el})
6062
}, children);
6163
};
6264

@@ -86,7 +88,11 @@ const createView = props => {
8688
},
8789
oncreate: el => (el.scrollTop = props.scrollTop),
8890
onupdate: el => {
89-
if (props.selectedIndex < 0) {
91+
const notSelected = props.multiselect
92+
? props.selectedIndex.length === 0
93+
: props.selectedIndex < 0;
94+
95+
if (notSelected) {
9096
el.scrollTop = props.scrollTop;
9197
}
9298
}
@@ -99,25 +105,101 @@ export const ListView = props => h(Element, Object.assign({
99105

100106
export const listView = ({
101107
component: (state, actions) => {
108+
const createSelection = index => {
109+
if (state.multiselect) {
110+
const foundIndex = state.selectedIndex.indexOf(index);
111+
const newSelection = [...state.selectedIndex];
112+
if (foundIndex === -1) {
113+
newSelection.push(index);
114+
} else {
115+
newSelection.splice(foundIndex, 1);
116+
}
117+
118+
return newSelection;
119+
}
120+
121+
return state.selectedIndex;
122+
};
123+
124+
/**
125+
* Creates a range of indexes from start to end
126+
* @param {Number} start
127+
* @param {Number} end
128+
* @return {Array}
129+
*/
130+
const createSelectionRange = (start, end) => {
131+
// Swaps start and end if start is greater than end
132+
if (start > end) {
133+
[start, end] = [end, start];
134+
}
135+
136+
const indices = [
137+
...state.selectedIndex,
138+
// Generates a range of indexes from start to end
139+
...Array.from({length: end - start + 1}, (_, i) => i + start)
140+
];
141+
142+
// Remove duplicates from the array
143+
return [...new Set(indices)];
144+
};
145+
146+
const getSelection = (index, ev) => {
147+
const selected = state.multiselect
148+
? (ev.shiftKey
149+
? createSelectionRange(state.previousSelectedIndex, index)
150+
: ev.ctrlKey
151+
? createSelection(index)
152+
: [index])
153+
: index;
154+
155+
const data = state.multiselect
156+
? selected.map((item) => state.rows[item].data)
157+
: state.rows[selected].data;
158+
159+
// Store the previous index in the state to use for calculating the
160+
// range if the shift key is pressed
161+
if (state.multiselect) {
162+
actions.setPreviousSelectedIndex(index);
163+
}
164+
165+
return {selected, data};
166+
};
167+
168+
const clearCurrentSelection = (index) => {
169+
const selected = state.multiselect ? [] : -1;
170+
171+
const data = state.multiselect
172+
? state.selectedIndex.map((item) => state.rows[item].data)
173+
: state.rows[index].data;
174+
175+
return {selected, data};
176+
};
177+
102178
const newProps = Object.assign({
179+
multiselect: false,
103180
zebra: true,
104181
columns: [],
105182
rows: [],
106-
onselect: ({data, index, ev}) => {
107-
actions.select({data, index, ev});
108-
actions.setSelectedIndex(index);
183+
onselect: ({index, ev}) => {
184+
const {selected, data} = getSelection(index, ev);
185+
actions.select({data, index, ev, selected});
186+
actions.setSelectedIndex(selected);
109187
},
110-
onactivate: ({data, index, ev}) => {
111-
actions.activate({data, index, ev});
112-
actions.setSelectedIndex(-1);
188+
onactivate: ({index, ev}) => {
189+
const {selected, data} = clearCurrentSelection(index);
190+
actions.activate({data, index, ev, selected});
191+
actions.setSelectedIndex(selected);
113192
},
114-
oncontextmenu: ({data, index, ev}) => {
193+
oncontextmenu: ({index, ev}) => {
194+
const {selected, data} = getSelection(index, ev);
195+
115196
actions.select({data, index, ev});
116-
actions.contextmenu({data, index, ev});
117-
actions.setSelectedIndex(index);
197+
actions.contextmenu({data, index, ev, selected});
198+
actions.setSelectedIndex(selected);
118199
},
119-
oncreate: (args) => {
120-
actions.created(args);
200+
oncreate: ({index, el}) => {
201+
const data = state.rows[index].data;
202+
actions.created({index, el, data});
121203
},
122204
onscroll: (ev) => {
123205
actions.scroll(ev);
@@ -128,7 +210,7 @@ export const listView = ({
128210
},
129211

130212
state: state => Object.assign({
131-
selectedIndex: -1,
213+
selectedIndex: state.multiselect ? [] : -1,
132214
scrollTop: 0
133215
}, state),
134216

@@ -141,6 +223,7 @@ export const listView = ({
141223
setRows: rows => ({rows}),
142224
setColumns: columns => ({columns}),
143225
setScrollTop: scrollTop => state => ({scrollTop}),
144-
setSelectedIndex: selectedIndex => state => ({selectedIndex})
226+
setSelectedIndex: selectedIndex => ({selectedIndex}),
227+
setPreviousSelectedIndex: previousSelectedIndex => ({previousSelectedIndex}),
145228
}, actions || {})
146229
});

0 commit comments

Comments
 (0)