Extract list selection into a mixin
parent
f4374ec5f6
commit
3cd3685448
241
mail.js
241
mail.js
|
@ -736,7 +736,131 @@ function formatDate(dt)
|
|||
return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m;
|
||||
}
|
||||
|
||||
// Common selection mixin
|
||||
var ListWithSelection = {
|
||||
// requires to override methods: this.deleteSelected(), this.getPageSize(), this.getItemOffset(index), this.getTotalItems()
|
||||
getInitialState: function()
|
||||
{
|
||||
return {
|
||||
selected: {}
|
||||
};
|
||||
},
|
||||
onListKeyDown: function(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
|
||||
{
|
||||
var 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)
|
||||
{
|
||||
var nsel = sel+dir;
|
||||
if (nsel < 0)
|
||||
nsel = 0;
|
||||
if (nsel >= this.getTotalItems())
|
||||
nsel = this.getTotalItems()-1;
|
||||
if (sel != nsel)
|
||||
{
|
||||
if (ev.shiftKey)
|
||||
this.selectTo(nsel);
|
||||
else
|
||||
this.selectOne(nsel);
|
||||
var 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
|
||||
{
|
||||
var nsel = this.getTotalItems()-1;
|
||||
if (ev.shiftKey)
|
||||
{
|
||||
this.selectTo(nsel);
|
||||
var pos = this.getItemOffset(nsel);
|
||||
this.refs.scroll.scrollTop = pos[0] + pos[1] - this.refs.scroll.offsetHeight;
|
||||
}
|
||||
else
|
||||
this.selectOne(nsel);
|
||||
}
|
||||
},
|
||||
selectTo: function(ns)
|
||||
{
|
||||
if (this.lastSel === undefined)
|
||||
return this.selectOne(ns);
|
||||
var sel = { ns: 1 };
|
||||
if (this.lastSel >= this.getTotalItems())
|
||||
this.lastSel = this.getTotalItems()-1;
|
||||
if (ns < this.lastSel)
|
||||
{
|
||||
for (var i = ns; i <= this.lastSel; i++)
|
||||
sel[i] = true;
|
||||
}
|
||||
else if (ns > this.lastSel)
|
||||
{
|
||||
for (var i = this.lastSel; i <= ns; i++)
|
||||
sel[i] = true;
|
||||
}
|
||||
this.setState({ selected: sel });
|
||||
this.curSel = ns;
|
||||
},
|
||||
selectOne: function(ns)
|
||||
{
|
||||
var sel = {};
|
||||
sel[ns] = true;
|
||||
this.setState({ selected: sel });
|
||||
this.lastSel = ns;
|
||||
this.curSel = ns;
|
||||
},
|
||||
onListItemClick: function(ev)
|
||||
{
|
||||
var t = ev.target;
|
||||
while (t && !t.getAttribute('data-i'))
|
||||
t = t.parentNode;
|
||||
if (t)
|
||||
{
|
||||
var ns = parseInt(t.getAttribute('data-i'));
|
||||
if (ev.shiftKey)
|
||||
this.selectTo(ns);
|
||||
else if (ev.ctrlKey)
|
||||
{
|
||||
this.state.selected[ns] = true;
|
||||
this.curSel = ns;
|
||||
this.lastSel = this.lastSel === undefined ? ns : this.lastSel;
|
||||
this.setState({ selected: this.state.selected });
|
||||
}
|
||||
else
|
||||
this.selectOne(ns);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var AttachList = React.createClass({
|
||||
mixins: [ ListWithSelection ],
|
||||
getInitialState: function()
|
||||
{
|
||||
return {
|
||||
|
@ -758,108 +882,29 @@ var AttachList = React.createClass({
|
|||
{
|
||||
this.setState({ attachScroll: ev.target.scrollTop });
|
||||
},
|
||||
attachKeyDown: function(ev)
|
||||
deleteSelected: function()
|
||||
{
|
||||
if (!this.state.attachments.length)
|
||||
return;
|
||||
if (ev.keyCode == 46) // delete
|
||||
{
|
||||
for (var i = this.state.attachments.length-1; i >= 0; i--)
|
||||
if (this.state.attachments[i].selected)
|
||||
this.state.attachments.splice(i, 1);
|
||||
this.setState({ attachments: this.state.attachments });
|
||||
ev.stopPropagation();
|
||||
}
|
||||
else if (ev.keyCode == 38 || ev.keyCode == 40 || ev.keyCode == 33 || ev.keyCode == 34) // up, down, pgup, pgdown
|
||||
{
|
||||
var sel = this.curSel, dir;
|
||||
if (ev.keyCode < 35)
|
||||
dir = (ev.keyCode == 34 ? 1 : -1) * Math.round(this.refs.scroll.offsetHeight / this.refs.a0.offsetHeight);
|
||||
else
|
||||
dir = (ev.keyCode == 40 ? 1 : -1);
|
||||
if (sel !== null)
|
||||
{
|
||||
var nsel = sel+dir;
|
||||
if (nsel < 0)
|
||||
nsel = 0;
|
||||
if (nsel >= this.state.attachments.length)
|
||||
nsel = this.state.attachments.length-1;
|
||||
if (sel != nsel)
|
||||
{
|
||||
if (ev.shiftKey)
|
||||
this.selectTo(nsel);
|
||||
else
|
||||
this.selectOne(nsel);
|
||||
var item = this.refs['a'+nsel];
|
||||
if (item.offsetTop + item.offsetHeight > this.refs.scroll.scrollTop + this.refs.scroll.offsetHeight)
|
||||
this.refs.scroll.scrollTop = item.offsetTop + item.offsetHeight - this.refs.scroll.offsetHeight;
|
||||
else if (item.offsetTop < this.refs.scroll.scrollTop+this.refs.title.offsetHeight)
|
||||
this.refs.scroll.scrollTop = item.offsetTop-this.refs.title.offsetHeight;
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault(); // prevent scroll
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ev.keyCode == 36) // home
|
||||
{
|
||||
if (ev.shiftKey)
|
||||
this.selectTo(0);
|
||||
else
|
||||
this.selectOne(0);
|
||||
}
|
||||
else if (ev.keyCode == 35) // end
|
||||
{
|
||||
if (ev.shiftKey)
|
||||
this.selectTo(this.state.attachments.length-1);
|
||||
else
|
||||
this.selectOne(this.state.attachments.length-1);
|
||||
}
|
||||
},
|
||||
selectTo: function(ns)
|
||||
{
|
||||
if (this.lastSel === undefined)
|
||||
return this.selectOne(ns);
|
||||
if (ns < this.lastSel)
|
||||
{
|
||||
for (var i = 0; i < this.state.attachments.length; i++)
|
||||
this.state.attachments[i].selected = i >= ns && i <= this.lastSel;
|
||||
}
|
||||
else if (ns > this.lastSel)
|
||||
{
|
||||
for (var i = 0; i < this.state.attachments.length; i++)
|
||||
this.state.attachments[i].selected = i >= this.lastSel && i <= ns;
|
||||
}
|
||||
for (var i = this.state.attachments.length-1; i >= 0; i--)
|
||||
if (this.state.selected[i])
|
||||
this.state.attachments.splice(i, 1);
|
||||
this.setState({ attachments: this.state.attachments });
|
||||
this.curSel = ns;
|
||||
},
|
||||
selectOne: function(ns)
|
||||
getTotalItems: function()
|
||||
{
|
||||
for (var i = 0; i < this.state.attachments.length; i++)
|
||||
this.state.attachments[i].selected = (i == ns);
|
||||
this.setState({ attachments: this.state.attachments });
|
||||
this.lastSel = ns;
|
||||
this.curSel = ns;
|
||||
return this.state.attachments.length;
|
||||
},
|
||||
selectAttachment: function(ev)
|
||||
getPageSize: function()
|
||||
{
|
||||
var t = ev.target;
|
||||
while (t && !t.getAttribute('data-i'))
|
||||
t = t.parentNode;
|
||||
if (t)
|
||||
{
|
||||
var ns = parseInt(t.getAttribute('data-i'));
|
||||
if (ev.shiftKey)
|
||||
this.selectTo(ns);
|
||||
else if (ev.ctrlKey)
|
||||
{
|
||||
this.state.attachments[ns].selected = true;
|
||||
this.curSel = ns;
|
||||
this.lastSel = this.lastSel === undefined ? ns : this.lastSel;
|
||||
this.setState({ attachments: this.state.attachments });
|
||||
}
|
||||
else
|
||||
this.selectOne(ns);
|
||||
}
|
||||
return this.refs.a0 ? Math.round(this.refs.scroll.offsetHeight / this.refs.a0.offsetHeight) : 1;
|
||||
},
|
||||
getItemOffset: function(index)
|
||||
{
|
||||
var item = this.refs['a'+index];
|
||||
return [ item.offsetTop, item.offsetHeight ];
|
||||
},
|
||||
getScrollPaddingTop: function()
|
||||
{
|
||||
return this.refs.title.offsetHeight;
|
||||
},
|
||||
render: function()
|
||||
{
|
||||
|
@ -867,7 +912,7 @@ var AttachList = React.createClass({
|
|||
<div className="no-attach" style={this.state.attachments.length ? { display: 'none' } : null}>
|
||||
<input type="file" multiple="multiple" onChange={this.addAttachments} />
|
||||
</div>
|
||||
<div ref="scroll" className="attach-list" tabIndex="1" onScroll={this.scrollAttachList} onKeyDown={this.attachKeyDown}
|
||||
<div ref="scroll" className="attach-list" tabIndex="1" onScroll={this.scrollAttachList} onKeyDown={this.onListKeyDown}
|
||||
style={this.state.attachments.length ? null : { display: 'none' }}>
|
||||
<div ref="title" className="title" style={{ top: this.state.attachScroll }}>
|
||||
<div className="name">Attachment <a className="button">
|
||||
|
@ -878,7 +923,7 @@ var AttachList = React.createClass({
|
|||
</div>
|
||||
{this.state.attachments.map((a, i) =>
|
||||
<div ref={'a'+i} title={a.name+' ('+formatBytes(a.size)+')'} key={'a'+i} data-i={i}
|
||||
className={'attachment'+(a.selected ? ' selected' : '')} onMouseDown={this.selectAttachment}>
|
||||
className={'attachment'+(this.state.selected[i] ? ' selected' : '')} onMouseDown={this.onListItemClick}>
|
||||
<div className="name">{a.name}</div>
|
||||
<div className="size">{formatBytes(a.size)}</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue