Implement DropDownButton component, fix vertical centering of nomail

master
Vitaliy Filippov 2016-06-21 16:57:33 +03:00
parent 0e3572055d
commit 4ee59393d5
3 changed files with 223 additions and 41 deletions

View File

@ -179,6 +179,7 @@ div
animation-iteration-count: 1;
animation-timing-function: linear;
opacity: 1;
display: block;
}
.dropdown.window
@ -1270,20 +1271,18 @@ div
.message-view.no-mail-shown .nomail
{
flex: 1;
display: block;
flex-direction: column;
display: flex;
}
.message-view .nomail .middle-wrap
.message-view .nomail .flex1
{
display: table;
width: 100%;
height: 100%;
flex: 1;
}
.message-view .nomail .middle
{
display: table-cell;
vertical-align: middle;
text-align: center;
}
.message-view .nomail .txt

View File

@ -648,12 +648,12 @@
<a class="button whole show-dropdown settings" data-dropdown="settings"><img src="icons/config.png" />Settings <span class="down"></span></a>
</div>
<div class="nomail">
<div class="middle-wrap">
<div class="middle">
<img src="icons/no_mail.png" />
<div class="txt">No message selected</div>
</div>
<div class="flex1"></div>
<div class="middle">
<img src="icons/no_mail.png" />
<div class="txt">No message selected</div>
</div>
<div class="flex1"></div>
</div>
<div class="headers">
<div class="top">

241
mail.js
View File

@ -1,11 +1,101 @@
function getOffset(elem)
{
if (elem.getBoundingClientRect)
{
var box = elem.getBoundingClientRect();
var body = document.body;
var docElem = document.documentElement;
var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
var clientTop = docElem.clientTop || body.clientTop || 0;
var clientLeft = docElem.clientLeft || body.clientLeft || 0;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
return { top: Math.round(top), left: Math.round(left) };
}
else
{
var top = 0, left = 0;
while(elem)
{
top = top + parseInt(elem.offsetTop);
left = left + parseInt(elem.offsetLeft);
elem = elem.offsetParent;
}
return { top: top, left: left };
}
}
var DropDownBase = {
instances: {},
componentDidMount: function()
{
if (!DropDownBase.setBodyListener)
{
window.addEventListener('click', DropDownBase.hideAll);
DropDownBase.setBodyListener = true;
}
DropDownBase.instances[this.props.id] = this;
},
hideAll: function()
{
for (var i in DropDownBase.instances)
DropDownBase.instances[i].setState({ visible: false });
},
componentWillUnmount: function()
{
delete DropDownBase.instances[this.props.id];
},
getInitialState: function()
{
return { visible: false, top: 0, left: 0, calloutLeft: null, selectedItem: -1 };
},
onClick: function(ev)
{
ev.stopPropagation();
},
hide: function()
{
this.setState({ visible: false });
},
showAt: function(el)
{
// TODO: move on window resize
var p = getOffset(el);
var left = p.left, top = p.top+el.offsetHeight, calloutLeft = null;
this.setState({ visible: true, top: top, left: left, selectedItem: -1 });
this.refs.dd.style.display = 'block';
if (this.props.window)
{
left = Math.round(p.left+el.offsetWidth/2-this.refs.dd.offsetWidth/2);
top = p.top+el.offsetHeight+3;
}
var ww = window.innerWidth || de.clientWidth || db.clientWidth;
var wh = window.innerHeight || de.clientHeight || db.clientHeight;
if (left + this.refs.dd.offsetWidth > ww)
{
left = ww-this.refs.dd.offsetWidth;
calloutLeft = Math.round(p.left+el.offsetWidth/2-left);
}
if (top + this.refs.dd.offsetHeight > wh)
top = wh-this.refs.dd.offsetHeight;
this.setState({ visible: true, top: top, left: left, calloutLeft: calloutLeft });
this.refs.dd.focus();
}
};
var DropDownMenu = React.createClass({
mixins: [ DropDownBase ],
render: function()
{
return <div className="dropdown" id={'dropdown-'+this.props.id} tabindex="1">
var sel = this.state.selectedItem;
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}
onMouseOver={this.onMouseOver}>
{this.props.items.map(function(i, index) {
return (i.split
? <div key={index} className="split"><i></i></div> :
<div key={index} className={'item'+(i.i16 ? ' i16' : '')+(i.disabled ? ' disabled' : '')}>
<div key={index} data-index={index} className={'item'+(i.i16 ? ' i16' : '')+(i.disabled ? ' disabled' : (sel == index ? ' over' : ''))}>
{i.hotkey ? <div className="hotkey">{i.hotkey}</div> : null}
{i.icon ? <img src={'icons/'+i.icon+'.png'} /> : null}
<span>{i.text}</span>
@ -13,6 +103,99 @@ var DropDownMenu = React.createClass({
);
})}
</div>
},
onMouseOver: function(ev)
{
var t = ev.target;
while ((t && t != this.refs.dd) && (!t.className || t.className.substr(0, 4) != 'item'))
t = t.parentNode;
if (t && t != this.refs.dd)
this.setState({ selectedItem: parseInt(t.getAttribute('data-index')) });
},
onKeyDown: function(ev)
{
if (ev.keyCode == 40 || ev.keyCode == 38)
{
var a = ev.keyCode == 40 ? 1 : this.props.items.length-1;
var sel = this.state.selectedItem;
do
{
sel = ((sel+a) % this.props.items.length);
}
while (this.props.items[sel].split && sel != this.state.selectedItem);
this.setState({ selectedItem: sel });
}
else if (ev.keyCode == 10 || ev.keyCode == 13)
this.clickItem();
ev.preventDefault();
ev.stopPropagation();
},
clickItem: function(ev)
{
},
myOnClick: function(ev)
{
if (ev.target.getAttribute('data-index'))
this.clickItem();
this.onClick(ev);
}
});
var DropDownButton = React.createClass({
statics: {
instances: [],
setBodyListener: false,
hideAll: function(ev)
{
for (var i = 0; i < DropDownButton.instances.length; i++)
DropDownButton.instances[i].setState({ pressed: false });
}
},
componentDidMount: function()
{
if (!DropDownButton.setBodyListener)
{
window.addEventListener('click', DropDownButton.hideAll);
DropDownButton.setBodyListener = true;
}
DropDownButton.instances.push(this);
},
componentWillUnmount: function()
{
for (var i = DropDownButton.instances.length-1; i >= 0; i--)
if (DropDownButton.instances[i] == this)
DropDownButton.instances.splice(i, 1);
},
render: function()
{
return <a ref="btn" className={'button show-dropdown '+(this.state.pressed ? 'pressed ' : '')+(this.props.className || '')} onClick={this.onClickButton}>
{this.props.icon ? <img src={'icons/'+this.props.icon+'.png'} /> : null}
{this.props.cssIcon ? <span class="img"></span> : null}
{this.props.text || null}
<span className="down" onClick={this.onClickDown}></span>
</a>
},
getInitialState: function()
{
return { pressed: false };
},
toggle: function()
{
DropDownBase.hideAll();
if (!this.state.pressed)
DropDownBase.instances[this.props.dropdownId].showAt(this.refs.btn);
this.setState({ pressed: !this.state.pressed });
},
onClickButton: function(ev)
{
if (this.props.whole)
this.toggle();
ev.stopPropagation();
},
onClickDown: function(ev)
{
this.toggle();
ev.stopPropagation();
}
});
@ -144,11 +327,13 @@ var ListSortSettings = React.createClass({
});
var ListSortSettingsWindow = React.createClass({
mixins: [ DropDownBase ],
render: function()
{
var sort = this.props.override ? this.props.sorting : this.props.defaultSorting;
return <div className="dropdown window list-sort" id="dropdown-list-sort" tabindex="1">
<div className="callout-top"></div>
return <div ref="dd" onClick={this.onClick} className={'dropdown window list-sort'+(this.state.visible ? ' visible' : '')}
id={'dropdown-'+this.props.id} tabIndex="1" style={{ top: this.state.top, left: this.state.left }}>
<div ref="callout" className="callout-top"></div>
<div className="title">Sorting for {this.props.folder}</div>
<label><input type="checkbox" checked={this.props.override ? "checked" : null} /> Override default sorting</label>
<ListSortSettings className="sorting" sort={sort} />
@ -176,10 +361,11 @@ var ListSortSettingsWindow = React.createClass({
});
var MailSettingsWindow = React.createClass({
mixins: [ DropDownBase ],
render: function()
{
return <div className="dropdown window" id="dropdown-settings" tabindex="1">
<div className="callout-top"></div>
return <div ref="dd" onClick={this.onClick} className="dropdown window" id="dropdown-settings" tabIndex="1">
<div ref="callout" className="callout-top"></div>
<div className="text">Mail Layout</div>
<div className="layouts">
<a className={'button mail-message-on-right'+(this.props.layout == 'r' ? ' selected' : '')} title="List and Message on Right"><span></span></a>
@ -193,12 +379,12 @@ var MailSettingsWindow = React.createClass({
<div className="split"><i></i></div>
<div className="text">Mark as Read</div>
<div className="fields">
<select className="sortby">
<option selected={this.props.markDelay == -1 ? 'selected' : null}>Manually</option>
<option selected={this.props.markDelay == 0 ? 'selected' : null}>On Select</option>
<option selected={this.props.markDelay == 1 ? 'selected' : null}>After 1 second</option>
<option selected={this.props.markDelay == 3 ? 'selected' : null}>After 3 seconds</option>
<option selected={this.props.markDelay == 5 ? 'selected' : null}>After 5 seconds</option>
<select className="sortby" defaultValue={this.props.markDelay}>
<option value="-1">Manually</option>
<option value="0">On Select</option>
<option value="1">After 1 second</option>
<option value="3">After 3 seconds</option>
<option value="5">After 5 seconds</option>
</select>
</div>
</div>
@ -217,6 +403,8 @@ var MailSettingsWindow = React.createClass({
var dropdown_list_sort = React.createElement(
ListSortSettingsWindow, {
id: 'list-sort',
window: true,
folder: 'INBOX',
override: false,
sorting: {},
@ -241,6 +429,8 @@ var dropdown_list_sort = React.createElement(
var dropdown_settings = React.createElement(
MailSettingsWindow, {
id: 'settings',
window: true,
layout: 'r',
showQuickReply: true,
markDelay: -1,
@ -294,10 +484,7 @@ var FolderList = React.createClass({
<div className="bottom-border-gradient"></div>
<div className="actions">
<a className="button"><img src="icons/compose.png" /> Compose</a>
<a data-dropdown="check-send" className="button show-dropdown check-send">
<img src="icons/mail_check_send.png" />
<span className="down"></span>
</a>
<DropDownButton dropdownId="check-send" className="check-send" icon="mail_check_send" />
</div>
<div className="listview">
{this.props.accounts.map(function(account) {
@ -561,18 +748,14 @@ var MessageList = React.createClass({
<div className="searchbox">
<input type="text" placeholder="Search mail" />
</div>
<a data-dropdown="threads" className="button checkable show-dropdown threads" title="Show Message Thread">
<span className="img"></span>
<span className="down"></span>
</a>
<DropDownButton dropdownId="threads" className="threads"
title="Show Message Thread" checkedTitle="Hide Message Thread" cssIcon="1" checkable="1" />
<a className="button whole show-dropdown settings" data-dropdown="settings">
<img src="icons/config.png" />
<span className="down"></span>
</a>
<a data-dropdown="list-sort" className="button show-dropdown whole list-sort">
<img src="icons/list_sort.png" />
<span className="down"></span>
</a>
<DropDownButton dropdownId="list-sort" className="list-sort" whole="1"
title="Sorting settings" icon="list_sort" />
<div className="clear"></div>
</div>
<div className="listview" tabindex="1" onScroll={this.changeFirstDay}>
@ -616,12 +799,12 @@ var MessageView = React.createClass({
<a className="button whole show-dropdown settings" data-dropdown="settings"><img src="icons/config.png" />Settings <span className="down"></span></a>
</div>
<div className="nomail">
<div className="middle-wrap">
<div className="middle">
<img src="icons/no_mail.png" />
<div className="txt">No message selected</div>
</div>
<div className="flex1"></div>
<div className="middle">
<img src="icons/no_mail.png" />
<div className="txt">No message selected</div>
</div>
<div className="flex1"></div>
</div>
{msg ? [
<div className="headers">