diff --git a/experiments/outline-editor/editor.js b/experiments/outline-editor/editor.js index a0010af..a49e28c 100755 --- a/experiments/outline-editor/editor.js +++ b/experiments/outline-editor/editor.js @@ -644,6 +644,27 @@ var Outline = { return cur }, deindent: function(node='focused', indent=false){ return this.indent(node, indent) }, + shift: function(node='focused', direction){ + if(node == 'up' || node == 'down'){ + direction = node + node = 'focused' } + if(direction == null){ + return } + node = this.get(node) + var focused = node.classList.contains('focused') + var siblings = this.get(node, 'siblings') + var i = siblings.indexOf(node) + if(direction == 'up' + && i > 0){ + siblings[i-1].before(node) + focused + && this.focus() + } else if(direction == 'down' + && i < siblings.length-1){ + siblings[i+1].after(node) + focused + && this.focus() } + return this }, show: function(node='focused', offset){ var node = this.get(...arguments) var outline = this.outline @@ -750,14 +771,7 @@ var Outline = { elem.text = run('post', text) return elem }, - // XXX essentially here we need to remove service stuff like some - // attributes (collapsed, id, ...)... - // XXX also need to quote leading '- ' in block text here... - // e.g. - // - block - // some text - // - text in the above block ('-' needs to be quoted) - // - next block + // output format... __code2text__: function(code){ return code .replace(/(\n\s*)-/g, '$1\\-') }, @@ -999,6 +1013,21 @@ var Outline = { this.toggleCollapse(false) : this.focus('next') } }, + PageUp: function(evt){ + var edited = this.get('edited') + if(!edited + && (evt.shiftKey + || evt.ctrlKey)){ + evt.preventDefault() + this.shift('up') } }, + PageDown: function(evt){ + var edited = this.get('edited') + if(!edited + && (evt.shiftKey + || evt.ctrlKey)){ + evt.preventDefault() + this.shift('down') } }, + // indent... Tab: function(evt){ evt.preventDefault() @@ -1012,37 +1041,78 @@ var Outline = { // edit mode... O: function(evt){ - if(evt.target.nodeName != 'TEXTAREA'){ + if(!this.get('edited')){ evt.preventDefault() - this.Block('before') - ?.querySelector('textarea') - ?.focus() } }, + this.edit( + this.Block('before')) } }, o: function(evt){ - if(evt.target.nodeName != 'TEXTAREA'){ + if(!this.get('edited')){ evt.preventDefault() - this.Block('next') - ?.querySelector('textarea') - ?.focus() } }, + this.edit( + this.Block('next')) } }, Enter: function(evt){ - if(evt.ctrlKey - || evt.shiftKey){ - return } - evt.preventDefault() - evt.target.nodeName == 'TEXTAREA' ? + + var edited = this.get('edited') + // edit -> split text... + if(edited){ + if(evt.ctrlKey + || evt.shiftKey){ + return } + evt.preventDefault() + var a = edited.selectionStart + var b = edited.selectionEnd + var prev = edited.value.slice(0, a) + var next = edited.value.slice(b) + edited.value = prev this.Block('next') - ?.querySelector('textarea') - ?.focus() - : this.get() - ?.querySelector('textarea') - ?.focus() }, + edited = this.edit('next') + edited.value = next + edited.selectionStart = 0 + edited.selectionEnd = 0 + return } + // view -> edit... + evt.preventDefault() + this.edit() }, Escape: function(evt){ - this.outline.querySelector('textarea:focus') - ?.parentElement - ?.focus() }, + this.focus() }, + Delete: function(evt){ - if(this.get('edited')){ + var edited = this.get('edited') + if(edited){ + if(edited.selectionStart == edited.value.length){ + var next = this.get('edited', 'next') + // can't reclaim nested children... + if(this.get(next, 'children').length > 0){ + return } + // do not delete past the top element... + if(this.get(0).querySelector('.code') === next){ + return } + evt.preventDefault() + var i = edited.value.length + edited.value += next.value + edited.selectionStart = i + edited.selectionEnd = i + this.remove(next) } return } this.remove() }, + Backspace: function(evt){ + var edited = this.get('edited') + if(edited + && edited.selectionStart == 0 + // can't reclaim nested children... + && this.get(edited, 'children').length == 0){ + var prev = this.get('edited', 'prev') + // do not delete past the bottom element... + if(this.get(-1).querySelector('.code') === prev){ + return } + evt.preventDefault() + var i = prev.value.length + prev.value += edited.value + this.edit(prev) + prev.selectionStart = i + prev.selectionEnd = i + this.remove(edited) + return } }, // select... // XXX add: diff --git a/experiments/outline-editor/index.html b/experiments/outline-editor/index.html index 7ae1b90..e05f35b 100755 --- a/experiments/outline-editor/index.html +++ b/experiments/outline-editor/index.html @@ -47,14 +47,12 @@ var setup = function(){ - BUG: last node seems to get trash tags added to it's end... - - ## ToDo: - - ASAP: editor: backsapce/del at start/end of a block should join it with prev/next - - ASAP: editor: pressing enter in text edit mode should split text into two blocks - - ASAP: editor: shifting nodes up/down - ASAP: scroll into view is bad... - ASAP: need to reach checkboxes via keyboard - FEATURE: read-only mode - FEATURE: `collapse-children:: true` block option -- when loading collapse all immediate children - FF: figure out a way to draw expand/collapse bullets without the use of CSS' `:has(..)` + - show list bullet if node is empty but edited... (???) - Code blocks and bullets: - ``` code @@ -69,6 +67,7 @@ var setup = function(){ - delete node - indent/deindent - edit node + - auto-shift done blocks to the end of siblings... (option?) - FEATURE? block templates... collapsed:: true - something like: `TPL: [_] -- ` @@ -97,6 +96,9 @@ var setup = function(){ block text - NOTE: this is only a problem if making list-items manually -- disable??? - empty item height is a bit off... + - DONE editor: backsapce/del at start/end of a block should join it with prev/next + - DONE editor: pressing enter in text edit mode should split text into two blocks + - DONE editor: shifting nodes up/down - DONE Q: can we edit code in a code block directly? (a-la Logseq) - DONE "percentage complete" in parent blocks with todo's nested - DONE `.editor .outline:empty` view and behavior... @@ -261,6 +263,8 @@ Controls: right - focus first child node tab - indent node s-tab - deindent node + s-pgup - shift node up + s-pgdown - shift node down s-left - collapse node s-right - expand node enter - normal mode: edit node