mirror of
https://github.com/flynx/pWiki.git
synced 2025-12-25 12:21:58 +00:00
Compare commits
No commits in common. "a8797669feaab289c82580ba1ef4e118af82c949" and "69f294cec3f45a8ea55afd920a0685be737c933e" have entirely different histories.
a8797669fe
...
69f294cec3
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-size: 5mm;
|
--font-size: 5mm;
|
||||||
--item-padding: 0.2em;
|
|
||||||
--button-size: 2em;
|
--button-size: 2em;
|
||||||
|
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
@ -37,16 +36,14 @@
|
|||||||
}
|
}
|
||||||
.editor .outline [tabindex]>span,
|
.editor .outline [tabindex]>span,
|
||||||
.editor .outline [tabindex]>textarea {
|
.editor .outline [tabindex]>textarea {
|
||||||
|
--padding: 0.2em;
|
||||||
|
|
||||||
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: 1em;
|
min-height: 1em;
|
||||||
padding-top: var(--item-padding);
|
padding: var(--padding);
|
||||||
padding-bottom: var(--item-padding);
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
@ -56,7 +53,7 @@
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
.editor .outline [tabindex]>textarea {
|
.editor .outline [tabindex]>textarea {
|
||||||
height: calc(2 * var(--item-padding) + 1em);
|
height: calc(2 * var(--padding) + 1em);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
@ -133,15 +130,6 @@
|
|||||||
.editor .outline .heading-6>textarea {
|
.editor .outline .heading-6>textarea {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.editor .outline .heading-1>span,
|
|
||||||
.editor .outline .heading-1>textarea,
|
|
||||||
.editor .outline .heading-2>span,
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor .outline .heading-1>span,
|
.editor .outline .heading-1>span,
|
||||||
.editor .outline .heading-1>textarea {
|
.editor .outline .heading-1>textarea {
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
@ -167,20 +155,16 @@
|
|||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX needs to be in the middle of the first span but with universal size... */
|
/* XXX EXPERIMENTAL -- not sure about this... */
|
||||||
.editor .outline .list-item:before,
|
.editor .outline .list-item:before,
|
||||||
.editor .outline .list>[tabindex]>span:before {
|
.editor .outline .list>[tabindex]:before {
|
||||||
--size: 0.5rem;
|
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "";
|
content: "";
|
||||||
top: calc(0.6em + var(--item-padding));
|
top: 0.6em;
|
||||||
left: calc(var(--size) * -2);
|
left: -0.8em;
|
||||||
width: var(--size);
|
width: 0.5em;
|
||||||
height: var(--size);
|
height: 0.5em;
|
||||||
margin-top: calc(var(--size) / -2);
|
|
||||||
|
|
||||||
background: silver;
|
background: silver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,31 +172,15 @@
|
|||||||
background: yellow;
|
background: yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor.hide-comments .outline .comment {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.editor .outline .comment>span {
|
.editor .outline .comment>span {
|
||||||
color: silver;
|
color: silver;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor .outline [tabindex]>span>input[type=checkbox].check,
|
.editor .outline [tabindex]>span>input[type=checkbox] {
|
||||||
.editor .outline [tabindex]>span>input[type=checkbox].todo {
|
--width: 3em;
|
||||||
--size: 1.5rem;
|
|
||||||
--height: calc(var(--font-size) + 2 * var(--item-padding));
|
|
||||||
|
|
||||||
top: calc(0.6em + var(--item-padding));
|
height: 1em;
|
||||||
height: var(--size);
|
width: var(--width);
|
||||||
width: var(--size);
|
margin-left: calc(-1 * var(--width));
|
||||||
margin-top: calc(var(--size) / -2);
|
|
||||||
|
|
||||||
/* 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]>span>input[type=checkbox].check {
|
|
||||||
transform: translateY(calc(2 * var(--item-padding)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,8 +27,46 @@ var atLine = function(elem, index){
|
|||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
// XXX experiment with a concatinative model...
|
var Node = {
|
||||||
// .get(..) -> Outline (view)
|
dom: undefined,
|
||||||
|
document: undefined,
|
||||||
|
|
||||||
|
get: function(){},
|
||||||
|
|
||||||
|
get root(){},
|
||||||
|
get parent(){},
|
||||||
|
get children(){},
|
||||||
|
get next(){},
|
||||||
|
get prev(){},
|
||||||
|
|
||||||
|
focus: function(){},
|
||||||
|
edit: function(){},
|
||||||
|
|
||||||
|
indent: function(){ },
|
||||||
|
deindent: function(){ },
|
||||||
|
toggleCollapse: function(){ },
|
||||||
|
|
||||||
|
remove: function(){},
|
||||||
|
|
||||||
|
json: function(){},
|
||||||
|
text: function(){},
|
||||||
|
|
||||||
|
load: function(){},
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeGroup = {
|
||||||
|
__proto__: Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX should this be Page or root??
|
||||||
|
var Root = {
|
||||||
|
__proto__: NodeGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// XXX might be a good idea to do a view-action model...
|
||||||
var Outline = {
|
var Outline = {
|
||||||
dom: undefined,
|
dom: undefined,
|
||||||
|
|
||||||
@ -181,10 +219,9 @@ var Outline = {
|
|||||||
// XXX should this handle children???
|
// XXX should this handle children???
|
||||||
update: function(node='focused', data){
|
update: function(node='focused', data){
|
||||||
var node = this.get(node)
|
var node = this.get(node)
|
||||||
typeof(data.collapsed) == 'boolean'
|
data.collapsed ?
|
||||||
&& (data.collapsed ?
|
|
||||||
node.setAttribute('collapsed', '')
|
node.setAttribute('collapsed', '')
|
||||||
: node.removeAttribute('collapsed'))
|
: node.removeAttribute('collapsed')
|
||||||
if(data.text){
|
if(data.text){
|
||||||
var text = node.querySelector('textarea')
|
var text = node.querySelector('textarea')
|
||||||
var html = node.querySelector('span')
|
var html = node.querySelector('span')
|
||||||
@ -194,7 +231,7 @@ var Outline = {
|
|||||||
html.innerHTML = parsed.text
|
html.innerHTML = parsed.text
|
||||||
// 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 }
|
||||||
@ -269,10 +306,9 @@ var Outline = {
|
|||||||
return this },
|
return this },
|
||||||
|
|
||||||
// block serialization...
|
// block serialization...
|
||||||
|
// XXX STUB...
|
||||||
// XXX shouild we support headings + other formatting per block???
|
// XXX shouild we support headings + other formatting per block???
|
||||||
// XXX split this up into a generic handler + plugins...
|
// XXX these should be symetrical -- now one returns text the other an object...
|
||||||
// XXX need a way to filter input text...
|
|
||||||
// use-case: hidden attributes...
|
|
||||||
__styles__: [
|
__styles__: [
|
||||||
'heading-1',
|
'heading-1',
|
||||||
'heading-2',
|
'heading-2',
|
||||||
@ -288,13 +324,11 @@ var Outline = {
|
|||||||
}
|
}
|
||||||
var heading = function(level){
|
var heading = function(level){
|
||||||
return function(_, text){
|
return function(_, text){
|
||||||
elem.style ??= []
|
elem.style = 'heading-'+level
|
||||||
elem.style.push('heading-'+level)
|
|
||||||
return text } }
|
return text } }
|
||||||
var style = function(style){
|
var style = function(style){
|
||||||
return function(_, text){
|
return function(_, text){
|
||||||
elem.style ??= []
|
elem.style = style
|
||||||
elem.style.push(style)
|
|
||||||
return text } }
|
return text } }
|
||||||
elem.text = code
|
elem.text = code
|
||||||
// hidden attributes...
|
// hidden attributes...
|
||||||
@ -308,37 +342,33 @@ var Outline = {
|
|||||||
// id...
|
// id...
|
||||||
.replace(/(\n|^)\s*id::\s*(.*)\s*(\n|$)/,
|
.replace(/(\n|^)\s*id::\s*(.*)\s*(\n|$)/,
|
||||||
function(_, value){
|
function(_, value){
|
||||||
elem.id = value.trim()
|
elem.collapsed = value.trim() == 'true'
|
||||||
return '' })
|
return '' })
|
||||||
// markdown...
|
// markdown...
|
||||||
// style: headings...
|
|
||||||
.replace(/^######\s*(.*)$/m, style('heading-6'))
|
|
||||||
.replace(/^#####\s*(.*)$/m, style('heading-5'))
|
|
||||||
.replace(/^####\s*(.*)$/m, style('heading-4'))
|
|
||||||
.replace(/^###\s*(.*)$/m, style('heading-3'))
|
|
||||||
.replace(/^##\s*(.*)$/m, style('heading-2'))
|
|
||||||
.replace(/^#\s*(.*)$/m, style('heading-1'))
|
|
||||||
// style: list...
|
|
||||||
.replace(/^[-\*]\s+(.*)$/m, style('list-item'))
|
|
||||||
.replace(/^\s*(.*):\s*$/m, style('list'))
|
|
||||||
// style: misc...
|
|
||||||
.replace(/^((\/\/|;)\s+.*)$/m, style('comment'))
|
|
||||||
.replace(/^XXX\s+(.*)$/m, style('XXX'))
|
|
||||||
.replace(/^(.*)\s*XXX$/m, style('XXX'))
|
|
||||||
// elements...
|
|
||||||
.replace(/(\n|^)---*\h*(\n|$)/m, '$1<hr>')
|
|
||||||
// ToDo...
|
// ToDo...
|
||||||
.replace(/^TODO\s*(.*)$/m, '<input class="todo" type="checkbox">$1')
|
.replace(/^TODO\s*(.*)$/, '<input type="checkbox"> $1')
|
||||||
.replace(/^DONE\s*(.*)$/m, '<input class="todo" type="checkbox" checked>$1')
|
.replace(/^DONE\s*(.*)$/, '<input type="checkbox" checked> $1')
|
||||||
// checkboxes...
|
// style: headings...
|
||||||
// XXX these can not be clicked (yet)...
|
.replace(/^######\s*(.*)$/, style('heading-6'))
|
||||||
.replace(/\[_\]/gm, '<input class="check" type="checkbox">')
|
.replace(/^#####\s*(.*)$/, style('heading-5'))
|
||||||
.replace(/\[[X]\]/gm, '<input class="check" type="checkbox" checked>')
|
.replace(/^####\s*(.*)$/, style('heading-4'))
|
||||||
|
.replace(/^###\s*(.*)$/, style('heading-3'))
|
||||||
|
.replace(/^##\s*(.*)$/, style('heading-2'))
|
||||||
|
.replace(/^#\s*(.*)$/, style('heading-1'))
|
||||||
|
// style: list...
|
||||||
|
.replace(/^[-\*]\s+(.*)$/, style('list-item'))
|
||||||
|
.replace(/^\s*(.*):\s*$/, style('list'))
|
||||||
|
// style: misc...
|
||||||
|
.replace(/^((\/\/|;)\s+.*)$/, style('comment'))
|
||||||
|
.replace(/^XXX\s+(.*)$/, style('XXX'))
|
||||||
|
.replace(/^(.*)\s*XXX$/, style('XXX'))
|
||||||
// basic styling...
|
// basic styling...
|
||||||
// XXX these are quite naive...
|
// XXX these are quite naive...
|
||||||
.replace(/\*(.*)\*/gm, '<b>$1</b>')
|
.replace(/\*(.*)\*/g, '<b>$1</b>')
|
||||||
.replace(/~([^~]*)~/gm, '<s>$1</s>')
|
.replace(/~([^~]*)~/g, '<s>$1</s>')
|
||||||
.replace(/_([^_]*)_/gm, '<i>$1</i>')
|
.replace(/_([^_]*)_/g, '<i>$1</i>')
|
||||||
|
// elements...
|
||||||
|
.replace(/(\n|^)---*\h*(\n|$)/, '$1<hr>')
|
||||||
return elem },
|
return elem },
|
||||||
|
|
||||||
// serialization...
|
// serialization...
|
||||||
@ -430,19 +460,9 @@ var Outline = {
|
|||||||
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)
|
||||||
// place...
|
|
||||||
var cur = this.get()
|
var cur = this.get()
|
||||||
if(place && cur){
|
place && cur
|
||||||
place = place == 'prev' ?
|
&& cur[place](block)
|
||||||
'before'
|
|
||||||
: place
|
|
||||||
;(place == 'next'
|
|
||||||
&& (cur.querySelector('[tabindex]')
|
|
||||||
|| cur.nextElementSibling)) ?
|
|
||||||
this.get(place).before(block)
|
|
||||||
: (place == 'before' || place == 'after') ?
|
|
||||||
cur[place](block)
|
|
||||||
: undefined }
|
|
||||||
return block },
|
return block },
|
||||||
load: function(data){
|
load: function(data){
|
||||||
var that = this
|
var that = this
|
||||||
@ -461,9 +481,6 @@ var Outline = {
|
|||||||
.clear()
|
.clear()
|
||||||
.outline
|
.outline
|
||||||
.append(...level(data))
|
.append(...level(data))
|
||||||
// update sizes of all the textareas (transparent)...
|
|
||||||
for(var e of [...this.outline.querySelectorAll('textarea')]){
|
|
||||||
e.updateSize() }
|
|
||||||
return this },
|
return this },
|
||||||
|
|
||||||
sync: function(){
|
sync: function(){
|
||||||
@ -552,31 +569,25 @@ var Outline = {
|
|||||||
O: function(evt){
|
O: function(evt){
|
||||||
if(evt.target.nodeName != 'TEXTAREA'){
|
if(evt.target.nodeName != 'TEXTAREA'){
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.Block('before')
|
this.Block('before')?.querySelector('textarea')?.focus() } },
|
||||||
?.querySelector('textarea')
|
|
||||||
?.focus() } },
|
|
||||||
o: function(evt){
|
o: function(evt){
|
||||||
if(evt.target.nodeName != 'TEXTAREA'){
|
if(evt.target.nodeName != 'TEXTAREA'){
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.Block('next')
|
this.Block('after')?.querySelector('textarea')?.focus() } },
|
||||||
?.querySelector('textarea')
|
|
||||||
?.focus() } },
|
|
||||||
Enter: function(evt){
|
Enter: function(evt){
|
||||||
|
/*if(evt.target.isContentEditable){
|
||||||
|
// XXX create new node...
|
||||||
|
return }
|
||||||
|
//*/
|
||||||
if(evt.ctrlKey
|
if(evt.ctrlKey
|
||||||
|| evt.shiftKey){
|
|| evt.shiftKey){
|
||||||
return }
|
return }
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
evt.target.nodeName == 'TEXTAREA' ?
|
evt.target.nodeName == 'TEXTAREA' ?
|
||||||
this.Block('next')
|
this.Block('after')?.querySelector('textarea')?.focus()
|
||||||
?.querySelector('textarea')
|
: this.get()?.querySelector('textarea')?.focus() },
|
||||||
?.focus()
|
|
||||||
: this.get()
|
|
||||||
?.querySelector('textarea')
|
|
||||||
?.focus() },
|
|
||||||
Escape: function(evt){
|
Escape: function(evt){
|
||||||
this.outline.querySelector('textarea:focus')
|
this.outline.querySelector('textarea:focus')?.parentElement?.focus() },
|
||||||
?.parentElement
|
|
||||||
?.focus() },
|
|
||||||
Delete: function(evt){
|
Delete: function(evt){
|
||||||
if(this.get('edited')){
|
if(this.get('edited')){
|
||||||
return }
|
return }
|
||||||
@ -609,27 +620,14 @@ var Outline = {
|
|||||||
outline.addEventListener('click',
|
outline.addEventListener('click',
|
||||||
function(evt){
|
function(evt){
|
||||||
var elem = evt.target
|
var elem = evt.target
|
||||||
// todo: toggle checkbox...
|
// toggle checkbox...
|
||||||
if(elem.classList.contains('todo')){
|
if(elem.nodeName == 'INPUT' && elem.type == 'checkbox'){
|
||||||
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...
|
|
||||||
if(elem.classList.contains('check')){
|
|
||||||
var node = elem.parentElement.parentElement
|
|
||||||
var text = node.querySelector('textarea')
|
|
||||||
var i = [...node.querySelectorAll('.check')].indexOf(elem)
|
|
||||||
var to = elem.checked ?
|
|
||||||
'[X]'
|
|
||||||
: '[_]'
|
|
||||||
var toggle = function(m){
|
|
||||||
return i-- == 0 ?
|
|
||||||
to
|
|
||||||
: m }
|
|
||||||
text.value = text.value.replace(/\[[X_]\]/g, toggle) } })
|
|
||||||
// heboard handling...
|
// heboard handling...
|
||||||
outline.addEventListener('keydown',
|
outline.addEventListener('keydown',
|
||||||
function(evt){
|
function(evt){
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link href="editor.css" rel="stylesheet"/>
|
<link href="editor.css" rel="stylesheet"/>
|
||||||
@ -28,7 +27,8 @@ var setup = function(){
|
|||||||
<!-- code -->
|
<!-- code -->
|
||||||
<div class="code">
|
<div class="code">
|
||||||
- # Outline editor prototype
|
- # Outline editor prototype
|
||||||
- ## ToDo
|
- ## TODO
|
||||||
|
- editor: enter on an expanded parent node should create child (currently next sibling)
|
||||||
- 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
|
||||||
@ -51,7 +51,6 @@ var setup = function(){
|
|||||||
- indent/deindent
|
- indent/deindent
|
||||||
- edit node
|
- edit node
|
||||||
- copy/paste nodes/trees
|
- copy/paste nodes/trees
|
||||||
- markdown tables
|
|
||||||
- ~serialize~/deserialize
|
- ~serialize~/deserialize
|
||||||
- ~add optional text styling to nodes~
|
- ~add optional text styling to nodes~
|
||||||
-
|
-
|
||||||
@ -72,14 +71,12 @@ var setup = function(){
|
|||||||
- // C-style comment
|
- // C-style comment
|
||||||
- ; ASM-style comment
|
- ; ASM-style comment
|
||||||
- XXX Highlight
|
- XXX Highlight
|
||||||
- Line
|
- line
|
||||||
- ---
|
- ---
|
||||||
- Basic inline *bold*, _italic_ and ~striked~
|
- basic inline *bold*, _italic_ and ~striked~
|
||||||
- To do items
|
- To do items
|
||||||
- TODO undone item
|
- TODO undone item
|
||||||
- DONE done item
|
- DONE done item
|
||||||
_(clicking the checkbox updates the item)_
|
|
||||||
- Inline [X] checkboxes [_]
|
|
||||||
- A
|
- A
|
||||||
collapsed:: true
|
collapsed:: true
|
||||||
- a
|
- a
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user