add cfg dropdowns, collapse folders handler

master
Vitaliy Filippov 2016-06-21 22:55:28 +03:00
parent 97d7dc3e9f
commit 0f38cac953
2 changed files with 85 additions and 20 deletions

View File

@ -875,11 +875,16 @@ div
border-bottom: 1px solid #dadce0; border-bottom: 1px solid #dadce0;
} }
.message-list .listview .day-list:last-child .thread:last-child .message:last-child .message-list .listview .day-list .thread:last-child .message:last-child
{ {
border-bottom: 0; border-bottom: 0;
} }
.message-list .listview .day-list:last-child .thread:last-child .message:last-child
{
border-bottom: 1px solid #dadce0;
}
.message-list .listview .message .icon .message-list .listview .message .icon
{ {
position: absolute; position: absolute;

98
mail.js
View File

@ -40,7 +40,7 @@ var DropDownBase = {
hideAll: function() hideAll: function()
{ {
for (var i in DropDownBase.instances) for (var i in DropDownBase.instances)
DropDownBase.instances[i].setState({ visible: false }); DropDownBase.instances[i].hide();
}, },
componentWillUnmount: function() componentWillUnmount: function()
{ {
@ -57,9 +57,19 @@ var DropDownBase = {
hide: function() hide: function()
{ {
this.setState({ visible: false }); this.setState({ visible: false });
if (this.onClose)
{
this.onClose();
delete this.onClose;
}
}, },
showAt: function(el) showAt: function(el, onClose)
{ {
if (this.onClose)
{
this.onClose();
delete this.onClose;
}
// TODO: move on window resize // TODO: move on window resize
var p = getOffset(el); var p = getOffset(el);
var left = p.left, top = p.top+el.offsetHeight, calloutLeft = null; var left = p.left, top = p.top+el.offsetHeight, calloutLeft = null;
@ -81,18 +91,23 @@ var DropDownBase = {
top = wh-this.refs.dd.offsetHeight; top = wh-this.refs.dd.offsetHeight;
this.setState({ visible: true, top: top, left: left, calloutLeft: calloutLeft }); this.setState({ visible: true, top: top, left: left, calloutLeft: calloutLeft });
this.refs.dd.focus(); this.refs.dd.focus();
this.onClose = onClose;
} }
}; };
var DropDownMenu = React.createClass({ var DropDownMenu = React.createClass({
mixins: [ DropDownBase ], mixins: [ DropDownBase ],
getInitialState: function()
{
return { items: this.props.items };
},
render: function() render: function()
{ {
var sel = this.state.selectedItem; var sel = this.state.selectedItem;
return <div ref="dd" className={'dropdown'+(this.state.visible ? ' visible' : '')} id={'dropdown-'+this.props.id} return <div ref="dd" className={'dropdown'+(this.state.visible ? ' visible' : '')} id={'dropdown-'+this.props.id}
tabIndex="1" style={{ top: this.state.top, left: this.state.left }} onClick={this.myOnClick} onKeyDown={this.onKeyDown} tabIndex="1" style={{ top: this.state.top, left: this.state.left }} onClick={this.myOnClick} onKeyDown={this.onKeyDown}
onMouseOver={this.onMouseOver}> onMouseOver={this.onMouseOver}>
{this.props.items.map(function(i, index) { {this.state.items.map(function(i, index) {
return (i.split return (i.split
? <div key={index} className="split"><i></i></div> : ? <div key={index} className="split"><i></i></div> :
<div key={index} data-index={index} className={'item'+(i.i16 ? ' i16' : '')+(i.disabled ? ' disabled' : (sel == index ? ' over' : ''))}> <div key={index} data-index={index} className={'item'+(i.i16 ? ' i16' : '')+(i.disabled ? ' disabled' : (sel == index ? ' over' : ''))}>
@ -116,13 +131,13 @@ var DropDownMenu = React.createClass({
{ {
if (ev.keyCode == 40 || ev.keyCode == 38) if (ev.keyCode == 40 || ev.keyCode == 38)
{ {
var a = ev.keyCode == 40 ? 1 : this.props.items.length-1; var a = ev.keyCode == 40 ? 1 : this.state.items.length-1;
var sel = this.state.selectedItem; var sel = this.state.selectedItem;
do do
{ {
sel = ((sel+a) % this.props.items.length); sel = ((sel+a) % this.state.items.length);
} }
while (this.props.items[sel].split && sel != this.state.selectedItem); while (this.state.items[sel].split && sel != this.state.selectedItem);
this.setState({ selectedItem: sel }); this.setState({ selectedItem: sel });
} }
else if (ev.keyCode == 10 || ev.keyCode == 13) else if (ev.keyCode == 10 || ev.keyCode == 13)
@ -457,16 +472,17 @@ var AccountFolders = React.createClass({
render: function() render: function()
{ {
return <div className="account"> return <div className="account">
<div className={"account-header"+(this.state.collapsed ? ' collapsed' : '')}> <div className={"account-header"+(this.state.collapsed ? ' collapsed' : '')} onClick={this.onClick}>
{this.props.name} {this.props.name}
{this.props.accountId ? [ {this.props.accountId ? [
<div key="load" className="loading icon" style={{display: this.props.loading ? '' : 'none'}}></div>, <div key="load" className="loading icon" style={{display: this.props.loading ? '' : 'none'}}></div>,
<div key="warn" className="warning icon" style={{display: this.props.warning ? '' : 'none'}}></div>, <div key="warn" className="warning icon" style={{display: this.props.warning ? '' : 'none'}}></div>,
<div key="cfg" className="cfg"></div> <div key="cfg" className={'cfg'+(this.state.cfgPressed ? ' pressed' : '')} onClick={this.showCfg}></div>
] : null} ] : null}
<div key="n" className="msg-count">{this.props.unreadCount}</div> <div key="n" className="msg-count">{this.props.unreadCount}</div>
</div> </div>
<div className={"account-folders"+(this.props.collapsed ? ' collapsed' : '')}><div className="visible-part"> <div className={"account-folders"+(this.state.collapsed ? ' collapsed' : '')} style={{ height: this.state.h }}>
<div ref="vis" className={'visible-part'+(this.state.animating ? ' animating' : '')}>
{this.props.folders.map((f, i) => {this.props.folders.map((f, i) =>
<div key={'f'+i} className={'folder'+(f.unreadCount > 0 ? ' with-unread' : '')+(f.selected ? ' selected' : '')}> <div key={'f'+i} className={'folder'+(f.unreadCount > 0 ? ' with-unread' : '')+(f.selected ? ' selected' : '')}>
{f.icon ? <img src={'icons/'+f.icon+'.png'} /> : null} {f.icon ? <img src={'icons/'+f.icon+'.png'} /> : null}
@ -475,12 +491,44 @@ var AccountFolders = React.createClass({
{f.unreadCount > 0 ? <div className="msg-count">{f.unreadCount}</div> : null} {f.unreadCount > 0 ? <div className="msg-count">{f.unreadCount}</div> : null}
</div> </div>
)} )}
</div></div> </div>
</div>
</div> </div>
}, },
showCfg: function(ev)
{
var self = this;
var i = DropDownBase.instances.account.state.items;
i[0].text = 'Read '+this.props.name;
DropDownBase.instances.account.setState({ items: i });
DropDownBase.instances.account.showAt(ev.target, function()
{
self.setState({ cfgPressed: false });
});
self.setState({ cfgPressed: true });
ev.stopPropagation();
},
getInitialState: function() getInitialState: function()
{ {
return { collapsed: this.props.collapsed }; return { collapsed: this.props.collapsed, animating: false, h: null, cfgPressed: false };
},
onClick: function()
{
var self = this;
if (this.state.animating)
return;
this.setState({ animating: true, h: this.refs.vis.offsetHeight });
if (!this.state.collapsed)
{
setTimeout(function()
{
self.setState({ h: 0 });
}, 50);
}
setTimeout(function()
{
self.setState({ collapsed: !self.state.collapsed, animating: false, h: null });
}, this.state.collapsed ? 200 : 250);
} }
}); });
@ -592,6 +640,7 @@ var TabPanel = React.createClass({
function formatBytes(s) function formatBytes(s)
{ {
if (!s) return '';
if (s < 1024) return s+' B'; if (s < 1024) return s+' B';
else if (s < 1024*1024) return (Math.round(s*10/1024)/10)+' KB'; else if (s < 1024*1024) return (Math.round(s*10/1024)/10)+' KB';
else if (s < 1024*1024*1024) return (Math.round(s*10/1024/1024)/10)+' MB'; else if (s < 1024*1024*1024) return (Math.round(s*10/1024/1024)/10)+' MB';
@ -600,7 +649,7 @@ function formatBytes(s)
function formatDate(dt) function formatDate(dt)
{ {
if (!dt instanceof Date) if (!(dt instanceof Date))
dt = new Date(dt); dt = new Date(dt);
var tod = new Date(); var tod = new Date();
tod.setHours(0); tod.setHours(0);
@ -619,10 +668,10 @@ function formatDate(dt)
{ {
var wd = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]; var wd = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
var mon = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; var mon = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
return wd[d.getDay()]+' '+d.getDate()+' '+mon[d.getMonth()]; return wd[dt.getDay()]+' '+dt.getDate()+' '+mon[dt.getMonth()];
} }
var h = d.getHours(); var h = dt.getHours();
var m = d.getMinutes(); var m = dt.getMinutes();
return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m; return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m;
} }
@ -694,7 +743,7 @@ var ComposeWindow = React.createClass({
<div className="no-attach" style={this.state.attachments.length ? { display: 'none' } : null}> <div className="no-attach" style={this.state.attachments.length ? { display: 'none' } : null}>
<input type="file" multiple="multiple" onChange={this.addAttachments} /> <input type="file" multiple="multiple" onChange={this.addAttachments} />
</div> </div>
<div className="attach-list" tabindex="1" onScroll={this.scrollAttachList} <div className="attach-list" tabIndex="1" onScroll={this.scrollAttachList}
style={this.state.attachments.length ? null : { display: 'none' }}> style={this.state.attachments.length ? null : { display: 'none' }}>
<div className="title" style={{ top: this.state.attachScroll }}> <div className="title" style={{ top: this.state.attachScroll }}>
<div className="name">Attachment <a className="button"> <div className="name">Attachment <a className="button">
@ -728,7 +777,7 @@ var MessageInList = React.createClass({
return <div className={'message'+(this.msgClasses.map(c => (msg[c] ? ' '+c : '')).join(''))+(msg.thread ? ' thread0' : '')}> return <div className={'message'+(this.msgClasses.map(c => (msg[c] ? ' '+c : '')).join(''))+(msg.thread ? ' thread0' : '')}>
<div className="icon" style={{ width: (20+10*msg.level), backgroundPosition: (10*msg.level)+'px 7px' }}></div> <div className="icon" style={{ width: (20+10*msg.level), backgroundPosition: (10*msg.level)+'px 7px' }}></div>
<div className="subject">{msg.subject}</div> <div className="subject">{msg.subject}</div>
{msg.thread ? <div className={'expand'+(msg.expanded ? ' collapse' : '')}></div> : null} {msg.thread ? <div className={'expand'+(msg.collapsed ? '' : ' collapse')}></div> : null}
<div className="bullet"></div> <div className="bullet"></div>
<div className="from" style={{ left: (21+10*msg.level) }}>{(msg.sent || msg.outgoing ? 'To '+msg.to : msg.from)}</div> <div className="from" style={{ left: (21+10*msg.level) }}>{(msg.sent || msg.outgoing ? 'To '+msg.to : msg.from)}</div>
<div className="size">{formatBytes(msg.size)}</div> <div className="size">{formatBytes(msg.size)}</div>
@ -764,7 +813,7 @@ var MessageList = React.createClass({
title="Sorting settings" icon="list_sort" /> title="Sorting settings" icon="list_sort" />
<div className="clear"></div> <div className="clear"></div>
</div> </div>
<div className="listview" tabindex="1" onScroll={this.changeFirstDay}> <div className="listview" tabIndex="1" onScroll={this.changeFirstDay}>
<div className="day first-day" style={{ top: this.state.firstDayTop }}>{this.props.groups[0].name}</div> <div className="day first-day" style={{ top: this.state.firstDayTop }}>{this.props.groups[0].name}</div>
{this.props.groups.map((grp, i) => [ {this.props.groups.map((grp, i) => [
i > 0 ? <div className="day">{grp.name}</div> : null, i > 0 ? <div className="day">{grp.name}</div> : null,
@ -922,7 +971,18 @@ var composeAccounts = [
var listGroups = [ { var listGroups = [ {
name: 'TODAY', name: 'TODAY',
messages: [] messages: [ {
subject: 'кошку хочешь?))',
sent: true,
to: 'Margarita Philippova',
date: '2016-06-14 21:24',
thread: [ {
subject: 'Re: кошку хочешь?))',
from: 'Margarita Philippova',
date: '2016-06-14 22:31',
level: 1
} ]
} ]
} ]; } ];
ReactDOM.render( ReactDOM.render(