diff --git a/editor/src/actions.js b/editor/src/actions.js new file mode 100644 index 0000000..d021144 --- /dev/null +++ b/editor/src/actions.js @@ -0,0 +1,93 @@ +function hasCheckbox(id) { + let found = false + let todo = false + this.update(id, function (item, prev, next) { + let res = item.text.match(/^#\[\[(TODO|DONE)\]\]/) + if (res) { + found = true + todo = res[1] === 'TODO' + } + return item + }); + return [found, todo] +} + +function addCheckbox(id) { + this.update(id, function (item, prev, next) { + if (item.text.match(/^#\[\[TODO\]\]/)) { + return item + } else if (item.text.match(/^#\[\[|DONE\]\]/)) { + item.text = item.text.replace(/^#\[\[DONE\]\]/, '#[[TODO]]') + } else { + item.text = '#[[TODO]] ' + item.text + } + return item + }); +} + +function removeCheckbox(id) { + this.update(id, function (item, prev, next) { + if (item.text.match(/^#\[\[(TODO|DONE)\]\]/)) { + item.text = item.text.replace(/#\[\[(TODO|DONE)\]\]\s*/, '') + } + return item + }); +} + +function markDone(id) { + this.update(id, function (item, prev, next) { + if (item.text.match(/^#\[\[(TODO)\]\]/)) { + item.text = item.text.replace(/#\[\[(TODO)\]\]\s*/, '#[[DONE]]') + } + return item + }); +} + +function markTodo(id) { + this.update(id, function (item, prev, next) { + if (item.text.match(/^#\[\[(DONE)\]\]/)) { + item.text = item.text.replace(/#\[\[(DONE)\]\]\s*/, '#[[TODO]]') + } + return item + }); +} + +function sort(id, property, direction) { + let children = this.getChildren(id) + if (property === 'todo') { + children = _.orderBy(children, item => { + let res = item.text.match(/^#\[\[(TODO|DONE)/) + if (!res) return "C"; + if (res[1] === 'TODO') return direction === 'asc' ? "A" : "B"; + if (res[1] === 'DONE') return direction === 'asc' ? "B" : "A"; + return "D"; + }, direction) + } else { + children = _.orderBy(children, property, direction) + } + this.replaceChildren(id, children) +} + +function removeCompleted(id) { + let children = this.getChildren(id) + let newChildren = _.filter(children, item => { + let res = item.text.match(/^#\[\[(TODO|DONE)\]\]/) + let found = false, todo = false + if (res) { + found = true + todo = res[1] === 'TODO' + } + return !found || todo + }) + this.replaceChildren(id, newChildren) +} + +export default { + hasCheckbox, + addCheckbox, + removeCheckbox, + markTodo, + markDone, + sort, + removeCompleted +} diff --git a/editor/src/editor.js b/editor/src/editor.js index de2237d..9130b0d 100644 --- a/editor/src/editor.js +++ b/editor/src/editor.js @@ -23,6 +23,7 @@ import MD from './markdown' import he from 'he' import {all, create} from 'mathjs' import formulaFunctions from './formula' +import actions from './actions' moment.locale('nl') @@ -375,6 +376,7 @@ function Editor(holder, input) { let editor = listEditor(holder, inputData, options); holder.$listEditor = editor editor.scope = {} + editor.actions = actions $(holder).on('click', '.content input[type="checkbox"]', function (event) { let that = this diff --git a/editor/src/menu.js b/editor/src/menu.js index 99482f9..0ef9336 100644 --- a/editor/src/menu.js +++ b/editor/src/menu.js @@ -49,35 +49,135 @@ function connectContextMenu(editor) { name: 'Zoom in', callback: function (key, opt) { editor.zoomin(this).then(id => { - location.href = '/edit/'+id; + location.href = '/edit/' + id; }) } }, debug: { name: 'Debug block', callback: function (key, opt) { - editor.copy(this, {recursive:true}).then(console.log) + editor.copy(this, {recursive: true}).then(console.log) + } + }, + addCheckbox: { + name: 'Add checkbox', + visible: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + let [found] = editor.actions.hasCheckbox.call(editor, id) + return !found + }, + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.addCheckbox.call(editor, id) + } + }, + removeCheckbox: { + name: 'Remove checkbox', + visible: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + let [found] = editor.actions.hasCheckbox.call(editor, id) + return found + }, + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.removeCheckbox.call(editor, id) + } + }, + markTodo: { + name: 'Mark To Do', + visible: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + let [found, todo] = editor.actions.hasCheckbox.call(editor, id) + return found && !todo + }, + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.markTodo.call(editor, id) + } + }, + markDone: { + name: 'Mark Done', + visible: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + let [found, todo] = editor.actions.hasCheckbox.call(editor, id) + return found && todo + }, + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.markDone.call(editor, id) + } + }, + sort: { + name: 'Sort', + items: [ + { + name: 'Title (A-Z)', + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.sort.call(editor, id, 'text', 'asc') + editor.render() + } + }, + { + name: 'Title (Z-A)', + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.sort.call(editor, id, 'text', 'desc') + editor.render() + } + }, + { + name: 'Checked first', + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.sort.call(editor, id, 'todo', 'desc') + editor.render() + } + }, + { + name: 'Unchecked first', + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.sort.call(editor, id, 'todo', 'asc') + editor.render() + } + } + ] + }, + + removeCompleted: { + name: 'Remove completed', + callback: function (key, opt) { + let item = $(this).parents('.list-item') + let id = item.attr('data-id') + editor.actions.removeCompleted.call(editor, id) + editor.render() } } // TODO // - Sort - // - Title (A-Z) - // - Title (Z-A) // - Date (on item) (new-old) // - Date (on item) (old-new) - // - Unchecked first - // - Checked first // - Updated (new to old) // - Updated (old to new) // - Created (new to old) // - Created (old to new) // - Reverse - // - Zoom in (should work in the same document) // - Move To // - Indent - // - Add checkboxes - // - Remove checkboxes } }); } diff --git a/list-editor/index.js b/list-editor/index.js index e20a214..4e35541 100644 --- a/list-editor/index.js +++ b/list-editor/index.js @@ -136,11 +136,13 @@ function editor(root, inputData, options) { startEditing(root, store, cursor) } + function getChildren(id ) { + return store.children(id ) + } function replaceChildren(id, children) { store.replaceChildren(id, children) } - function renderUpdate() { disableDragging(drake) render(root, store); @@ -148,6 +150,9 @@ function editor(root, inputData, options) { } let EDITOR = { + normalKeymap, + editorKeymap, + on, save, saveTree, @@ -156,6 +161,7 @@ function editor(root, inputData, options) { start, zoomin, treeForId, + getChildren, replaceChildren, render: renderUpdate, diff --git a/list-editor/store.js b/list-editor/store.js index 6c96df1..612c126 100644 --- a/list-editor/store.js +++ b/list-editor/store.js @@ -417,6 +417,21 @@ function Store(inputData) { return changed; } + function children(id) { + let first = index(id) + let firstVal = value(id) + if (firstVal.fleeting) { + return [] + } + let last = lastHigherIndented(first) + first++ + return _.map(idList.slice(first, last), function (id) { + return _.tap(_.clone(value(id)), c => { + c.indented -= firstVal.indented + 1 + }) + }) + } + function replaceChildren(id, newChildren) { let first = index(id) let firstVal = value(id) @@ -470,6 +485,7 @@ function Store(inputData) { debug, tree, flat, + children, replaceChildren, hasChanged, clearChanged,