Update to ES6 and Webpack
parent
dbe8af8a69
commit
16ec9ec90b
13
.babelrc
13
.babelrc
|
@ -1,15 +1,4 @@
|
||||||
{
|
{
|
||||||
"plugins": [
|
"presets": [ "env", "stage-1", "react" ],
|
||||||
"check-es2015-constants",
|
|
||||||
"transform-es2015-arrow-functions",
|
|
||||||
"transform-es2015-block-scoping",
|
|
||||||
"transform-es2015-classes",
|
|
||||||
"transform-es2015-for-of",
|
|
||||||
"transform-es2015-computed-properties",
|
|
||||||
"transform-es2015-destructuring",
|
|
||||||
"transform-es2015-shorthand-properties",
|
|
||||||
"transform-object-rest-spread",
|
|
||||||
"transform-react-jsx",
|
|
||||||
],
|
|
||||||
"retainLines": true
|
"retainLines": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
module.exports = {
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"APPCFG": false
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-control-regex": [
|
||||||
|
"off"
|
||||||
|
],
|
||||||
|
"no-empty": [
|
||||||
|
"off"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,8 +1,16 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownBase = require('./DropDownBase.js');
|
import DropDownBase from './DropDownBase.js';
|
||||||
|
|
||||||
var AccountFolders = module.exports = React.createClass({
|
export default class AccountFolders extends React.PureComponent
|
||||||
render: function()
|
{
|
||||||
|
state = {
|
||||||
|
collapsed: this.props.collapsed,
|
||||||
|
animating: false,
|
||||||
|
h: null,
|
||||||
|
cfgPressed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
return <div className="account">
|
return <div className="account">
|
||||||
<div className={"account-header"+(this.state.collapsed ? ' collapsed' : '')} onClick={this.onClick}>
|
<div className={"account-header"+(this.state.collapsed ? ' collapsed' : '')} onClick={this.onClick}>
|
||||||
|
@ -28,8 +36,9 @@ var AccountFolders = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
selectFolder: function(ev)
|
|
||||||
|
selectFolder = (ev) =>
|
||||||
{
|
{
|
||||||
var t = ev.target;
|
var t = ev.target;
|
||||||
while (t && !t.getAttribute('data-i') && t != this.refs.vis)
|
while (t && !t.getAttribute('data-i') && t != this.refs.vis)
|
||||||
|
@ -40,8 +49,9 @@ var AccountFolders = module.exports = React.createClass({
|
||||||
this.props.onSelect(this.props.accountIndex, i);
|
this.props.onSelect(this.props.accountIndex, i);
|
||||||
}
|
}
|
||||||
// FIXME: send select event + switch focus to message list if folder changed
|
// FIXME: send select event + switch focus to message list if folder changed
|
||||||
},
|
}
|
||||||
showCfg: function(ev)
|
|
||||||
|
showCfg = (ev) =>
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
var i = DropDownBase.instances.account.state.items;
|
var i = DropDownBase.instances.account.state.items;
|
||||||
|
@ -53,12 +63,9 @@ var AccountFolders = module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
self.setState({ cfgPressed: true });
|
self.setState({ cfgPressed: true });
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
onClick = () =>
|
||||||
return { collapsed: this.props.collapsed, animating: false, h: null, cfgPressed: false };
|
|
||||||
},
|
|
||||||
onClick: function()
|
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
if (this.state.animating)
|
if (this.state.animating)
|
||||||
|
@ -76,4 +83,4 @@ var AccountFolders = module.exports = React.createClass({
|
||||||
self.setState({ collapsed: !self.state.collapsed, animating: false, h: null });
|
self.setState({ collapsed: !self.state.collapsed, animating: false, h: null });
|
||||||
}, this.state.collapsed ? 200 : 250);
|
}, this.state.collapsed ? 200 : 250);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownMenu = require('./DropDownMenu.js');
|
import DropDownMenu from './DropDownMenu.js';
|
||||||
const ListSortSettingsWindow = require('./ListSortSettingsWindow.js');
|
import ListSortSettingsWindow from './ListSortSettingsWindow.js';
|
||||||
const MailSettingsWindow = require('./MailSettingsWindow.js');
|
import MailSettingsWindow from './MailSettingsWindow.js';
|
||||||
|
|
||||||
var dropdown_account = React.createElement(
|
var dropdown_account = React.createElement(
|
||||||
DropDownMenu, {
|
DropDownMenu, {
|
||||||
id: 'account',
|
id: 'account',
|
||||||
|
key: 'account',
|
||||||
items: [ {
|
items: [ {
|
||||||
icon: 'mail_unread',
|
icon: 'mail_unread',
|
||||||
i16: true,
|
i16: true,
|
||||||
|
@ -23,6 +24,7 @@ var dropdown_account = React.createElement(
|
||||||
var dropdown_reply = React.createElement(
|
var dropdown_reply = React.createElement(
|
||||||
DropDownMenu, {
|
DropDownMenu, {
|
||||||
id: 'reply',
|
id: 'reply',
|
||||||
|
key: 'reply',
|
||||||
items: [ {
|
items: [ {
|
||||||
hotkey: 'R',
|
hotkey: 'R',
|
||||||
icon: 'mail_reply',
|
icon: 'mail_reply',
|
||||||
|
@ -41,6 +43,7 @@ var dropdown_reply = React.createElement(
|
||||||
var dropdown_forward = React.createElement(
|
var dropdown_forward = React.createElement(
|
||||||
DropDownMenu, {
|
DropDownMenu, {
|
||||||
id: 'forward',
|
id: 'forward',
|
||||||
|
key: 'forward',
|
||||||
items: [ {
|
items: [ {
|
||||||
hotkey: 'F',
|
hotkey: 'F',
|
||||||
icon: 'mail_forward',
|
icon: 'mail_forward',
|
||||||
|
@ -55,6 +58,7 @@ var dropdown_forward = React.createElement(
|
||||||
var dropdown_delete = React.createElement(
|
var dropdown_delete = React.createElement(
|
||||||
DropDownMenu, {
|
DropDownMenu, {
|
||||||
id: 'delete',
|
id: 'delete',
|
||||||
|
key: 'delete',
|
||||||
items: [ {
|
items: [ {
|
||||||
text: 'Move to Trash'
|
text: 'Move to Trash'
|
||||||
}, {
|
}, {
|
||||||
|
@ -67,6 +71,7 @@ var dropdown_delete = React.createElement(
|
||||||
var dropdown_check_send = React.createElement(
|
var dropdown_check_send = React.createElement(
|
||||||
DropDownMenu, {
|
DropDownMenu, {
|
||||||
id: 'check-send',
|
id: 'check-send',
|
||||||
|
key: 'check-send',
|
||||||
items: [ {
|
items: [ {
|
||||||
hotkey: 'Ctrl-K',
|
hotkey: 'Ctrl-K',
|
||||||
icon: 'mail_check',
|
icon: 'mail_check',
|
||||||
|
@ -90,6 +95,7 @@ var dropdown_check_send = React.createElement(
|
||||||
var dropdown_threads = React.createElement(
|
var dropdown_threads = React.createElement(
|
||||||
DropDownMenu, {
|
DropDownMenu, {
|
||||||
id: 'threads',
|
id: 'threads',
|
||||||
|
key: 'threads',
|
||||||
items: [ {
|
items: [ {
|
||||||
icon: 'thread',
|
icon: 'thread',
|
||||||
text: 'Show Message Thread'
|
text: 'Show Message Thread'
|
||||||
|
@ -111,6 +117,7 @@ var dropdown_threads = React.createElement(
|
||||||
var dropdown_list_sort = React.createElement(
|
var dropdown_list_sort = React.createElement(
|
||||||
ListSortSettingsWindow, {
|
ListSortSettingsWindow, {
|
||||||
id: 'list-sort',
|
id: 'list-sort',
|
||||||
|
key: 'list-sort',
|
||||||
window: true,
|
window: true,
|
||||||
folder: 'INBOX',
|
folder: 'INBOX',
|
||||||
override: false,
|
override: false,
|
||||||
|
@ -134,9 +141,7 @@ var dropdown_list_sort = React.createElement(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
var dropdown_settings = MailSettingsWindow;
|
export default function()
|
||||||
|
|
||||||
module.exports = function()
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
dropdown_account,
|
dropdown_account,
|
||||||
|
@ -146,6 +151,6 @@ module.exports = function()
|
||||||
dropdown_check_send,
|
dropdown_check_send,
|
||||||
dropdown_threads,
|
dropdown_threads,
|
||||||
dropdown_list_sort,
|
dropdown_list_sort,
|
||||||
dropdown_settings
|
<MailSettingsWindow key="mail-settings" />,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const ListWithSelection = require('./ListWithSelection.js');
|
|
||||||
const Util = require('./Util.js');
|
|
||||||
|
|
||||||
var AttachList = module.exports = React.createClass({
|
import ListWithSelection from './ListWithSelection.js';
|
||||||
mixins: [ ListWithSelection ],
|
import Util from './Util.js';
|
||||||
getInitialState: function()
|
|
||||||
{
|
export default class AttachList extends ListWithSelection
|
||||||
return {
|
{
|
||||||
attachments: [],
|
state = {
|
||||||
attachScroll: 0
|
...this.state,
|
||||||
};
|
attachments: [],
|
||||||
},
|
attachScroll: 0
|
||||||
addAttachments: function(ev)
|
}
|
||||||
|
|
||||||
|
addAttachments = (ev) =>
|
||||||
{
|
{
|
||||||
var a = this.state.attachments;
|
var a = this.state.attachments;
|
||||||
if (ev.target.files)
|
if (ev.target.files)
|
||||||
|
@ -20,36 +20,43 @@ var AttachList = module.exports = React.createClass({
|
||||||
this.setState({ attachments: a });
|
this.setState({ attachments: a });
|
||||||
// reset file input
|
// reset file input
|
||||||
ev.target.innerHTML = ev.target.innerHTML;
|
ev.target.innerHTML = ev.target.innerHTML;
|
||||||
},
|
}
|
||||||
scrollAttachList: function(ev)
|
|
||||||
|
scrollAttachList = (ev) =>
|
||||||
{
|
{
|
||||||
this.setState({ attachScroll: ev.target.scrollTop });
|
this.setState({ attachScroll: ev.target.scrollTop });
|
||||||
},
|
}
|
||||||
deleteSelected: function()
|
|
||||||
|
deleteSelected = () =>
|
||||||
{
|
{
|
||||||
for (var i = this.state.attachments.length-1; i >= 0; i--)
|
for (var i = this.state.attachments.length-1; i >= 0; i--)
|
||||||
if (this.state.selected[i])
|
if (this.state.selected[i])
|
||||||
this.state.attachments.splice(i, 1);
|
this.state.attachments.splice(i, 1);
|
||||||
this.setState({ attachments: this.state.attachments });
|
this.setState({ attachments: this.state.attachments });
|
||||||
},
|
}
|
||||||
getTotalItems: function()
|
|
||||||
|
getTotalItems = () =>
|
||||||
{
|
{
|
||||||
return this.state.attachments.length;
|
return this.state.attachments.length;
|
||||||
},
|
}
|
||||||
getPageSize: function()
|
|
||||||
|
getPageSize = () =>
|
||||||
{
|
{
|
||||||
return this.refs.a0 ? Math.round(this.refs.scroll.offsetHeight / this.refs.a0.offsetHeight) : 1;
|
return this.refs.a0 ? Math.round(this.refs.scroll.offsetHeight / this.refs.a0.offsetHeight) : 1;
|
||||||
},
|
}
|
||||||
getItemOffset: function(index)
|
|
||||||
|
getItemOffset = (index) =>
|
||||||
{
|
{
|
||||||
var item = this.refs['a'+index];
|
var item = this.refs['a'+index];
|
||||||
return [ item.offsetTop, item.offsetHeight ];
|
return [ item.offsetTop, item.offsetHeight ];
|
||||||
},
|
}
|
||||||
getScrollPaddingTop: function()
|
|
||||||
|
getScrollPaddingTop = () =>
|
||||||
{
|
{
|
||||||
return this.refs.title.offsetHeight;
|
return this.refs.title.offsetHeight;
|
||||||
},
|
}
|
||||||
render: function()
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
return <div className="attach">
|
return <div className="attach">
|
||||||
<div className="no-attach" style={this.state.attachments.length ? { display: 'none' } : null}>
|
<div className="no-attach" style={this.state.attachments.length ? { display: 'none' } : null}>
|
||||||
|
@ -74,4 +81,4 @@ var AttachList = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
106
ComposeWindow.js
106
ComposeWindow.js
|
@ -1,63 +1,63 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const AttachList = require('./AttachList.js');
|
import AttachList from './AttachList.js';
|
||||||
const StoreListener = require('./StoreListener.js');
|
import StoreListener from './StoreListener.js';
|
||||||
|
|
||||||
var ComposeWindow = React.createClass({
|
class ComposeWindow extends React.PureComponent
|
||||||
getInitialState: function()
|
{
|
||||||
{
|
state = {
|
||||||
return {
|
text: ''
|
||||||
text: ''
|
}
|
||||||
};
|
|
||||||
},
|
changeText = (ev) =>
|
||||||
changeText: function(ev)
|
|
||||||
{
|
{
|
||||||
this.setState({ text: ev.target.value });
|
this.setState({ text: ev.target.value });
|
||||||
},
|
}
|
||||||
render: function()
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
return <div className="compose">
|
return (<div className="compose">
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<a className="button"><img src="icons/mail_send.png" />Send</a>
|
<a className="button"><img src="icons/mail_send.png" />Send</a>
|
||||||
<a className="button"><img src="icons/delete.png" /></a>
|
<a className="button"><img src="icons/delete.png" /></a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="headers">
|
<div className="headers">
|
||||||
<div className="headers-table">
|
<div className="headers-table">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="field">From</div>
|
<div className="field">From</div>
|
||||||
<div className="value">
|
<div className="value">
|
||||||
<select>
|
<select>
|
||||||
{this.props.accounts.filter(a => a.accountId).map((a, i) =>
|
{this.props.accounts.filter(a => a.accountId).map((a, i) =>
|
||||||
<option key={'a'+a.accountId}>{'"'+a.name+'" <'+a.email+'>'}</option>
|
<option key={'a'+a.accountId}>{'"'+a.name+'" <'+a.email+'>'}</option>
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="header">
|
||||||
|
<div className="field">To</div>
|
||||||
|
<div className="value"><input /></div>
|
||||||
|
</div>
|
||||||
|
<div className="header">
|
||||||
|
<div className="field">Cc</div>
|
||||||
|
<div className="value"><input /></div>
|
||||||
|
</div>
|
||||||
|
<div className="header">
|
||||||
|
<div className="field">Bcc</div>
|
||||||
|
<div className="value"><input /></div>
|
||||||
|
</div>
|
||||||
|
<div className="header">
|
||||||
|
<div className="field">Subject</div>
|
||||||
|
<div className="value"><input /></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="header">
|
<AttachList />
|
||||||
<div className="field">To</div>
|
</div>
|
||||||
<div className="value"><input /></div>
|
<div className="text">
|
||||||
</div>
|
<textarea onChange={this.changeText} defaultValue={this.state.text}></textarea>
|
||||||
<div className="header">
|
|
||||||
<div className="field">Cc</div>
|
|
||||||
<div className="value"><input /></div>
|
|
||||||
</div>
|
|
||||||
<div className="header">
|
|
||||||
<div className="field">Bcc</div>
|
|
||||||
<div className="value"><input /></div>
|
|
||||||
</div>
|
|
||||||
<div className="header">
|
|
||||||
<div className="field">Subject</div>
|
|
||||||
<div className="value"><input /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<AttachList />
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text">
|
</div>);
|
||||||
<textarea onChange={this.changeText} defaultValue={this.state.text}></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
module.exports = StoreListener(ComposeWindow, (data) => { return { accounts: data.accounts }; });
|
export default StoreListener(ComposeWindow, (data) => { return { accounts: data.accounts }; });
|
||||||
|
|
109
DropDownBase.js
109
DropDownBase.js
|
@ -1,35 +1,13 @@
|
||||||
function getOffset(elem)
|
import React from 'react';
|
||||||
{
|
|
||||||
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 = module.exports = {
|
export default class DropDownBase extends React.PureComponent
|
||||||
instances: {},
|
{
|
||||||
currentVisible: null,
|
static instances = {};
|
||||||
componentDidMount: function()
|
static currentVisible = null;
|
||||||
|
|
||||||
|
state = { visible: false, top: 0, left: 0, calloutLeft: null, selectedItem: -1 };
|
||||||
|
|
||||||
|
componentDidMount()
|
||||||
{
|
{
|
||||||
if (!DropDownBase.setBodyListener)
|
if (!DropDownBase.setBodyListener)
|
||||||
{
|
{
|
||||||
|
@ -38,36 +16,38 @@ var DropDownBase = module.exports = {
|
||||||
DropDownBase.setBodyListener = true;
|
DropDownBase.setBodyListener = true;
|
||||||
}
|
}
|
||||||
DropDownBase.instances[this.props.id] = this;
|
DropDownBase.instances[this.props.id] = this;
|
||||||
},
|
}
|
||||||
hideAll: function()
|
|
||||||
|
static hideAll()
|
||||||
{
|
{
|
||||||
for (var i in DropDownBase.instances)
|
for (var i in DropDownBase.instances)
|
||||||
DropDownBase.instances[i].hide();
|
DropDownBase.instances[i].hide();
|
||||||
},
|
}
|
||||||
repositionCurrent: function()
|
|
||||||
|
static repositionCurrent()
|
||||||
{
|
{
|
||||||
if (DropDownBase.currentVisible)
|
if (DropDownBase.currentVisible)
|
||||||
DropDownBase.currentVisible[0].showAt(DropDownBase.currentVisible[1], DropDownBase.currentVisible[0].onClose);
|
DropDownBase.currentVisible[0].showAt(DropDownBase.currentVisible[1], DropDownBase.currentVisible[0].onClose);
|
||||||
},
|
}
|
||||||
componentWillUnmount: function()
|
|
||||||
|
componentWillUnmount()
|
||||||
{
|
{
|
||||||
delete DropDownBase.instances[this.props.id];
|
delete DropDownBase.instances[this.props.id];
|
||||||
if (DropDownBase.currentVisible[0] == this)
|
if (DropDownBase.currentVisible[0] == this)
|
||||||
DropDownBase.currentVisible = null;
|
DropDownBase.currentVisible = null;
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
onClick = (ev) =>
|
||||||
return { visible: false, top: 0, left: 0, calloutLeft: null, selectedItem: -1 };
|
|
||||||
},
|
|
||||||
onClick: function(ev)
|
|
||||||
{
|
{
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
}
|
||||||
isVisible: function()
|
|
||||||
|
isVisible = () =>
|
||||||
{
|
{
|
||||||
return this.state.visible;
|
return this.state.visible;
|
||||||
},
|
}
|
||||||
hide: function()
|
|
||||||
|
hide = () =>
|
||||||
{
|
{
|
||||||
this.setState({ visible: false });
|
this.setState({ visible: false });
|
||||||
DropDownBase.currentVisible = null;
|
DropDownBase.currentVisible = null;
|
||||||
|
@ -76,8 +56,9 @@ var DropDownBase = module.exports = {
|
||||||
this.onClose();
|
this.onClose();
|
||||||
delete this.onClose;
|
delete this.onClose;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
showAt: function(el, onClose)
|
|
||||||
|
showAt = (el, onClose) =>
|
||||||
{
|
{
|
||||||
if (this.onClose && this.onClose != onClose)
|
if (this.onClose && this.onClose != onClose)
|
||||||
{
|
{
|
||||||
|
@ -108,4 +89,32 @@ var DropDownBase = module.exports = {
|
||||||
this.refs.dd.focus();
|
this.refs.dd.focus();
|
||||||
this.onClose = onClose;
|
this.onClose = onClose;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownBase = require('./DropDownBase.js');
|
|
||||||
|
|
||||||
var DropDownButton = module.exports = React.createClass({
|
import DropDownBase from './DropDownBase.js';
|
||||||
componentDidUpdate: function(prevProps, prevState)
|
|
||||||
|
export default class DropDownButton extends React.PureComponent
|
||||||
|
{
|
||||||
|
state = { pressed: false, checked: false }
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState)
|
||||||
{
|
{
|
||||||
if (prevProps.hidden && !this.props.hidden &&
|
if (prevProps.hidden && !this.props.hidden &&
|
||||||
DropDownBase.instances[this.props.dropdownId].isVisible())
|
DropDownBase.instances[this.props.dropdownId].isVisible())
|
||||||
{
|
{
|
||||||
DropDownBase.instances[this.props.dropdownId].showAt(this.refs.btn, this.unpress);
|
DropDownBase.instances[this.props.dropdownId].showAt(this.refs.btn, this.unpress);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
render: function()
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
return <a ref="btn" title={(this.state.checked ? this.props.checkedTitle : null) || this.props.title} onClick={this.onClickButton}
|
return <a ref="btn" title={(this.state.checked ? this.props.checkedTitle : null) || this.props.title} onClick={this.onClickButton}
|
||||||
className={'button '+(this.props.dropdownId ? 'show-dropdown ' : '')+(this.state.checked ? 'checked ' : '')+
|
className={'button '+(this.props.dropdownId ? 'show-dropdown ' : '')+(this.state.checked ? 'checked ' : '')+
|
||||||
|
@ -20,12 +25,9 @@ var DropDownButton = module.exports = React.createClass({
|
||||||
{this.state.checked && this.props.checkedText || this.props.text || null}
|
{this.state.checked && this.props.checkedText || this.props.text || null}
|
||||||
{this.props.dropdownId ? <span className="down" onClick={this.onClickDown}></span> : null}
|
{this.props.dropdownId ? <span className="down" onClick={this.onClickDown}></span> : null}
|
||||||
</a>
|
</a>
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
toggle = () =>
|
||||||
return { pressed: false, checked: false };
|
|
||||||
},
|
|
||||||
toggle: function()
|
|
||||||
{
|
{
|
||||||
if (!this.state.pressed)
|
if (!this.state.pressed)
|
||||||
{
|
{
|
||||||
|
@ -35,12 +37,14 @@ var DropDownButton = module.exports = React.createClass({
|
||||||
else
|
else
|
||||||
DropDownBase.instances[this.props.dropdownId].hide();
|
DropDownBase.instances[this.props.dropdownId].hide();
|
||||||
this.setState({ pressed: !this.state.pressed });
|
this.setState({ pressed: !this.state.pressed });
|
||||||
},
|
}
|
||||||
unpress: function()
|
|
||||||
|
unpress = () =>
|
||||||
{
|
{
|
||||||
this.setState({ pressed: false });
|
this.setState({ pressed: false });
|
||||||
},
|
}
|
||||||
onClickButton: function(ev)
|
|
||||||
|
onClickButton = (ev) =>
|
||||||
{
|
{
|
||||||
if (this.props.whole || this.props.checkable && this.state.pressed)
|
if (this.props.whole || this.props.checkable && this.state.pressed)
|
||||||
this.toggle();
|
this.toggle();
|
||||||
|
@ -53,10 +57,11 @@ var DropDownButton = module.exports = React.createClass({
|
||||||
else if (this.props.onClick)
|
else if (this.props.onClick)
|
||||||
this.props.onClick();
|
this.props.onClick();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
}
|
||||||
onClickDown: function(ev)
|
|
||||||
|
onClickDown = (ev) =>
|
||||||
{
|
{
|
||||||
this.toggle();
|
this.toggle();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownBase = require('./DropDownBase.js');
|
|
||||||
|
|
||||||
var DropDownMenu = module.exports = React.createClass({
|
import DropDownBase from './DropDownBase.js';
|
||||||
mixins: [ DropDownBase ],
|
|
||||||
getInitialState: function()
|
export default class DropDownMenu extends DropDownBase
|
||||||
{
|
{
|
||||||
return { items: this.props.items };
|
state = { items: this.props.items }
|
||||||
},
|
|
||||||
render: function()
|
render()
|
||||||
{
|
{
|
||||||
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}
|
||||||
|
@ -24,16 +23,18 @@ var DropDownMenu = module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
onMouseOver: function(ev)
|
|
||||||
|
onMouseOver = (ev) =>
|
||||||
{
|
{
|
||||||
var t = ev.target;
|
var t = ev.target;
|
||||||
while ((t && t != this.refs.dd) && (!t.className || t.className.substr(0, 4) != 'item'))
|
while ((t && t != this.refs.dd) && (!t.className || t.className.substr(0, 4) != 'item'))
|
||||||
t = t.parentNode;
|
t = t.parentNode;
|
||||||
if (t && t != this.refs.dd)
|
if (t && t != this.refs.dd)
|
||||||
this.setState({ selectedItem: parseInt(t.getAttribute('data-index')) });
|
this.setState({ selectedItem: parseInt(t.getAttribute('data-index')) });
|
||||||
},
|
}
|
||||||
onKeyDown: function(ev)
|
|
||||||
|
onKeyDown = (ev) =>
|
||||||
{
|
{
|
||||||
if (ev.keyCode == 40 || ev.keyCode == 38)
|
if (ev.keyCode == 40 || ev.keyCode == 38)
|
||||||
{
|
{
|
||||||
|
@ -50,14 +51,16 @@ var DropDownMenu = module.exports = React.createClass({
|
||||||
this.clickItem();
|
this.clickItem();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
}
|
||||||
clickItem: function(ev)
|
|
||||||
|
clickItem = (ev) =>
|
||||||
{
|
{
|
||||||
},
|
}
|
||||||
myOnClick: function(ev)
|
|
||||||
|
myOnClick = (ev) =>
|
||||||
{
|
{
|
||||||
if (ev.target.getAttribute('data-index'))
|
if (ev.target.getAttribute('data-index'))
|
||||||
this.clickItem();
|
this.clickItem();
|
||||||
this.onClick(ev);
|
this.onClick(ev);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const AccountFolders = require('./AccountFolders.js');
|
import AccountFolders from './AccountFolders.js';
|
||||||
const DropDownButton = require('./DropDownButton.js');
|
import DropDownButton from './DropDownButton.js';
|
||||||
const Store = require('./Store.js');
|
import Store from './Store.js';
|
||||||
const StoreListener = require('./StoreListenerClass.js');
|
import StoreListener from './StoreListener.js';
|
||||||
const MailProgress = require('./MailProgress.js');
|
import MailProgress from './MailProgress.js';
|
||||||
|
|
||||||
var FolderList = React.createClass({
|
class FolderList extends React.PureComponent
|
||||||
render: function()
|
{
|
||||||
|
state = { selectedAccount: -1, selectedFolder: -1 }
|
||||||
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
return <div className={"folder-list"+(self.props.progressText ? ' progress-visible' : '')}>
|
return (<div className={"folder-list"+(self.props.progressText ? ' progress-visible' : '')}>
|
||||||
<div className="top-border-gradient"></div>
|
<div className="top-border-gradient"></div>
|
||||||
<div className="bottom-border-gradient"></div>
|
<div className="bottom-border-gradient"></div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
|
@ -24,13 +27,15 @@ var FolderList = React.createClass({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<MailProgress />
|
<MailProgress />
|
||||||
</div>
|
</div>)
|
||||||
},
|
}
|
||||||
onClickCheckSend: function()
|
|
||||||
|
onClickCheckSend = () =>
|
||||||
{
|
{
|
||||||
Store.startResync();
|
Store.startResync();
|
||||||
},
|
}
|
||||||
onSelectFolder: function(accIndex, folderIndex)
|
|
||||||
|
onSelectFolder = (accIndex, folderIndex) =>
|
||||||
{
|
{
|
||||||
var acc = this.props.accounts[accIndex];
|
var acc = this.props.accounts[accIndex];
|
||||||
var folder = this.props.accounts[accIndex].folders[folderIndex];
|
var folder = this.props.accounts[accIndex].folders[folderIndex];
|
||||||
|
@ -42,11 +47,7 @@ var FolderList = React.createClass({
|
||||||
Store.loadFolder({ accountId: acc.accountId, folderType: folder.type });
|
Store.loadFolder({ accountId: acc.accountId, folderType: folder.type });
|
||||||
}
|
}
|
||||||
this.setState({ selectedAccount: accIndex, selectedFolder: folderIndex });
|
this.setState({ selectedAccount: accIndex, selectedFolder: folderIndex });
|
||||||
},
|
|
||||||
getInitialState: function()
|
|
||||||
{
|
|
||||||
return { selectedAccount: -1, selectedFolder: -1 };
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
module.exports = StoreListener(FolderList, (data) => { return { accounts: data.accounts, progressText: data.progressText }; });
|
export default StoreListener(FolderList, (data) => { return { accounts: data.accounts, progressText: data.progressText }; });
|
||||||
|
|
2
I18n.js
2
I18n.js
|
@ -1,4 +1,4 @@
|
||||||
module.exports = function(msg)
|
export default function(msg)
|
||||||
{
|
{
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
|
|
||||||
var ListSortSettings = module.exports = React.createClass({
|
export default class ListSortSettings extends React.PureComponent
|
||||||
render: function()
|
{
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
return <div className={this.props.className} value={this.props.sort.sortby}>
|
return <div className={this.props.className} value={this.props.sort.sortby}>
|
||||||
<select className="sortby">
|
<select className="sortby">
|
||||||
|
@ -19,4 +20,4 @@ var ListSortSettings = module.exports = React.createClass({
|
||||||
<label><input type="checkbox" checked={this.props.sort.threaded} /> Threaded</label>
|
<label><input type="checkbox" checked={this.props.sort.threaded} /> Threaded</label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownBase = require('./DropDownBase.js');
|
|
||||||
const ListSortSettings = require('./ListSortSettings.js');
|
|
||||||
|
|
||||||
var ListSortSettingsWindow = module.exports = React.createClass({
|
import DropDownBase from './DropDownBase.js';
|
||||||
mixins: [ DropDownBase ],
|
import ListSortSettings from './ListSortSettings.js';
|
||||||
render: function()
|
|
||||||
|
export default class ListSortSettingsWindow extends DropDownBase
|
||||||
|
{
|
||||||
|
state = { checksVisible: false }
|
||||||
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
var sort = this.props.override ? this.props.sorting : this.props.defaultSorting;
|
var sort = this.props.override ? this.props.sorting : this.props.defaultSorting;
|
||||||
return <div ref="dd" onClick={this.onClick} className={'dropdown window list-sort'+(this.state.visible ? ' visible' : '')}
|
return <div ref="dd" onClick={this.onClick} className={'dropdown window list-sort'+(this.state.visible ? ' visible' : '')}
|
||||||
|
@ -25,13 +28,10 @@ var ListSortSettingsWindow = module.exports = React.createClass({
|
||||||
<label><input type="checkbox" checked={this.props.show.dups ? "checked" : null} /> Show Duplicates</label>
|
<label><input type="checkbox" checked={this.props.show.dups ? "checked" : null} /> Show Duplicates</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
expandChecks = () =>
|
||||||
return { checksVisible: false };
|
|
||||||
},
|
|
||||||
expandChecks: function()
|
|
||||||
{
|
{
|
||||||
this.setState({ checksVisible: !this.state.checksVisible });
|
this.setState({ checksVisible: !this.state.checksVisible });
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
// Common selection mixin
|
import React from 'react';
|
||||||
var ListWithSelection = module.exports = {
|
|
||||||
|
// 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()
|
// requires to override methods: this.deleteSelected(), this.getPageSize(), this.getItemOffset(index), this.getTotalItems()
|
||||||
getInitialState: function()
|
constructor(props)
|
||||||
{
|
{
|
||||||
return {
|
super(props);
|
||||||
selected: {}
|
this.state = this.state||{};
|
||||||
};
|
this.state.selected = {};
|
||||||
},
|
}
|
||||||
isSelected: function(i)
|
|
||||||
|
isSelected(i)
|
||||||
{
|
{
|
||||||
return this.state.selected[i] || this.state.selected.begin !== undefined &&
|
return this.state.selected[i] || this.state.selected.begin !== undefined &&
|
||||||
this.state.selected.begin <= i && this.state.selected.end >= i;
|
this.state.selected.begin <= i && this.state.selected.end >= i;
|
||||||
},
|
}
|
||||||
onListKeyDown: function(ev)
|
|
||||||
|
onListKeyDown = (ev) =>
|
||||||
{
|
{
|
||||||
if (!this.getTotalItems())
|
if (!this.getTotalItems())
|
||||||
return;
|
return;
|
||||||
|
@ -74,8 +80,9 @@ var ListWithSelection = module.exports = {
|
||||||
else
|
else
|
||||||
this.selectOne(nsel);
|
this.selectOne(nsel);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
selectTo: function(ns)
|
|
||||||
|
selectTo(ns)
|
||||||
{
|
{
|
||||||
if (this.lastSel === undefined)
|
if (this.lastSel === undefined)
|
||||||
return this.selectOne(ns);
|
return this.selectOne(ns);
|
||||||
|
@ -93,8 +100,9 @@ var ListWithSelection = module.exports = {
|
||||||
this.curSel = ns;
|
this.curSel = ns;
|
||||||
if (this.onSelectCurrent)
|
if (this.onSelectCurrent)
|
||||||
this.onSelectCurrent(ns);
|
this.onSelectCurrent(ns);
|
||||||
},
|
}
|
||||||
selectOne: function(ns)
|
|
||||||
|
selectOne(ns)
|
||||||
{
|
{
|
||||||
var sel = {};
|
var sel = {};
|
||||||
sel[ns] = true;
|
sel[ns] = true;
|
||||||
|
@ -103,8 +111,9 @@ var ListWithSelection = module.exports = {
|
||||||
this.curSel = ns;
|
this.curSel = ns;
|
||||||
if (this.onSelectCurrent)
|
if (this.onSelectCurrent)
|
||||||
this.onSelectCurrent(ns);
|
this.onSelectCurrent(ns);
|
||||||
},
|
}
|
||||||
onListItemClick: function(ev)
|
|
||||||
|
onListItemClick = (ev) =>
|
||||||
{
|
{
|
||||||
var t = ev.target;
|
var t = ev.target;
|
||||||
while (t && !t.getAttribute('data-i'))
|
while (t && !t.getAttribute('data-i'))
|
||||||
|
@ -127,4 +136,4 @@ var ListWithSelection = module.exports = {
|
||||||
this.selectOne(ns);
|
this.selectOne(ns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const StoreListener = require('./StoreListenerClass.js');
|
import StoreListener from './StoreListener.js';
|
||||||
const ProgressBar = require('./ProgressBar.js');
|
import ProgressBar from './ProgressBar.js';
|
||||||
|
|
||||||
module.exports = StoreListener(ProgressBar, function(data)
|
export default StoreListener(ProgressBar, function(data)
|
||||||
{
|
{
|
||||||
return { text: data.progressText, progress: data.progressPercent };
|
return { text: data.progressText, progress: data.progressPercent };
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownBase = require('./DropDownBase.js');
|
import DropDownBase from './DropDownBase.js';
|
||||||
const ListSortSettings = require('./ListSortSettings.js');
|
import ListSortSettings from './ListSortSettings.js';
|
||||||
const Store = require('./Store.js');
|
import Store from './Store.js';
|
||||||
const StoreListener = require('./StoreListener.js');
|
import StoreListener from './StoreListener.js';
|
||||||
|
|
||||||
var MailSettingsWindow = React.createClass({
|
class MailSettingsWindow extends DropDownBase
|
||||||
mixins: [ DropDownBase ],
|
{
|
||||||
render: function()
|
render()
|
||||||
{
|
{
|
||||||
return <div ref="dd" onClick={this.onClick} className={'dropdown window'+(this.state.visible ? ' visible' : '')}
|
return <div ref="dd" onClick={this.onClick} className={'dropdown window'+(this.state.visible ? ' visible' : '')}
|
||||||
id={'dropdown-'+this.props.id} tabIndex="1" style={{ top: this.state.top, left: this.state.left }}>
|
id={'dropdown-'+this.props.id} tabIndex="1" style={{ top: this.state.top, left: this.state.left }}>
|
||||||
|
@ -35,20 +35,22 @@ var MailSettingsWindow = React.createClass({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
switchLayout: function(ev)
|
|
||||||
|
switchLayout = (ev) =>
|
||||||
{
|
{
|
||||||
var t = ev.target.nodeName == 'A' ? ev.target : ev.target.parentNode;
|
var t = ev.target.nodeName == 'A' ? ev.target : ev.target.parentNode;
|
||||||
var l = / mail-(\S+)/.exec(t.className)[1];
|
var l = / mail-(\S+)/.exec(t.className)[1];
|
||||||
Store.set('layout', l);
|
Store.set('layout', l);
|
||||||
},
|
}
|
||||||
showQuickReply: function()
|
|
||||||
|
showQuickReply = () =>
|
||||||
{
|
{
|
||||||
Store.set('quickReply', !this.props.quickReply);
|
Store.set('quickReply', !this.props.quickReply);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
module.exports = StoreListener(
|
export default StoreListener(
|
||||||
MailSettingsWindow,
|
MailSettingsWindow,
|
||||||
(data) => { return { layout: data.layout, quickReply: data.quickReply }; },
|
(data) => { return { layout: data.layout, quickReply: data.quickReply }; },
|
||||||
{
|
{
|
||||||
|
|
114
MessageList.js
114
MessageList.js
|
@ -1,13 +1,15 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownButton = require('./DropDownButton.js');
|
import DropDownButton from './DropDownButton.js';
|
||||||
const ListWithSelection = require('./ListWithSelection.js');
|
import ListWithSelection from './ListWithSelection.js';
|
||||||
const Store = require('./Store.js');
|
import Store from './Store.js';
|
||||||
const StoreListener = require('./StoreListener.js');
|
import StoreListener from './StoreListener.js';
|
||||||
const Util = require('./Util.js');
|
import Util from './Util.js';
|
||||||
|
|
||||||
var MessageInList = React.createClass({
|
class MessageInList extends React.PureComponent
|
||||||
msgClasses: { unread: 'unread', unseen: 'unseen', answered: 'replied', flagged: 'pinned', sent: 'sent' },
|
{
|
||||||
render: function()
|
msgClasses = { unread: 'unread', unseen: 'unseen', answered: 'replied', flagged: 'pinned', sent: 'sent' }
|
||||||
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
var msg = this.props.msg;
|
var msg = this.props.msg;
|
||||||
return <div data-i={this.props.i} className={'message'+
|
return <div data-i={this.props.i} className={'message'+
|
||||||
|
@ -29,26 +31,26 @@ var MessageInList = React.createClass({
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// TODO: expand/collapse days
|
// TODO: expand/collapse days
|
||||||
var MessageList = React.createClass({
|
class MessageList extends ListWithSelection
|
||||||
mixins: [ ListWithSelection ],
|
{
|
||||||
_preloadSize: 20,
|
_preloadSize = 20
|
||||||
_pageSize: 50,
|
_pageSize = 50
|
||||||
getInitialState: function()
|
state = {
|
||||||
{
|
...this.state,
|
||||||
return {
|
firstDayTop: 0,
|
||||||
firstDayTop: 0,
|
firstDay: this.props.groups && this.props.groups[0] && this.props.groups[0].name || null
|
||||||
firstDay: this.props.groups && this.props.groups[0] && this.props.groups[0].name || null
|
}
|
||||||
};
|
|
||||||
},
|
componentWillReceiveProps(nextProps)
|
||||||
componentWillReceiveProps: function(nextProps)
|
|
||||||
{
|
{
|
||||||
this.setFirstDayFromProps(nextProps);
|
this.setFirstDayFromProps(nextProps);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Main virtual scroll detector method
|
// Main virtual scroll detector method
|
||||||
setFirstDayFromProps: function(props)
|
setFirstDayFromProps(props)
|
||||||
{
|
{
|
||||||
var groups = props.groups;
|
var groups = props.groups;
|
||||||
var messages = props.messages;
|
var messages = props.messages;
|
||||||
|
@ -118,16 +120,19 @@ var MessageList = React.createClass({
|
||||||
Store.loadMessages(loadFirst, loadFirstEnd-loadFirst);
|
Store.loadMessages(loadFirst, loadFirstEnd-loadFirst);
|
||||||
if (loadFirstEnd < loadLastStart && loadLastStart < loadLast)
|
if (loadFirstEnd < loadLastStart && loadLastStart < loadLast)
|
||||||
Store.loadMessages(loadLastStart, loadLast-loadLastStart);
|
Store.loadMessages(loadLastStart, loadLast-loadLastStart);
|
||||||
},
|
}
|
||||||
changeFirstDay: function(ev)
|
|
||||||
|
changeFirstDay = (ev) =>
|
||||||
{
|
{
|
||||||
this.setFirstDayFromProps(this.props);
|
this.setFirstDayFromProps(this.props);
|
||||||
},
|
}
|
||||||
deleteSelected: function()
|
|
||||||
|
deleteSelected = () =>
|
||||||
{
|
{
|
||||||
|
|
||||||
},
|
}
|
||||||
onSelectCurrent: function(index)
|
|
||||||
|
onSelectCurrent = (index) =>
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
var total = 0, p, msg, idx;
|
var total = 0, p, msg, idx;
|
||||||
|
@ -155,8 +160,9 @@ var MessageList = React.createClass({
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getTotalItems: function()
|
|
||||||
|
getTotalItems = () =>
|
||||||
{
|
{
|
||||||
var total = -1; // do not count first-day as item
|
var total = -1; // do not count first-day as item
|
||||||
for (var i = 0; i < (this.props.groups||[]).length; i++)
|
for (var i = 0; i < (this.props.groups||[]).length; i++)
|
||||||
|
@ -164,12 +170,14 @@ var MessageList = React.createClass({
|
||||||
total += 1+this.props.groups[i].messageCount;
|
total += 1+this.props.groups[i].messageCount;
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
},
|
}
|
||||||
getPageSize: function()
|
|
||||||
|
getPageSize = () =>
|
||||||
{
|
{
|
||||||
return Math.floor(this.refs.scroll.offsetHeight / (this.props.layout == 'message-on-right' ? 60 : 30));
|
return Math.floor(this.refs.scroll.offsetHeight / (this.props.layout == 'message-on-right' ? 60 : 30));
|
||||||
},
|
}
|
||||||
getItemOffset: function(index)
|
|
||||||
|
getItemOffset = (index) =>
|
||||||
{
|
{
|
||||||
var n = 0, top = 0, p;
|
var n = 0, top = 0, p;
|
||||||
var h = (this.props.layout == 'message-on-right' ? 60 : 30);
|
var h = (this.props.layout == 'message-on-right' ? 60 : 30);
|
||||||
|
@ -185,28 +193,32 @@ var MessageList = React.createClass({
|
||||||
top += (i > 0 ? 30 : 0) + h*this.props.groups[i].messageCount;
|
top += (i > 0 ? 30 : 0) + h*this.props.groups[i].messageCount;
|
||||||
}
|
}
|
||||||
return [ top, index == p && i > 0 ? 30 : h ];
|
return [ top, index == p && i > 0 ? 30 : h ];
|
||||||
},
|
}
|
||||||
getScrollPaddingTop: function()
|
|
||||||
|
getScrollPaddingTop = () =>
|
||||||
{
|
{
|
||||||
return this.refs.title.offsetHeight;
|
return this.refs.title.offsetHeight;
|
||||||
},
|
}
|
||||||
getMessages: function(grp, start, end)
|
|
||||||
|
getMessages = (grp, start, end) =>
|
||||||
{
|
{
|
||||||
var a = this.props.messages.slice(grp.start+start, grp.start+end);
|
var a = this.props.messages.slice(grp.start+start, grp.start+end);
|
||||||
for (var i = 0; i < end-start; i++)
|
for (var i = 0; i < end-start; i++)
|
||||||
if (!a[i])
|
if (!a[i])
|
||||||
a[i] = null;
|
a[i] = null;
|
||||||
return a;
|
return a;
|
||||||
},
|
}
|
||||||
onSearchTextChange: function(event)
|
|
||||||
|
onSearchTextChange = (event) =>
|
||||||
{
|
{
|
||||||
var s = event.target.value;
|
var s = event.target.value;
|
||||||
this.setState({ searchText: s });
|
this.setState({ searchText: s });
|
||||||
if (this._searchTimeout)
|
if (this._searchTimeout)
|
||||||
clearTimeout(this._searchTimeout);
|
clearTimeout(this._searchTimeout);
|
||||||
this._searchTimeout = setTimeout(function() { Store.search(s) }, 300);
|
this._searchTimeout = setTimeout(function() { Store.search(s) }, 300);
|
||||||
},
|
}
|
||||||
render: function()
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
var total = 0;
|
var total = 0;
|
||||||
|
@ -270,18 +282,20 @@ var MessageList = React.createClass({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
componentDidMount: function()
|
|
||||||
|
componentDidMount()
|
||||||
{
|
{
|
||||||
window.addEventListener('resize', this.changeFirstDay);
|
window.addEventListener('resize', this.changeFirstDay);
|
||||||
},
|
}
|
||||||
componentWillUnmount: function()
|
|
||||||
|
componentWillUnmount()
|
||||||
{
|
{
|
||||||
window.removeEventListener('resize', this.changeFirstDay);
|
window.removeEventListener('resize', this.changeFirstDay);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
module.exports = StoreListener(MessageList, function(data)
|
export default StoreListener(MessageList, function(data)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
threads: data.threads,
|
threads: data.threads,
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const DropDownButton = require('./DropDownButton.js');
|
import DropDownButton from './DropDownButton.js';
|
||||||
const Store = require('./Store.js');
|
import Store from './Store.js';
|
||||||
const StoreListener = require('./StoreListener.js');
|
import StoreListener from './StoreListener.js';
|
||||||
const Util = require('./Util.js');
|
import Util from './Util.js';
|
||||||
|
|
||||||
var MessageView = React.createClass({
|
class MessageView extends React.PureComponent
|
||||||
formatLongDate: function(dt)
|
{
|
||||||
|
state = { showImages: false }
|
||||||
|
|
||||||
|
formatLongDate(dt)
|
||||||
{
|
{
|
||||||
if (!(dt instanceof Date))
|
if (!(dt instanceof Date))
|
||||||
dt = new Date(dt.replace(' ', 'T'));
|
dt = new Date(dt.replace(' ', 'T'));
|
||||||
|
@ -14,8 +17,9 @@ var MessageView = React.createClass({
|
||||||
var s = dt.getSeconds();
|
var s = dt.getSeconds();
|
||||||
return Util.WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Util.Months[dt.getMonth()]+' '+dt.getFullYear()+' '+(h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m+':'+(s < 10 ? '0' : '')+s
|
return Util.WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Util.Months[dt.getMonth()]+' '+dt.getFullYear()+' '+(h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m+':'+(s < 10 ? '0' : '')+s
|
||||||
//return dt.toLocaleString();
|
//return dt.toLocaleString();
|
||||||
},
|
}
|
||||||
componentWillReceiveProps: function(nextProps)
|
|
||||||
|
componentWillReceiveProps(nextProps)
|
||||||
{
|
{
|
||||||
if (nextProps.msg != this.props.msg)
|
if (nextProps.msg != this.props.msg)
|
||||||
{
|
{
|
||||||
|
@ -26,16 +30,14 @@ var MessageView = React.createClass({
|
||||||
}
|
}
|
||||||
this.setState(ns);
|
this.setState(ns);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
showImages: function()
|
|
||||||
|
showImages = () =>
|
||||||
{
|
{
|
||||||
this.setState({ showImages: true });
|
this.setState({ showImages: true });
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
render()
|
||||||
return { showImages: false };
|
|
||||||
},
|
|
||||||
render: function()
|
|
||||||
{
|
{
|
||||||
var showImages = this.state.showImages;
|
var showImages = this.state.showImages;
|
||||||
var msg = this.props.msg;
|
var msg = this.props.msg;
|
||||||
|
@ -148,6 +150,6 @@ var MessageView = React.createClass({
|
||||||
] : null}
|
] : null}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
module.exports = StoreListener(MessageView, (data) => { return { layout: data.layout, quickReply: data.quickReply, msg: data.msg }; });
|
export default StoreListener(MessageView, (data) => { return { layout: data.layout, quickReply: data.quickReply, msg: data.msg }; });
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
|
|
||||||
var ProgressBar = module.exports = React.createClass({
|
export default class ProgressBar extends React.PureComponent
|
||||||
render: function()
|
{
|
||||||
|
state = { width: '' }
|
||||||
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
return <div className="progress-bar" ref="pbar" style={{ display: this.props.text ? '' : 'none' }}>
|
return <div className="progress-bar" ref="pbar" style={{ display: this.props.text ? '' : 'none' }}>
|
||||||
<div className="pending" style={{ width: this.state.width }}>{this.props.text} ({this.props.progress||0}%)</div>
|
<div className="pending" style={{ width: this.state.width }}>{this.props.text} ({this.props.progress||0}%)</div>
|
||||||
|
@ -9,29 +12,29 @@ var ProgressBar = module.exports = React.createClass({
|
||||||
<div className="done" ref="pdone" style={{ width: this.state.width }}>{this.props.text} ({this.props.progress||0}%)</div>
|
<div className="done" ref="pdone" style={{ width: this.state.width }}>{this.props.text} ({this.props.progress||0}%)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
componentDidUpdate: function(prevProps, prevState)
|
|
||||||
|
componentDidUpdate(prevProps, prevState)
|
||||||
{
|
{
|
||||||
if (!prevState.width)
|
if (!prevState.width)
|
||||||
{
|
{
|
||||||
setTimeout(this.onResize, 50);
|
setTimeout(this.onResize, 50);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
onResize = () =>
|
||||||
return { width: '' };
|
|
||||||
},
|
|
||||||
onResize: function()
|
|
||||||
{
|
{
|
||||||
this.setState({ width: this.refs.pbar.offsetWidth });
|
this.setState({ width: this.refs.pbar.offsetWidth });
|
||||||
},
|
}
|
||||||
componentDidMount: function()
|
|
||||||
|
componentDidMount()
|
||||||
{
|
{
|
||||||
window.addEventListener('resize', this.onResize);
|
window.addEventListener('resize', this.onResize);
|
||||||
this.onResize();
|
this.onResize();
|
||||||
},
|
}
|
||||||
componentWillUnmount: function()
|
|
||||||
|
componentWillUnmount()
|
||||||
{
|
{
|
||||||
window.removeEventListener('resize', this.onResize);
|
window.removeEventListener('resize', this.onResize);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
12
Store.js
12
Store.js
|
@ -1,10 +1,10 @@
|
||||||
const superagent = require('superagent');
|
import superagent from 'superagent';
|
||||||
const socket_io = require('socket.io-client');
|
import socket_io from 'socket.io-client';
|
||||||
|
|
||||||
const _ = require('./I18n.js');
|
import _ from './I18n.js';
|
||||||
const Util = require('./Util.js');
|
import Util from './Util.js';
|
||||||
|
|
||||||
var Store = module.exports = {
|
const Store = {
|
||||||
data: {
|
data: {
|
||||||
layout: 'message-on-right',
|
layout: 'message-on-right',
|
||||||
quickReply: true,
|
quickReply: true,
|
||||||
|
@ -195,3 +195,5 @@ var Store = module.exports = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Store.startIo();
|
Store.startIo();
|
||||||
|
|
||||||
|
export default Store;
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const Store = require('./Store.js');
|
|
||||||
|
import Store from './Store.js';
|
||||||
|
|
||||||
// "react-redux connect()"-like example
|
// "react-redux connect()"-like example
|
||||||
var StoreListener = React.createClass({
|
class StoreListener extends React.PureComponent
|
||||||
componentDidMount: function()
|
{
|
||||||
|
componentDidMount()
|
||||||
{
|
{
|
||||||
Store.on(this.update);
|
Store.on(this.update);
|
||||||
},
|
}
|
||||||
componentWillUnmount: function()
|
|
||||||
|
componentWillUnmount()
|
||||||
{
|
{
|
||||||
Store.un(this.update);
|
Store.un(this.update);
|
||||||
},
|
}
|
||||||
update: function()
|
|
||||||
|
update = () =>
|
||||||
{
|
{
|
||||||
var newState = this.props.mapStateToProps(Store.data);
|
var newState = this.mapStateToProps(Store.data);
|
||||||
for (var i in newState)
|
for (var i in newState)
|
||||||
{
|
{
|
||||||
if (this.state[i] != newState[i])
|
if (this.state[i] != newState[i])
|
||||||
|
@ -22,18 +26,28 @@ var StoreListener = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
getInitialState: function()
|
|
||||||
{
|
|
||||||
return { ...this.props.initial, ...this.props.mapStateToProps(Store.data) };
|
|
||||||
},
|
|
||||||
render: function()
|
|
||||||
{
|
|
||||||
return React.createElement(this.props.wrappedComponent, this.state);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = function(component, map, initial)
|
render()
|
||||||
|
{
|
||||||
|
var props = { ...this.initial, ...this.props, ...this.state };
|
||||||
|
return React.createElement(this.wrappedComponent, props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function(component, map, initial)
|
||||||
{
|
{
|
||||||
return React.createElement(StoreListener, { wrappedComponent: component, mapStateToProps: map, initial: initial||{} });
|
var cl = class extends StoreListener
|
||||||
|
{
|
||||||
|
constructor(props, context, updater)
|
||||||
|
{
|
||||||
|
super(props, context, updater);
|
||||||
|
this.wrappedComponent = component;
|
||||||
|
this.mapStateToProps = map;
|
||||||
|
this.initial = initial;
|
||||||
|
this.state = map(Store.data);
|
||||||
|
this.update = this.update.bind(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return cl;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
const React = require('react');
|
|
||||||
const Store = require('./Store.js');
|
|
||||||
|
|
||||||
// "react-redux connect()"-like example
|
|
||||||
class StoreListener extends React.Component
|
|
||||||
{
|
|
||||||
componentDidMount()
|
|
||||||
{
|
|
||||||
Store.on(this.update);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount()
|
|
||||||
{
|
|
||||||
Store.un(this.update);
|
|
||||||
}
|
|
||||||
|
|
||||||
update()
|
|
||||||
{
|
|
||||||
var newState = this.mapStateToProps(Store.data);
|
|
||||||
for (var i in newState)
|
|
||||||
{
|
|
||||||
if (this.state[i] != newState[i])
|
|
||||||
{
|
|
||||||
this.setState(newState);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
var props = { ...this.initial, ...this.props, ...this.state };
|
|
||||||
return React.createElement(this.wrappedComponent, props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function(component, map, initial)
|
|
||||||
{
|
|
||||||
var cl = class extends StoreListener
|
|
||||||
{
|
|
||||||
constructor(props, context, updater)
|
|
||||||
{
|
|
||||||
super(props, context, updater);
|
|
||||||
this.wrappedComponent = component;
|
|
||||||
this.mapStateToProps = map;
|
|
||||||
this.initial = initial;
|
|
||||||
this.state = map(Store.data);
|
|
||||||
this.update = this.update.bind(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return cl;
|
|
||||||
};
|
|
|
@ -1,50 +0,0 @@
|
||||||
// НЕ РАБОТАЕТ!
|
|
||||||
const React = require('react');
|
|
||||||
const Store = require('./Store.js');
|
|
||||||
|
|
||||||
// "react-redux connect()"-like example
|
|
||||||
var StoreListener = React.createClass({
|
|
||||||
componentDidMount: function()
|
|
||||||
{
|
|
||||||
Store.on(this.update);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function()
|
|
||||||
{
|
|
||||||
Store.un(this.update);
|
|
||||||
},
|
|
||||||
update: function()
|
|
||||||
{
|
|
||||||
var newState = this.mapStateToProps(Store.data);
|
|
||||||
for (var i in newState)
|
|
||||||
{
|
|
||||||
if (this.state[i] != newState[i])
|
|
||||||
{
|
|
||||||
this.setState(newState);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getInitialState: function()
|
|
||||||
{
|
|
||||||
return { ...this.initial, ...this.mapStateToProps(Store.data) };
|
|
||||||
},
|
|
||||||
render: function()
|
|
||||||
{
|
|
||||||
var props = { ...this.initial, ...this.props, ...this.state };
|
|
||||||
return React.createElement(this.wrappedComponent, props);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = function(component, map, initial)
|
|
||||||
{
|
|
||||||
var fn = function(props, context, updater)
|
|
||||||
{
|
|
||||||
StoreListener.call(this, props, context, updater);
|
|
||||||
this.wrappedComponent = component;
|
|
||||||
this.mapStateToProps = map;
|
|
||||||
this.initial = initial||{};
|
|
||||||
};
|
|
||||||
fn.prototype = Object.create(StoreListener);
|
|
||||||
fn.prototype.constructor = fn;
|
|
||||||
return fn;
|
|
||||||
};
|
|
30
TabPanel.js
30
TabPanel.js
|
@ -1,7 +1,10 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
|
|
||||||
var TabPanel = module.exports = React.createClass({
|
export default class TabPanel extends React.PureComponent
|
||||||
render: function()
|
{
|
||||||
|
state = { selected: 0, tabs: this.props.tabs }
|
||||||
|
|
||||||
|
render()
|
||||||
{
|
{
|
||||||
var bar = [];
|
var bar = [];
|
||||||
var body = [];
|
var body = [];
|
||||||
|
@ -26,21 +29,20 @@ var TabPanel = module.exports = React.createClass({
|
||||||
<div className="tab-bar">{bar}</div>
|
<div className="tab-bar">{bar}</div>
|
||||||
{body}
|
{body}
|
||||||
</div>
|
</div>
|
||||||
},
|
}
|
||||||
componentWillReceiveProps: function(nextProps, nextContent)
|
|
||||||
|
componentWillReceiveProps(nextProps, nextContent)
|
||||||
{
|
{
|
||||||
// FIXME: Do not own tabs?
|
// FIXME: Do not own tabs?
|
||||||
this.setState({ selected: this.state.selected % nextProps.tabs.length, tabs: nextProps.tabs });
|
this.setState({ selected: this.state.selected % nextProps.tabs.length, tabs: nextProps.tabs });
|
||||||
},
|
}
|
||||||
getInitialState: function()
|
|
||||||
{
|
switchTab = (ev) =>
|
||||||
return { selected: 0, tabs: this.props.tabs };
|
|
||||||
},
|
|
||||||
switchTab: function(ev)
|
|
||||||
{
|
{
|
||||||
this.setState({ selected: ev.target.id.substr(5) });
|
this.setState({ selected: ev.target.id.substr(5) });
|
||||||
},
|
}
|
||||||
closeTab: function(ev)
|
|
||||||
|
closeTab = (ev) =>
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
var tab = ev.target.parentNode;
|
var tab = ev.target.parentNode;
|
||||||
|
@ -59,4 +61,4 @@ var TabPanel = module.exports = React.createClass({
|
||||||
}, 200);
|
}, 200);
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
99
Util.js
99
Util.js
|
@ -1,58 +1,61 @@
|
||||||
var WeekDays = module.exports.WeekDays = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
|
export default class Util
|
||||||
var Months = module.exports.Months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
|
|
||||||
|
|
||||||
module.exports.formatBytes = function(s)
|
|
||||||
{
|
{
|
||||||
if (!s) return '';
|
static WeekDays = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
|
||||||
if (s < 1024) return s+' B';
|
static Months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
|
||||||
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';
|
|
||||||
return (Math.round(s*10/1024/1024/1024)/10)+' GB';
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.formatDate = function(dt)
|
static formatBytes(s)
|
||||||
{
|
|
||||||
if (!(dt instanceof Date))
|
|
||||||
dt = new Date(dt.replace(' ', 'T'));
|
|
||||||
var tod = new Date();
|
|
||||||
tod.setHours(0);
|
|
||||||
tod.setMinutes(0);
|
|
||||||
tod.setSeconds(0);
|
|
||||||
tod.setMilliseconds(0);
|
|
||||||
var prevweek = tod;
|
|
||||||
prevweek = prevweek.getTime() - (7 + (prevweek.getDay()+6)%7)*86400000;
|
|
||||||
if (dt.getTime() < prevweek)
|
|
||||||
{
|
{
|
||||||
var d = dt.getDate();
|
if (!s) return '';
|
||||||
var m = dt.getMonth()+1;
|
if (s < 1024) return s+' B';
|
||||||
return (d < 10 ? '0' : '')+d+'.'+(m < 10 ? '0' : '')+m+'.'+dt.getFullYear();
|
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';
|
||||||
|
return (Math.round(s*10/1024/1024/1024)/10)+' GB';
|
||||||
}
|
}
|
||||||
else if (dt.getTime() < tod.getTime())
|
|
||||||
{
|
|
||||||
return WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Months[dt.getMonth()];
|
|
||||||
}
|
|
||||||
var h = dt.getHours();
|
|
||||||
var m = dt.getMinutes();
|
|
||||||
return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.getGroupName = function(k)
|
static formatDate(dt)
|
||||||
{
|
|
||||||
if (k == 't')
|
|
||||||
{
|
{
|
||||||
return 'Today';
|
if (!(dt instanceof Date))
|
||||||
|
dt = new Date(dt.replace(' ', 'T'));
|
||||||
|
var tod = new Date();
|
||||||
|
tod.setHours(0);
|
||||||
|
tod.setMinutes(0);
|
||||||
|
tod.setSeconds(0);
|
||||||
|
tod.setMilliseconds(0);
|
||||||
|
var prevweek = tod;
|
||||||
|
prevweek = prevweek.getTime() - (7 + (prevweek.getDay()+6)%7)*86400000;
|
||||||
|
if (dt.getTime() < prevweek)
|
||||||
|
{
|
||||||
|
var d = dt.getDate();
|
||||||
|
var m = dt.getMonth()+1;
|
||||||
|
return (d < 10 ? '0' : '')+d+'.'+(m < 10 ? '0' : '')+m+'.'+dt.getFullYear();
|
||||||
|
}
|
||||||
|
else if (dt.getTime() < tod.getTime())
|
||||||
|
{
|
||||||
|
return WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Months[dt.getMonth()];
|
||||||
|
}
|
||||||
|
var h = dt.getHours();
|
||||||
|
var m = dt.getMinutes();
|
||||||
|
return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m;
|
||||||
}
|
}
|
||||||
else if (k[0] == 'd')
|
|
||||||
|
static getGroupName(k)
|
||||||
{
|
{
|
||||||
return WeekDays[k.substr(1)%7];
|
if (k == 't')
|
||||||
|
{
|
||||||
|
return 'Today';
|
||||||
|
}
|
||||||
|
else if (k[0] == 'd')
|
||||||
|
{
|
||||||
|
return WeekDays[k.substr(1)%7];
|
||||||
|
}
|
||||||
|
else if (k == 'pw')
|
||||||
|
{
|
||||||
|
return 'Last Week';
|
||||||
|
}
|
||||||
|
else if (k[0] == 'm')
|
||||||
|
{
|
||||||
|
return Months[k.substr(1)-1];
|
||||||
|
}
|
||||||
|
return k;
|
||||||
}
|
}
|
||||||
else if (k == 'pw')
|
|
||||||
{
|
|
||||||
return 'Last Week';
|
|
||||||
}
|
|
||||||
else if (k[0] == 'm')
|
|
||||||
{
|
|
||||||
return Months[k.substr(1)-1];
|
|
||||||
}
|
|
||||||
return k;
|
|
||||||
}
|
}
|
||||||
|
|
9
mail.css
9
mail.css
|
@ -17,6 +17,15 @@ html, body
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#app
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.clear
|
.clear
|
||||||
{
|
{
|
||||||
clear: both;
|
clear: both;
|
||||||
|
|
28
mail.js
28
mail.js
|
@ -1,13 +1,13 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const ReactDOM = require('react-dom');
|
import ReactDOM from 'react-dom';
|
||||||
const ComposeWindow = require('./ComposeWindow.js');
|
import ComposeWindow from './ComposeWindow.js';
|
||||||
const FolderList = require('./FolderList.js');
|
import FolderList from './FolderList.js';
|
||||||
const MessageList = require('./MessageList.js');
|
import MessageList from './MessageList.js';
|
||||||
const MessageView = require('./MessageView.js');
|
import MessageView from './MessageView.js';
|
||||||
const TabPanel = require('./TabPanel.js');
|
import TabPanel from './TabPanel.js';
|
||||||
const Store = require('./Store.js');
|
import Store from './Store.js';
|
||||||
const StoreListener = require('./StoreListener.js');
|
import StoreListener from './StoreListener.js';
|
||||||
const AllDropdowns = require('./AllDropdowns.js');
|
import AllDropdowns from './AllDropdowns.js';
|
||||||
|
|
||||||
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
|
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ var AllTabs = StoreListener(TabPanel, function(data)
|
||||||
noclose: true,
|
noclose: true,
|
||||||
icon: 'mail_unread',
|
icon: 'mail_unread',
|
||||||
title: 'Unread (64)',
|
title: 'Unread (64)',
|
||||||
children: [ MessageList, MessageView ]
|
children: [ <MessageList key="1" />, <MessageView key="2" /> ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'mail_drafts',
|
icon: 'mail_drafts',
|
||||||
i16: true,
|
i16: true,
|
||||||
title: 'Compose Message',
|
title: 'Compose Message',
|
||||||
children: [ ComposeWindow ]
|
children: [ <ComposeWindow key="1" /> ]
|
||||||
}
|
}
|
||||||
] }
|
] }
|
||||||
});
|
});
|
||||||
|
@ -34,9 +34,9 @@ ReactDOM.render(
|
||||||
<div>
|
<div>
|
||||||
{AllDropdowns()}
|
{AllDropdowns()}
|
||||||
<FolderList />
|
<FolderList />
|
||||||
{AllTabs}
|
<AllTabs />
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
document.getElementById('app')
|
||||||
);
|
);
|
||||||
|
|
||||||
Store.loadAccounts();
|
Store.loadAccounts();
|
||||||
|
|
|
@ -5,13 +5,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="mail.css" />
|
<link rel="stylesheet" type="text/css" href="mail.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- <script src="https://fb.me/react-15.0.1.js"></script>
|
<div id="app"></div>
|
||||||
<script src="https://fb.me/react-dom-15.1.0.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>-->
|
|
||||||
<!-- <script src="react-15.0.1.js"></script>
|
|
||||||
<script src="react-dom-15.1.0.js"></script>
|
|
||||||
<script src="browser.js"></script>
|
|
||||||
<script type="text/babel" src="mail.js"></script>-->
|
|
||||||
<script type="text/javascript" src="mail.c.js"></script>
|
<script type="text/javascript" src="mail.c.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
42
package.json
42
package.json
|
@ -6,33 +6,29 @@
|
||||||
"url": "http://yourcmc.ru/wiki/"
|
"url": "http://yourcmc.ru/wiki/"
|
||||||
},
|
},
|
||||||
"description": "LikeOperaMail",
|
"description": "LikeOperaMail",
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify": "latest",
|
"babel-core": "^6.26.0",
|
||||||
"babelify": "latest",
|
"babel-eslint": "^8.2.2",
|
||||||
"watchify": "latest",
|
"babel-loader": "^7.1.5",
|
||||||
"babel-plugin-check-es2015-constants": "latest",
|
"babel-polyfill": "^6.26.0",
|
||||||
"babel-plugin-transform-es2015-arrow-functions": "latest",
|
"babel-preset-env": "^1.6.1",
|
||||||
"babel-plugin-transform-es2015-block-scoping": "latest",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babel-plugin-transform-es2015-classes": "latest",
|
"babel-preset-stage-1": "^6.24.1",
|
||||||
"babel-plugin-transform-es2015-computed-properties": "latest",
|
|
||||||
"babel-plugin-transform-es2015-for-of": "latest",
|
|
||||||
"babel-plugin-transform-es2015-destructuring": "latest",
|
|
||||||
"babel-plugin-transform-es2015-shorthand-properties": "latest",
|
|
||||||
"babel-plugin-transform-object-rest-spread": "latest",
|
|
||||||
"babel-plugin-transform-react-jsx": "latest",
|
|
||||||
"react": "latest",
|
|
||||||
"react-dom": "latest",
|
|
||||||
"uglifyjs": "latest",
|
|
||||||
"uglifyify": "latest",
|
|
||||||
"eslint": "latest",
|
"eslint": "latest",
|
||||||
|
"eslint-plugin-react": "^7.7.0",
|
||||||
|
"react": "^16.2.0",
|
||||||
|
"react-dom": "^16.2.0",
|
||||||
"superagent": "latest",
|
"superagent": "latest",
|
||||||
"socket.io-client": "latest"
|
"socket.io-client": "latest",
|
||||||
|
"webpack": "^3.12.0",
|
||||||
|
"webpack-bundle-analyzer": "^2.13.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compile": "browserify -t babelify -t uglifyify mail.js | uglifyjs -cm > mail.c.js",
|
"lint": "eslint *.js",
|
||||||
"watch-dev": "watchify -t babelify mail.js -o mail.c.js",
|
"compile": "webpack --optimize-minimize",
|
||||||
"watch": "watchify -t babelify -t uglifyify mail.js -o 'uglifyjs -cm > mail.c.js'"
|
"stats": "NODE_ENV=production webpack --optimize-minimize --profile --json > stats.json; webpack-bundle-analyzer stats.json -h 0.0.0.0",
|
||||||
|
"watch-dev": "NODE_ENV=development webpack -w",
|
||||||
|
"watch": "NODE_ENV=production webpack -w --optimize-minimize"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
main: [ "babel-polyfill", './mail.js' ]
|
||||||
|
},
|
||||||
|
context: __dirname,
|
||||||
|
output: {
|
||||||
|
path: __dirname,
|
||||||
|
filename: './mail.c.js'
|
||||||
|
},
|
||||||
|
devtool: 'cheap-module-source-map',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /.jsx?$/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
exclude: /node_modules/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "style-loader",
|
||||||
|
options: {
|
||||||
|
singleton: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "css-loader",
|
||||||
|
options: {
|
||||||
|
modules: true, // default is false
|
||||||
|
sourceMap: true,
|
||||||
|
importLoaders: 1,
|
||||||
|
localIdentName: "[name]--[local]--[hash:base64:8]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
"process.env": {
|
||||||
|
NODE_ENV: JSON.stringify(process.env.NODE_ENV || "production")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
performance: {
|
||||||
|
maxEntrypointSize: 5000000,
|
||||||
|
maxAssetSize: 5000000
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue