You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-06-03 16:53:41 +02:00
Many changes and improvements
This commit is contained in:
@@ -21,7 +21,23 @@ class HeaderComponent extends React.Component {
|
||||
icon = <i style={iconStyle} className={"fa " + options.iconName}></i>
|
||||
}
|
||||
|
||||
return <a className="button" style={style} key={key} href="#" onClick={() => {options.onClick()}}>{icon}{options.title ? options.title : ''}</a>
|
||||
const isEnabled = (!('enabled' in options) || options.enabled);
|
||||
let classes = ['button'];
|
||||
if (!isEnabled) classes.push('disabled');
|
||||
|
||||
const finalStyle = Object.assign({}, style, {
|
||||
opacity: isEnabled ? 1 : 0.4,
|
||||
});
|
||||
|
||||
return <a
|
||||
className={classes.join(' ')}
|
||||
style={finalStyle}
|
||||
key={key}
|
||||
href="#"
|
||||
onClick={() => { if (isEnabled) options.onClick() }}
|
||||
>
|
||||
{icon}{options.title ? options.title : ''}
|
||||
</a>
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -57,6 +57,11 @@ class MainScreenComponent extends React.Component {
|
||||
let commandProcessed = true;
|
||||
|
||||
if (command.name === 'newNote') {
|
||||
if (!this.props.folders.length) {
|
||||
bridge().showErrorMessageBox(_('Please create a notebook first.'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
promptOptions: {
|
||||
label: _('Note title:'),
|
||||
@@ -67,6 +72,11 @@ class MainScreenComponent extends React.Component {
|
||||
},
|
||||
});
|
||||
} else if (command.name === 'newTodo') {
|
||||
if (!this.props.folders.length) {
|
||||
bridge().showErrorMessageBox(_('Please create a notebook first'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
promptOptions: {
|
||||
label: _('To-do title:'),
|
||||
@@ -134,6 +144,8 @@ class MainScreenComponent extends React.Component {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const promptOptions = this.state.promptOptions;
|
||||
const folders = this.props.folders;
|
||||
const notes = this.props.notes;
|
||||
|
||||
const headerStyle = {
|
||||
width: style.width,
|
||||
@@ -172,12 +184,14 @@ class MainScreenComponent extends React.Component {
|
||||
headerButtons.push({
|
||||
title: _('New note'),
|
||||
iconName: 'fa-file-o',
|
||||
enabled: !!folders.length,
|
||||
onClick: () => { this.doCommand({ name: 'newNote' }) },
|
||||
});
|
||||
|
||||
headerButtons.push({
|
||||
title: _('New to-do'),
|
||||
iconName: 'fa-check-square-o',
|
||||
enabled: !!folders.length,
|
||||
onClick: () => { this.doCommand({ name: 'newTodo' }) },
|
||||
});
|
||||
|
||||
@@ -190,6 +204,7 @@ class MainScreenComponent extends React.Component {
|
||||
headerButtons.push({
|
||||
title: _('Layout'),
|
||||
iconName: 'fa-columns',
|
||||
enabled: !!notes.length,
|
||||
onClick: () => {
|
||||
this.toggleVisiblePanes();
|
||||
},
|
||||
@@ -207,7 +222,7 @@ class MainScreenComponent extends React.Component {
|
||||
visible={!!this.state.promptOptions} />
|
||||
<Header style={headerStyle} showBackButton={false} buttons={headerButtons} />
|
||||
<SideBar style={sideBarStyle} />
|
||||
<NoteList itemHeight={40} style={noteListStyle} />
|
||||
<NoteList style={noteListStyle} />
|
||||
<NoteText style={noteTextStyle} visiblePanes={this.props.noteVisiblePanes} />
|
||||
</div>
|
||||
);
|
||||
@@ -220,6 +235,8 @@ const mapStateToProps = (state) => {
|
||||
theme: state.settings.theme,
|
||||
windowCommand: state.windowCommand,
|
||||
noteVisiblePanes: state.noteVisiblePanes,
|
||||
folders: state.folders,
|
||||
notes: state.notes,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ class NoteListComponent extends React.Component {
|
||||
|
||||
return (
|
||||
<ItemList
|
||||
itemHeight={this.props.itemHeight}
|
||||
itemHeight={this.style().listItem.height}
|
||||
style={style}
|
||||
className={"note-list"}
|
||||
items={this.props.notes}
|
||||
|
||||
@@ -46,10 +46,11 @@ class NoteTextComponent extends React.Component {
|
||||
// https://github.com/ajaxorg/ace/issues/2046
|
||||
this.editorMaxScrollTop_ = 0;
|
||||
this.onAfterEditorRender_ = () => {
|
||||
console.info('ENDER done');
|
||||
const r = this.editor_.editor.renderer;
|
||||
this.editorMaxScrollTop_ = Math.max(0, r.layerConfig.maxHeight - r.$size.scrollerHeight);
|
||||
|
||||
if (this.restoreScrollTop_) {
|
||||
if (this.restoreScrollTop_ !== null) {
|
||||
this.editorSetScrollTop(this.restoreScrollTop_);
|
||||
this.restoreScrollTop_ = null;
|
||||
}
|
||||
@@ -106,6 +107,8 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
async reloadNote(props) {
|
||||
console.info('Reload note...');
|
||||
|
||||
this.mdToHtml_ = null;
|
||||
|
||||
const noteId = props.noteId;
|
||||
@@ -120,7 +123,12 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
this.editorMaxScrollTop_ = 0;
|
||||
|
||||
this.editorSetScrollTop(0);
|
||||
// HACK: To go around a bug in Ace editor, we first set the scroll position to 1
|
||||
// and then (in the renderer callback) to the value we actually need. The first
|
||||
// operation helps clear the scroll position cache. See:
|
||||
// https://github.com/ajaxorg/ace/issues/2195
|
||||
this.editorSetScrollTop(1);
|
||||
this.restoreScrollTop_ = 0;
|
||||
|
||||
this.setState({
|
||||
note: note,
|
||||
@@ -235,7 +243,7 @@ class NoteTextComponent extends React.Component {
|
||||
webviewReady: true,
|
||||
});
|
||||
|
||||
//this.webview_.openDevTools();
|
||||
this.webview_.openDevTools();
|
||||
}
|
||||
|
||||
webview_ref(element) {
|
||||
|
||||
@@ -86,7 +86,8 @@ class OneDriveLoginScreenComponent extends React.Component {
|
||||
const headerButtons = [
|
||||
{
|
||||
title: _('Refresh'),
|
||||
onClick: () => this.refresh_click()
|
||||
onClick: () => this.refresh_click(),
|
||||
iconName: 'fa-refresh',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -78,26 +78,17 @@ ipcRenderer.on('setHtml', (event, html) => {
|
||||
|
||||
loadAndApplyHljs();
|
||||
|
||||
// A checkbox list is rendered like this by markdown-it:
|
||||
// <ul>
|
||||
// <li>
|
||||
// <p>
|
||||
// [x] some item
|
||||
// </p>
|
||||
// </li>
|
||||
// ...
|
||||
// </ul>
|
||||
// And we need to remove the padding from "p" and the bullet from "ul"
|
||||
// Remove the bullet from "ul" for checkbox lists and extra padding
|
||||
const checkboxes = document.getElementsByClassName('checkbox');
|
||||
for (let i = 0; i < checkboxes.length; i++) {
|
||||
const cb = checkboxes[i];
|
||||
cb.parentElement.style.marginBottom = 0;
|
||||
const ul = cb.parentElement.parentElement.parentElement;
|
||||
const ul = cb.parentElement.parentElement;
|
||||
if (!ul) {
|
||||
console.warn('Unexpected layout for checkbox');
|
||||
continue;
|
||||
}
|
||||
ul.style.listStyleType = 'none';
|
||||
ul.style.paddingLeft = 0;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -107,18 +98,6 @@ ipcRenderer.on('setPercentScroll', (event, percent) => {
|
||||
contentElement.scrollTop = percent * maxScrollTop();
|
||||
});
|
||||
|
||||
// function elementMapCoordinates(element) {
|
||||
// while (true) {
|
||||
// if (!element) break;
|
||||
|
||||
// const m = element.getAttribute('data-map');
|
||||
// if (m) return m.split(':');
|
||||
// element = element.parentElement;
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// }
|
||||
|
||||
function maxScrollTop() {
|
||||
return Math.max(0, contentElement.scrollHeight - contentElement.clientHeight);
|
||||
}
|
||||
@@ -142,7 +121,7 @@ document.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
webview.addEventListener('dragover', function(e) {
|
||||
document.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
</script>
|
||||
@@ -23,13 +23,13 @@ body, textarea {
|
||||
background-color: #564B6C;
|
||||
}
|
||||
|
||||
.header .button:hover {
|
||||
.header .button:not(.disabled):hover {
|
||||
background-color: rgba(0,160,255,0.1);
|
||||
border: 1px solid rgba(0,160,255,0.5);
|
||||
box-sizing: 'border-box';
|
||||
}
|
||||
|
||||
.header .button:active {
|
||||
.header .button:not(.disabled):active {
|
||||
background-color: rgba(0,160,255,0.2);
|
||||
border: 1px solid rgba(0,160,255,0.7);
|
||||
box-sizing: 'border-box';
|
||||
|
||||
@@ -37,7 +37,7 @@ const globalStyle = {
|
||||
globalStyle.htmlFontSize = globalStyle.fontSize + 'px';
|
||||
globalStyle.htmlColor ='black'; // Note: CSS in WebView component only supports named colors or rgb() notation
|
||||
globalStyle.htmlBackgroundColor ='white';
|
||||
globalStyle.htmlDividerColor ='Gainsboro';
|
||||
globalStyle.htmlDividerColor = 'rgb(150,150,150)';
|
||||
globalStyle.htmlLinkColor ='blue';
|
||||
globalStyle.htmlLineHeight ='20px';
|
||||
|
||||
|
||||
@@ -155,8 +155,10 @@ class MdToHtml {
|
||||
|
||||
renderTokens_(tokens, options) {
|
||||
let output = [];
|
||||
let previousToken = null;
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const t = tokens[i];
|
||||
const nextToken = i < tokens.length ? tokens[i+1] : null;
|
||||
|
||||
let tag = t.tag;
|
||||
let openTag = null;
|
||||
@@ -166,7 +168,12 @@ class MdToHtml {
|
||||
|
||||
// if (t.map) attrs.push(['data-map', t.map.join(':')]);
|
||||
|
||||
if (tag && t.type.indexOf('_open') >= 0) {
|
||||
if (previousToken && previousToken.tag === 'li' && tag === 'p') {
|
||||
// Markdown-it render list items as <li><p>Text<p></li> which makes it
|
||||
// complicated to style and layout the HTML, so we remove this extra
|
||||
// <p> here and below in closeTag.
|
||||
openTag = null;
|
||||
} else if (tag && t.type.indexOf('_open') >= 0) {
|
||||
openTag = tag;
|
||||
} else if (tag && t.type.indexOf('_close') >= 0) {
|
||||
closeTag = tag;
|
||||
@@ -196,6 +203,10 @@ class MdToHtml {
|
||||
if (t.type === 'image') {
|
||||
if (t.content) attrs.push(['title', t.content]);
|
||||
output.push(this.renderImage_(attrs, options));
|
||||
} else if (t.type === 'softbreak') {
|
||||
output.push('<br/>');
|
||||
} else if (t.type === 'hr') {
|
||||
output.push('<hr/>');
|
||||
} else {
|
||||
if (t.children) {
|
||||
const parsedChildren = this.renderTokens_(t.children, options);
|
||||
@@ -207,7 +218,9 @@ class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
if (t.type === 'link_close') {
|
||||
if (nextToken && nextToken.tag === 'li' && t.tag === 'p') {
|
||||
closeTag = null;
|
||||
} else if (t.type === 'link_close') {
|
||||
closeTag = 'a';
|
||||
} else if (tag && t.type.indexOf('inline') >= 0) {
|
||||
closeTag = openTag;
|
||||
@@ -223,7 +236,9 @@ class MdToHtml {
|
||||
} else {
|
||||
output.push('</' + closeTag + '>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousToken = t;
|
||||
}
|
||||
return output.join('');
|
||||
}
|
||||
@@ -235,7 +250,10 @@ class MdToHtml {
|
||||
const cacheKey = this.makeContentKey(this.loadedResources_, body, style, options);
|
||||
if (this.cachedContentKey_ === cacheKey) return this.cachedContent_;
|
||||
|
||||
const md = new MarkdownIt();
|
||||
const md = new MarkdownIt({
|
||||
breaks: true,
|
||||
linkify: true,
|
||||
});
|
||||
const env = {};
|
||||
|
||||
// Hack to make checkboxes clickable. Ideally, checkboxes should be parsed properly in
|
||||
@@ -257,7 +275,7 @@ class MdToHtml {
|
||||
const tokens = md.parse(body, env);
|
||||
|
||||
// console.info(body);
|
||||
// console.info(tokens);
|
||||
console.info(tokens);
|
||||
|
||||
let renderedBody = this.renderTokens_(tokens, options);
|
||||
|
||||
@@ -304,7 +322,8 @@ class MdToHtml {
|
||||
color: ` + style.htmlLinkColor + `
|
||||
}
|
||||
ul {
|
||||
padding-left: 0;
|
||||
padding-left: 1.3em;
|
||||
|
||||
}
|
||||
a.checkbox {
|
||||
font-size: 1.6em;
|
||||
@@ -321,7 +340,8 @@ class MdToHtml {
|
||||
padding: .5em 1em .5em 1em;
|
||||
}
|
||||
hr {
|
||||
border: 1px solid ` + style.htmlDividerColor + `;
|
||||
border: none;
|
||||
border-bottom: 1px solid ` + style.htmlDividerColor + `;
|
||||
}
|
||||
img {
|
||||
width: auto;
|
||||
|
||||
Reference in New Issue
Block a user