import React from 'react'; // Common "list with selection" component export default class ListWithSelection extends React.PureComponent { // requires to override methods: this.deleteSelected(), this.getPageSize(), this.getItemOffset(index), this.getTotalItems() constructor(props) { super(props); this.state = this.state||{}; this.state.selected = {}; } isSelected(i) { return this.state.selected[i] || this.state.selected.begin !== undefined && this.state.selected.begin <= i && this.state.selected.end >= i; } onListKeyDown = (ev) => { if (!this.getTotalItems()) return; if (ev.keyCode == 46) // delete { this.deleteSelected(); this.setState({ selected: {} }); ev.stopPropagation(); } else if (ev.keyCode == 38 || ev.keyCode == 40 || ev.keyCode == 33 || ev.keyCode == 34) // up, down, pgup, pgdown { let sel = this.curSel, dir; if (ev.keyCode < 35) dir = (ev.keyCode == 34 ? 1 : -1) * this.getPageSize(); else dir = (ev.keyCode == 40 ? 1 : -1); if (sel !== null) { let nsel = sel+dir, n = this.getTotalItems(); if (nsel < 0) nsel = 0; if (nsel >= n) nsel = n-1; if (sel != nsel) { if (ev.shiftKey) this.selectTo(nsel); else this.selectOne(nsel); let pos = this.getItemOffset(nsel); if (pos[0] + pos[1] > this.refs.scroll.scrollTop + this.refs.scroll.offsetHeight) this.refs.scroll.scrollTop = pos[0] + pos[1] - this.refs.scroll.offsetHeight; else if (pos[0] < this.refs.scroll.scrollTop + this.getScrollPaddingTop()) this.refs.scroll.scrollTop = pos[0] - this.getScrollPaddingTop(); ev.stopPropagation(); } ev.preventDefault(); // prevent scroll } } else if (ev.keyCode == 36) // home { if (ev.shiftKey) { this.selectTo(0); this.refs.scroll.scrollTop = pos[0] - this.getScrollPaddingTop(); } else this.selectOne(0); } else if (ev.keyCode == 35) // end { let nsel = this.getTotalItems()-1; if (ev.shiftKey) { this.selectTo(nsel); let pos = this.getItemOffset(nsel); this.refs.scroll.scrollTop = pos[0] + pos[1] - this.refs.scroll.offsetHeight; } else this.selectOne(nsel); } } selectTo(ns) { if (this.lastSel === undefined) return this.selectOne(ns); let sel = {}; let n = this.getTotalItems(); if (this.lastSel >= n) this.lastSel = n-1; if (ns < this.lastSel) sel = { begin: ns, end: this.lastSel }; else if (ns > this.lastSel) sel = { begin: this.lastSel, end: ns }; else sel[ns] = true; this.setState({ selected: sel }); this.curSel = ns; if (this.onSelectCurrent) this.onSelectCurrent(ns); } selectOne(ns) { let sel = {}; sel[ns] = true; this.setState({ selected: sel }); this.lastSel = ns; this.curSel = ns; if (this.onSelectCurrent) this.onSelectCurrent(ns); } onListItemClick = (ev) => { let t = ev.target; while (t && !t.getAttribute('data-i')) t = t.parentNode; if (t) { let ns = parseInt(t.getAttribute('data-i')); if (ev.shiftKey) this.selectTo(ns); else if (ev.ctrlKey) { this.state.selected[ns] = true; this.curSel = ns; if (this.onSelectCurrent) this.onSelectCurrent(ns); this.lastSel = this.lastSel === undefined ? ns : this.lastSel; this.setState({ selected: this.state.selected }); } else this.selectOne(ns); } } }