diff --git a/experiments/outline-editor/editor.css b/experiments/outline-editor/editor.css index e15df90..1ba8656 100755 --- a/experiments/outline-editor/editor.css +++ b/experiments/outline-editor/editor.css @@ -1,9 +1,24 @@ +/*********************************************************************/ + :root { --font-size: 5mm; + + --outline-padding: 5rem; + + --item-indent: 2rem; --item-padding: 0.2em; + + --checkbox-size: 1.5rem; + --button-size: 2em; +} + + +/*********************************************************************/ + +:root { font-family: sans-serif; font-size: var(--font-size); @@ -25,22 +40,26 @@ .editor .outline { display: block; position: relative; + /* XXX do a better calculation... */ - width: calc(100% - (var(--button-size) + 4em)); + width: calc(100% - var(--button-size) - var(--outline-padding) * 2); + + padding-left: var(--outline-padding); + padding-right: var(--outline-padding); } .editor .outline [tabindex] { position: relative; } -.editor .outline div [tabindex] { - margin-left: 2em; +.editor .outline [tabindex] [tabindex] { + margin-left: var(--item-indent); } .editor .outline [tabindex]>span, .editor .outline [tabindex]>textarea { display: block; width: 100%; /* XXX this is a tiny bit off and using textarea's height here is off too... */ - min-height: 1em; + min-height: calc(1em + var(--item-padding) * 2); padding-top: var(--item-padding); padding-bottom: var(--item-padding); padding-left: 0; @@ -55,6 +74,9 @@ outline: none; border: none; } +.editor .outline [tabindex]>span:blank { + content: " "; +} .editor .outline [tabindex]>textarea { height: calc(2 * var(--item-padding) + 1em); overflow: hidden; @@ -87,7 +109,66 @@ .editor .outline div[collapsed] { border-bottom: solid 1px silver; } -.editor .outline div[collapsed] div { +/* expand/collapse button... */ +.editor .outline [tabindex]:after { + --size: 0.5rem; + + content: ""; + position: absolute; + display: inline-block; + top: calc(1em / 2); + right: calc(-1rem + -1 * var(--size) - var(--item-padding)); + width: 0; + height: 0; + border: solid calc(var(--size) / 1.2) transparent; + border-top: solid var(--size) black; + opacity: 0; +} +.editor .outline [tabindex][collapsed]:after { + border: solid calc(var(--size) / 1.2) transparent; + border-left: solid var(--size) black; + margin-right: -0.25rem; + opacity: 0.1; +} +.editor .outline [tabindex]:hover:after { + opacity: 0.1; +} +/* click/tap zone for expand button... */ +.editor .outline [tabindex]>span:before { + content: ""; + position: absolute; + display: inline-block; + right: -2rem; + width: 2rem; + height: calc(1em + var(--item-padding) * 2); +} +/* +.editor .outline div[collapsed]:before, +.editor .outline div[collapsed]:after { + --size: 0.3em; + + content: ""; + display: inline-block; + position: absolute; + width: var(--size); + height: var(--size); + bottom: 0; + right: calc(-1 * var(--size)); + margin-bottom: calc(var(--size) / -2 - 0.5px); + rotate: -45deg; + box-sizing: border-box; + + color: silver; + border-top: solid 1px silver; + border-left: solid 1px silver; +} +.editor .outline div[collapsed]:before { + right: auto; + left: calc(-1 * var(--size)); + rotate: 135deg; +} +*/ +.editor .outline div[collapsed] [tabindex] { display: none; } @@ -119,6 +200,7 @@ /*-------------------------------------------------------------------*/ /* Styles */ +/* Headings... */ .editor .outline .heading-1>span, .editor .outline .heading-1>textarea, .editor .outline .heading-2>span, @@ -139,7 +221,7 @@ .editor .outline .heading-2>textarea, .editor .outline .heading-3>span, .editor .outline .heading-3>textarea { - border-bottom: solid 1px rgba(0,0,0,0.05); + border-bottom: solid 1px rgba(0,0,0,0.1); } .editor .outline .heading-1>span, @@ -195,24 +277,48 @@ color: silver; } -.editor .outline [tabindex]>span>input[type=checkbox].check, -.editor .outline [tabindex]>span>input[type=checkbox].todo { - --size: 1.5rem; - --height: calc(var(--font-size) + 2 * var(--item-padding)); - top: calc(0.6em + var(--item-padding)); - height: var(--size); - width: var(--size); - margin-top: calc(var(--size) / -2); +/* checkboxes... */ +:root { + /* NOTE: this must have a unit... */ + /* XXX move this to the config when fixed... */ + /* XXX setting this to >0em will add margins to both sides of + * the inline checkbox even if it is the first thing in the + * element -- can's seem to figure out a way to avoid this */ + --checkbox-margin: 0em; +} +/* align todo checkboxes to indnt (otherwise they are on the indent) */ +.editor .outline [tabindex].todo>span { + margin-left: var(--checkbox-size); + padding-left: calc( + var(--item-padding) + + var(--checkbox-margin) * 2); +} +.editor .outline [tabindex].check>span>input[type=checkbox], +.editor .outline [tabindex].todo>span>input[type=checkbox] { + height: var(--checkbox-size); + width: var(--checkbox-size); + + margin-top: calc(var(--checkbox-size) / -2); + margin-left: var(--checkbox-margin); + margin-right: var(--checkbox-margin); + + transform: translateY(calc(2 * var(--item-padding))); /* NOTE: this appears to be needed for the em sizes above to work correctly */ font-size: 1em; } -.editor .outline [tabindex]>span>input[type=checkbox].todo { - position: absolute; - margin-left: calc(-1 * var(--size) - var(--item-padding) * 2); +.editor .outline [tabindex].todo>span>input[type=checkbox]:first-child { + margin-left: calc( + var(--checkbox-size) * -1 + - var(--item-padding) + - var(--checkbox-margin) * 2); + margin-right: calc( + var(--item-padding) + + var(--checkbox-margin)); } -.editor .outline [tabindex]>span>input[type=checkbox].check { - transform: translateY(calc(2 * var(--item-padding))); +/* XXX need to remove left margin from strictly the first itme in block... */ +.editor .outline [tabindex].check>span>input[type=checkbox] { + /*margin-left: 0;*/ } diff --git a/experiments/outline-editor/editor.js b/experiments/outline-editor/editor.js index 8d0dd0a..5061dcf 100755 --- a/experiments/outline-editor/editor.js +++ b/experiments/outline-editor/editor.js @@ -195,7 +195,7 @@ var Outline = { // heading... parsed.style ? node.classList.add(...parsed.style) - : node.classList.remove(...this.__styles__) + : node.classList.remove(...this.__styles) } else { html.innerHTML = data.text } text.value = data.text @@ -259,7 +259,9 @@ var Outline = { if(elem.classList.contains('focused')){ // XXX need to be able to get the next elem on same level... this.toggleCollapse(elem, true) - next = this.get(elem, 'next') } + next = elem === this.get(-1) ? + this.get(elem, 'prev') + : this.get(elem, 'next') } elem?.remove() next?.focus() return this }, @@ -269,33 +271,27 @@ var Outline = { return this }, // block serialization... - // XXX shouild we support headings + other formatting per block??? // XXX split this up into a generic handler + plugins... // XXX need a way to filter input text... // use-case: hidden attributes... - __styles__: [ - 'heading-1', - 'heading-2', - 'heading-3', - 'heading-4', - 'heading-5', - 'heading-6', - 'list', - ], + // NOTE: this is auto-populated by .__code2html__(..) + __styles: undefined, __code2html__: function(code){ + var that = this var elem = { collapsed: false, } - var heading = function(level){ + var style = function(style, code=undefined){ + style = [style].flat() + that.__styles = [...new Set([ + ...(that.__styles ?? []), + ...style, + ])] return function(_, text){ elem.style ??= [] - elem.style.push('heading-'+level) - return text } } - var style = function(style){ - return function(_, text){ - elem.style ??= [] - elem.style.push(style) - return text } } + elem.style.push(...style) + return code + ?? text } } elem.text = code // hidden attributes... // XXX make this generic... @@ -328,12 +324,12 @@ var Outline = { // elements... .replace(/(\n|^)---*\h*(\n|$)/m, '$1
') // ToDo... - .replace(/^TODO\s*(.*)$/m, '$1') - .replace(/^DONE\s*(.*)$/m, '$1') + .replace(/^TODO\s*/m, style('todo', '')) + .replace(/^DONE\s*/m, style('todo', '')) // checkboxes... // XXX these can not be clicked (yet)... - .replace(/\[_\]/gm, '') - .replace(/\[[X]\]/gm, '') + .replace(/\[_\]/gm, style('check', '')) + .replace(/\[[X]\]/gm, style('check', '')) // basic styling... // XXX these are quite naive... .replace(/\*(.*)\*/gm, '$1') @@ -427,6 +423,7 @@ var Outline = { var block = document.createElement('div') block.setAttribute('tabindex', '0') var text = document.createElement('textarea') + .autoUpdateSize() var html = document.createElement('span') block.append(text, html) this.update(block, data) @@ -437,9 +434,12 @@ var Outline = { 'before' : place ;(place == 'next' - && (cur.querySelector('[tabindex]') + && (cur.querySelector('[tabindex]') || cur.nextElementSibling)) ? this.get(place).before(block) + : (place == 'next' + && !cur.nextElementSibling) ? + cur.after(block) : (place == 'before' || place == 'after') ? cur[place](block) : undefined } @@ -609,6 +609,10 @@ var Outline = { outline.addEventListener('click', function(evt){ var elem = evt.target + // expand/collapse... + // XXX + if(elem.getAttribute('collapsed')){ + } // todo: toggle checkbox... if(elem.classList.contains('todo')){ var node = elem.parentElement.parentElement diff --git a/experiments/outline-editor/index.html b/experiments/outline-editor/index.html index 92785b3..e6de346 100755 --- a/experiments/outline-editor/index.html +++ b/experiments/outline-editor/index.html @@ -28,7 +28,14 @@ var setup = function(){
- # Outline editor prototype + - An outline-based markdown editor experiment + - ### Infuences:: + - Logseq + - Tomboy + - Bonsai + - - ## ToDo + - read-only mode - editor: bksapce/del at start/end of a block should join it with prev/next - editor: pressing enter in text edit mode should split text into two blocks - editor: caret @@ -51,7 +58,8 @@ var setup = function(){ - indent/deindent - edit node - copy/paste nodes/trees - - markdown tables + - markdown: tables + - empty item height is a bit off... - ~serialize~/deserialize - ~add optional text styling to nodes~ - @@ -77,8 +85,9 @@ var setup = function(){ - Basic inline *bold*, _italic_ and ~striked~ - To do items - TODO undone item - - DONE done item _(clicking the checkbox updates the item)_ + - DONE done item + - [_] a different way to draw a checkbox - Inline [X] checkboxes [_] - A collapsed:: true