1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-12 08:54:00 +02:00
joplin/ElectronClient/app/gui/SideBar.jsx

266 lines
7.7 KiB
React
Raw Normal View History

2017-11-06 01:55:01 +02:00
const React = require('react');
const { connect } = require('react-redux');
const shared = require('lib/components/shared/side-menu-shared.js');
const { Synchronizer } = require('lib/synchronizer.js');
2017-11-08 23:22:24 +02:00
const { BaseModel } = require('lib/base-model.js');
const { Folder } = require('lib/models/folder.js');
const { Tag } = require('lib/models/tag.js');
const { _ } = require('lib/locale.js');
2017-11-08 19:51:55 +02:00
const { themeStyle } = require('../theme.js');
2017-11-08 23:22:24 +02:00
const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
2017-11-06 01:55:01 +02:00
class SideBarComponent extends React.Component {
2017-11-08 19:51:55 +02:00
style() {
const theme = themeStyle(this.props.theme);
2017-11-09 00:48:19 +02:00
const itemHeight = 25;
2017-11-08 19:51:55 +02:00
let style = {
2017-11-09 00:48:19 +02:00
root: {
backgroundColor: theme.backgroundColor2,
},
2017-11-08 19:51:55 +02:00
listItem: {
height: itemHeight,
2017-11-09 00:48:19 +02:00
fontFamily: theme.fontFamily,
fontSize: theme.fontSize,
textDecoration: 'none',
boxSizing: 'border-box',
color: theme.color2,
paddingLeft: 14,
display: 'flex',
alignItems: 'center',
2017-11-09 21:21:10 +02:00
cursor: 'default',
2017-11-12 02:44:26 +02:00
opacity: 0.8,
2017-11-09 00:48:19 +02:00
},
listItemSelected: {
backgroundColor: theme.selectedColor2,
},
conflictFolder: {
color: theme.colorError2,
fontWeight: 'bold',
},
2017-11-09 00:48:19 +02:00
header: {
height: itemHeight * 1.8,
fontFamily: theme.fontFamily,
fontSize: theme.fontSize * 1.3,
textDecoration: 'none',
boxSizing: 'border-box',
color: theme.color2,
paddingLeft: 8,
display: 'flex',
alignItems: 'center',
2017-11-08 19:51:55 +02:00
},
2017-11-09 21:21:10 +02:00
button: {
padding: 6,
fontFamily: theme.fontFamily,
fontSize: theme.fontSize,
textDecoration: 'none',
boxSizing: 'border-box',
color: theme.color2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: "1px solid rgba(255,255,255,0.2)",
marginTop: 10,
marginLeft: 5,
marginRight: 5,
cursor: 'default',
},
syncReport: {
fontFamily: theme.fontFamily,
fontSize: Math.round(theme.fontSize * .9),
color: theme.color2,
2017-11-12 02:44:26 +02:00
opacity: .5,
display: 'flex',
alignItems: 'left',
justifyContent: 'top',
2017-11-12 19:53:26 +02:00
flexDirection: 'column',
marginTop: 10,
marginLeft: 5,
marginRight: 5,
2017-11-12 01:13:14 +02:00
minHeight: 70,
},
2017-11-08 19:51:55 +02:00
};
return style;
}
2017-11-08 23:22:24 +02:00
itemContextMenu(event) {
const itemId = event.target.getAttribute('data-id');
2017-11-12 20:59:54 +02:00
if (itemId === Folder.conflictFolderId()) return;
2017-11-08 23:22:24 +02:00
const itemType = Number(event.target.getAttribute('data-type'));
if (!itemId || !itemType) throw new Error('No data on element');
let deleteMessage = '';
if (itemType === BaseModel.TYPE_FOLDER) {
deleteMessage = _('Delete notebook?');
} else if (itemType === BaseModel.TYPE_TAG) {
deleteMessage = _('Remove this tag from all the notes?');
2017-11-17 20:57:27 +02:00
} else if (itemType === BaseModel.TYPE_SEARCH) {
deleteMessage = _('Remove this search from the sidebar?');
2017-11-08 23:22:24 +02:00
}
const menu = new Menu();
menu.append(new MenuItem({label: _('Delete'), click: async () => {
const ok = bridge().showConfirmMessageBox(deleteMessage);
if (!ok) return;
if (itemType === BaseModel.TYPE_FOLDER) {
await Folder.delete(itemId);
} else if (itemType === BaseModel.TYPE_TAG) {
await Tag.untagAll(itemId);
2017-11-17 20:57:27 +02:00
} else if (itemType === BaseModel.TYPE_SEARCH) {
this.props.dispatch({
type: 'SEARCH_DELETE',
id: itemId,
});
2017-11-08 23:22:24 +02:00
}
}}))
2017-11-17 20:57:27 +02:00
if (itemType === BaseModel.TYPE_FOLDER) {
menu.append(new MenuItem({label: _('Rename'), click: async () => {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: 'renameNotebook',
id: itemId,
});
}}))
}
2017-11-08 23:22:24 +02:00
menu.popup(bridge().window());
}
2017-11-06 01:55:01 +02:00
folderItem_click(folder) {
this.props.dispatch({
2017-11-08 23:22:24 +02:00
type: 'FOLDER_SELECT',
2017-11-06 01:55:01 +02:00
id: folder ? folder.id : null,
});
}
tagItem_click(tag) {
this.props.dispatch({
2017-11-08 23:22:24 +02:00
type: 'TAG_SELECT',
2017-11-06 01:55:01 +02:00
id: tag ? tag.id : null,
});
}
2017-11-17 20:57:27 +02:00
searchItem_click(search) {
this.props.dispatch({
type: 'SEARCH_SELECT',
id: search ? search.id : null,
});
}
2017-11-06 23:11:15 +02:00
async sync_click() {
await shared.synchronize_press(this);
}
2017-11-06 01:55:01 +02:00
folderItem(folder, selected) {
2017-11-09 00:48:19 +02:00
let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected);
if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder);
2017-11-09 21:21:10 +02:00
return <a className="list-item" href="#" data-id={folder.id} data-type={BaseModel.TYPE_FOLDER} onContextMenu={(event) => this.itemContextMenu(event)} key={folder.id} style={style} onClick={() => {this.folderItem_click(folder)}}>{folder.title}</a>
2017-11-06 01:55:01 +02:00
}
tagItem(tag, selected) {
2017-11-09 00:48:19 +02:00
let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected);
2017-11-09 21:21:10 +02:00
return <a className="list-item" href="#" data-id={tag.id} data-type={BaseModel.TYPE_TAG} onContextMenu={(event) => this.itemContextMenu(event)} key={tag.id} style={style} onClick={() => {this.tagItem_click(tag)}}>{tag.title}</a>
2017-11-06 01:55:01 +02:00
}
2017-11-17 20:57:27 +02:00
searchItem(search, selected) {
let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected);
return <a className="list-item" href="#" data-id={search.id} data-type={BaseModel.TYPE_SEARCH} onContextMenu={(event) => this.itemContextMenu(event)} key={search.id} style={style} onClick={() => {this.searchItem_click(search)}}>{search.title}</a>
}
2017-11-06 01:55:01 +02:00
makeDivider(key) {
return <div style={{height:2, backgroundColor:'blue' }} key={key}></div>
}
2017-11-09 00:48:19 +02:00
makeHeader(key, label, iconName) {
const style = this.style().header;
2017-11-12 02:44:26 +02:00
const icon = <i style={{fontSize: style.fontSize * 1.2, marginRight: 5}} className={"fa " + iconName}></i>
2017-11-09 00:48:19 +02:00
return <div style={style} key={key}>{icon}{label}</div>
}
2017-11-06 01:55:01 +02:00
synchronizeButton(label) {
2017-11-09 21:21:10 +02:00
const style = this.style().button;
return <a className="synchronize-button" style={style} href="#" key="sync_button" onClick={() => {this.sync_click()}}>{label}</a>
2017-11-06 01:55:01 +02:00
}
render() {
2017-11-08 19:51:55 +02:00
const theme = themeStyle(this.props.theme);
2017-11-12 01:13:14 +02:00
const style = Object.assign({}, this.style().root, this.props.style, {
overflowX: 'hidden',
overflowY: 'auto',
});
2017-11-08 19:51:55 +02:00
2017-11-06 01:55:01 +02:00
let items = [];
2017-11-12 02:44:26 +02:00
items.push(this.makeHeader('folderHeader', _('Notebooks'), 'fa-folder-o'));
2017-11-09 00:48:19 +02:00
2017-11-06 01:55:01 +02:00
if (this.props.folders.length) {
const folderItems = shared.renderFolders(this.props, this.folderItem.bind(this));
items = items.concat(folderItems);
}
2017-11-12 02:44:26 +02:00
items.push(this.makeHeader('tagHeader', _('Tags'), 'fa-tags'));
2017-11-09 00:48:19 +02:00
2017-11-06 01:55:01 +02:00
if (this.props.tags.length) {
const tagItems = shared.renderTags(this.props, this.tagItem.bind(this));
items.push(<div className="tags" key="tag_items">{tagItems}</div>);
}
2017-11-17 20:57:27 +02:00
if (this.props.searches.length) {
items.push(this.makeHeader('searchHeader', _('Searches'), 'fa-search'));
const searchItems = shared.renderSearches(this.props, this.searchItem.bind(this));
items.push(<div className="searches" key="search_items">{searchItems}</div>);
}
2017-11-06 01:55:01 +02:00
let lines = Synchronizer.reportToLines(this.props.syncReport);
2017-11-12 19:53:26 +02:00
const syncReportText = [];
for (let i = 0; i < lines.length; i++) {
syncReportText.push(<div key={i}>{lines[i]}</div>);
}
2017-11-06 01:55:01 +02:00
2017-11-09 21:21:10 +02:00
items.push(this.synchronizeButton(this.props.syncStarted ? _('Cancel') : _('Synchronise')));
2017-11-06 01:55:01 +02:00
items.push(<div style={this.style().syncReport} key='sync_report'>{syncReportText}</div>);
2017-11-06 01:55:01 +02:00
return (
2017-11-08 19:51:55 +02:00
<div className="side-bar" style={style}>
2017-11-06 01:55:01 +02:00
{items}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
folders: state.folders,
tags: state.tags,
2017-11-17 20:57:27 +02:00
searches: state.searches,
2017-11-06 01:55:01 +02:00
syncStarted: state.syncStarted,
syncReport: state.syncReport,
selectedFolderId: state.selectedFolderId,
selectedTagId: state.selectedTagId,
2017-11-17 20:57:27 +02:00
selectedSearchId: state.selectedSearchId,
2017-11-06 01:55:01 +02:00
notesParentType: state.notesParentType,
locale: state.settings.locale,
theme: state.settings.theme,
};
};
const SideBar = connect(mapStateToProps)(SideBarComponent);
module.exports = { SideBar };