Compare commits

..

No commits in common. "4998ac950cc9ea40a42192078768fbb00a5c1412" and "a8797669feaab289c82580ba1ef4e118af82c949" have entirely different histories.

3 changed files with 49 additions and 187 deletions

View File

@ -1,24 +1,9 @@
/*********************************************************************/
:root { :root {
--font-size: 5mm; --font-size: 5mm;
--outline-padding: 5rem;
--item-indent: 2rem;
--item-padding: 0.2em; --item-padding: 0.2em;
--checkbox-size: 1.5rem;
--button-size: 2em; --button-size: 2em;
}
/*********************************************************************/
:root {
font-family: sans-serif; font-family: sans-serif;
font-size: var(--font-size); font-size: var(--font-size);
@ -40,26 +25,22 @@
.editor .outline { .editor .outline {
display: block; display: block;
position: relative; position: relative;
/* XXX do a better calculation... */ /* XXX do a better calculation... */
width: calc(100% - var(--button-size) - var(--outline-padding) * 2); width: calc(100% - (var(--button-size) + 4em));
padding-left: var(--outline-padding);
padding-right: var(--outline-padding);
} }
.editor .outline [tabindex] { .editor .outline [tabindex] {
position: relative; position: relative;
} }
.editor .outline [tabindex] [tabindex] { .editor .outline div [tabindex] {
margin-left: var(--item-indent); margin-left: 2em;
} }
.editor .outline [tabindex]>span, .editor .outline [tabindex]>span,
.editor .outline [tabindex]>textarea { .editor .outline [tabindex]>textarea {
display: block; display: block;
width: 100%; width: 100%;
/* XXX this is a tiny bit off and using textarea's height here is off too... */ /* XXX this is a tiny bit off and using textarea's height here is off too... */
min-height: calc(1em + var(--item-padding) * 2); min-height: 1em;
padding-top: var(--item-padding); padding-top: var(--item-padding);
padding-bottom: var(--item-padding); padding-bottom: var(--item-padding);
padding-left: 0; padding-left: 0;
@ -74,9 +55,6 @@
outline: none; outline: none;
border: none; border: none;
} }
.editor .outline [tabindex]>span:blank {
content: " ";
}
.editor .outline [tabindex]>textarea { .editor .outline [tabindex]>textarea {
height: calc(2 * var(--item-padding) + 1em); height: calc(2 * var(--item-padding) + 1em);
overflow: hidden; overflow: hidden;
@ -109,67 +87,7 @@
.editor .outline div[collapsed] { .editor .outline div[collapsed] {
border-bottom: solid 1px silver; border-bottom: solid 1px silver;
} }
/* expand/collapse button... */ .editor .outline div[collapsed] div {
.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]:before {
content: "";
position: absolute;
display: inline-block;
right: -2rem;
width: 2rem;
height: calc(1em + var(--item-padding) * 2);
background: transparent;
}
/*
.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; display: none;
} }
@ -201,7 +119,6 @@
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
/* Styles */ /* Styles */
/* Headings... */
.editor .outline .heading-1>span, .editor .outline .heading-1>span,
.editor .outline .heading-1>textarea, .editor .outline .heading-1>textarea,
.editor .outline .heading-2>span, .editor .outline .heading-2>span,
@ -222,7 +139,7 @@
.editor .outline .heading-2>textarea, .editor .outline .heading-2>textarea,
.editor .outline .heading-3>span, .editor .outline .heading-3>span,
.editor .outline .heading-3>textarea { .editor .outline .heading-3>textarea {
border-bottom: solid 1px rgba(0,0,0,0.1); border-bottom: solid 1px rgba(0,0,0,0.05);
} }
.editor .outline .heading-1>span, .editor .outline .heading-1>span,
@ -278,48 +195,24 @@
color: silver; 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));
/* checkboxes... */ top: calc(0.6em + var(--item-padding));
:root { height: var(--size);
/* NOTE: this must have a unit... */ width: var(--size);
/* XXX move this to the config when fixed... */ margin-top: calc(var(--size) / -2);
/* 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 */ /* NOTE: this appears to be needed for the em sizes above to work correctly */
font-size: 1em; font-size: 1em;
} }
.editor .outline [tabindex].todo>span>input[type=checkbox]:first-child { .editor .outline [tabindex]>span>input[type=checkbox].todo {
margin-left: calc( position: absolute;
var(--checkbox-size) * -1 margin-left: calc(-1 * var(--size) - var(--item-padding) * 2);
- var(--item-padding)
- var(--checkbox-margin) * 2);
margin-right: calc(
var(--item-padding)
+ var(--checkbox-margin));
} }
/* XXX need to remove left margin from strictly the first itme in block... */ .editor .outline [tabindex]>span>input[type=checkbox].check {
.editor .outline [tabindex].check>span>input[type=checkbox] { transform: translateY(calc(2 * var(--item-padding)));
/*margin-left: 0;*/
} }

View File

@ -195,7 +195,7 @@ var Outline = {
// heading... // heading...
parsed.style ? parsed.style ?
node.classList.add(...parsed.style) node.classList.add(...parsed.style)
: node.classList.remove(...this.__styles) : node.classList.remove(...this.__styles__)
} else { } else {
html.innerHTML = data.text } html.innerHTML = data.text }
text.value = data.text text.value = data.text
@ -259,9 +259,7 @@ var Outline = {
if(elem.classList.contains('focused')){ if(elem.classList.contains('focused')){
// XXX need to be able to get the next elem on same level... // XXX need to be able to get the next elem on same level...
this.toggleCollapse(elem, true) this.toggleCollapse(elem, true)
next = elem === this.get(-1) ? next = this.get(elem, 'next') }
this.get(elem, 'prev')
: this.get(elem, 'next') }
elem?.remove() elem?.remove()
next?.focus() next?.focus()
return this }, return this },
@ -271,27 +269,33 @@ var Outline = {
return this }, return this },
// block serialization... // block serialization...
// XXX shouild we support headings + other formatting per block???
// XXX split this up into a generic handler + plugins... // XXX split this up into a generic handler + plugins...
// XXX need a way to filter input text... // XXX need a way to filter input text...
// use-case: hidden attributes... // use-case: hidden attributes...
// NOTE: this is auto-populated by .__code2html__(..) __styles__: [
__styles: undefined, 'heading-1',
'heading-2',
'heading-3',
'heading-4',
'heading-5',
'heading-6',
'list',
],
__code2html__: function(code){ __code2html__: function(code){
var that = this
var elem = { var elem = {
collapsed: false, collapsed: false,
} }
var style = function(style, code=undefined){ var heading = function(level){
style = [style].flat()
that.__styles = [...new Set([
...(that.__styles ?? []),
...style,
])]
return function(_, text){ return function(_, text){
elem.style ??= [] elem.style ??= []
elem.style.push(...style) elem.style.push('heading-'+level)
return code return text } }
?? text } } var style = function(style){
return function(_, text){
elem.style ??= []
elem.style.push(style)
return text } }
elem.text = code elem.text = code
// hidden attributes... // hidden attributes...
// XXX make this generic... // XXX make this generic...
@ -324,12 +328,12 @@ var Outline = {
// elements... // elements...
.replace(/(\n|^)---*\h*(\n|$)/m, '$1<hr>') .replace(/(\n|^)---*\h*(\n|$)/m, '$1<hr>')
// ToDo... // ToDo...
.replace(/^TODO\s*/m, style('todo', '<input type="checkbox">')) .replace(/^TODO\s*(.*)$/m, '<input class="todo" type="checkbox">$1')
.replace(/^DONE\s*/m, style('todo', '<input type="checkbox" checked>')) .replace(/^DONE\s*(.*)$/m, '<input class="todo" type="checkbox" checked>$1')
// checkboxes... // checkboxes...
// XXX these can not be clicked (yet)... // XXX these can not be clicked (yet)...
.replace(/\[_\]/gm, style('check', '<input class="check" type="checkbox">')) .replace(/\[_\]/gm, '<input class="check" type="checkbox">')
.replace(/\[[X]\]/gm, style('check', '<input class="check" type="checkbox" checked>')) .replace(/\[[X]\]/gm, '<input class="check" type="checkbox" checked>')
// basic styling... // basic styling...
// XXX these are quite naive... // XXX these are quite naive...
.replace(/\*(.*)\*/gm, '<b>$1</b>') .replace(/\*(.*)\*/gm, '<b>$1</b>')
@ -423,7 +427,6 @@ var Outline = {
var block = document.createElement('div') var block = document.createElement('div')
block.setAttribute('tabindex', '0') block.setAttribute('tabindex', '0')
var text = document.createElement('textarea') var text = document.createElement('textarea')
.autoUpdateSize()
var html = document.createElement('span') var html = document.createElement('span')
block.append(text, html) block.append(text, html)
this.update(block, data) this.update(block, data)
@ -434,12 +437,9 @@ var Outline = {
'before' 'before'
: place : place
;(place == 'next' ;(place == 'next'
&& (cur.querySelector('[tabindex]') && (cur.querySelector('[tabindex]')
|| cur.nextElementSibling)) ? || cur.nextElementSibling)) ?
this.get(place).before(block) this.get(place).before(block)
: (place == 'next'
&& !cur.nextElementSibling) ?
cur.after(block)
: (place == 'before' || place == 'after') ? : (place == 'before' || place == 'after') ?
cur[place](block) cur[place](block)
: undefined } : undefined }
@ -596,9 +596,6 @@ var Outline = {
: focused.setAttribute('selected', '') }, : focused.setAttribute('selected', '') },
}, },
// XXX might be a good idea to defer specific actions to event-like
// handlers...
// e.g. clicking left if block -> .blockleft(..) ... etc.
setup: function(dom){ setup: function(dom){
var that = this var that = this
this.dom = dom this.dom = dom
@ -612,34 +609,16 @@ var Outline = {
outline.addEventListener('click', outline.addEventListener('click',
function(evt){ function(evt){
var elem = evt.target var elem = evt.target
// expand/collapse
if(elem.nodeName == 'DIV'
&& elem.getAttribute('tabindex')){
// click: left of elem (outside)
if(evt.offsetX < 0){
// XXX item menu?
// click: right of elem (outside)
} else if(elem.offsetWidth < evt.offsetX){
that.toggleCollapse(elem)
// click inside element...
} else {
// XXX
}
// todo: toggle checkbox... // todo: toggle checkbox...
} else if(elem.classList.contains('todo')){ if(elem.classList.contains('todo')){
var node = elem.parentElement.parentElement var node = elem.parentElement.parentElement
var text = node.querySelector('textarea') var text = node.querySelector('textarea')
text.value = text.value =
elem.checked ? elem.checked ?
text.value.replace(/^\s*TODO(\s*)/, 'DONE$1') text.value.replace(/^\s*TODO(\s*)/, 'DONE$1')
: text.value.replace(/^\s*DONE(\s*)/, 'TODO$1') : text.value.replace(/^\s*DONE(\s*)/, 'TODO$1') }
// check: toggle checkbox... // check: toggle checkbox...
} else if(elem.classList.contains('check')){ if(elem.classList.contains('check')){
var node = elem.parentElement.parentElement var node = elem.parentElement.parentElement
var text = node.querySelector('textarea') var text = node.querySelector('textarea')
var i = [...node.querySelectorAll('.check')].indexOf(elem) var i = [...node.querySelectorAll('.check')].indexOf(elem)

View File

@ -28,15 +28,7 @@ var setup = function(){
<!-- code --> <!-- code -->
<div class="code"> <div class="code">
- # Outline editor prototype - # Outline editor prototype
- An outline-based markdown editor experiment
- ### Infuences::
- Logseq
- Tomboy
- Bonsai
-
- ## ToDo - ## ToDo
- do a better expand/collapse icon on hover
- read-only mode
- editor: bksapce/del at start/end of a block should join it with prev/next - 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: pressing enter in text edit mode should split text into two blocks
- editor: caret - editor: caret
@ -59,8 +51,7 @@ var setup = function(){
- indent/deindent - indent/deindent
- edit node - edit node
- copy/paste nodes/trees - copy/paste nodes/trees
- markdown: tables - markdown tables
- empty item height is a bit off...
- ~serialize~/deserialize - ~serialize~/deserialize
- ~add optional text styling to nodes~ - ~add optional text styling to nodes~
- -
@ -86,9 +77,8 @@ var setup = function(){
- Basic inline *bold*, _italic_ and ~striked~ - Basic inline *bold*, _italic_ and ~striked~
- To do items - To do items
- TODO undone item - TODO undone item
_(clicking the checkbox updates the item)_
- DONE done item - DONE done item
- [_] a different way to draw a checkbox _(clicking the checkbox updates the item)_
- Inline [X] checkboxes [_] - Inline [X] checkboxes [_]
- A - A
collapsed:: true collapsed:: true