Implement DropDownButton component, fix vertical centering of nomail
parent
0e3572055d
commit
4ee59393d5
13
mail.css
13
mail.css
|
@ -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
|
||||
|
|
10
mail.htm
10
mail.htm
|
@ -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
241
mail.js
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue