mirror of
https://github.com/flynx/pWiki.git
synced 2025-12-25 04:11:56 +00:00
Compare commits
6 Commits
9ecbf4e060
...
0e4344a711
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e4344a711 | |||
| 12bd9ffebf | |||
| 66e0f112fd | |||
| 69701f0697 | |||
| 4c9597e27b | |||
| 857c1ae9a7 |
@ -50,6 +50,7 @@
|
|||||||
.editor .children {
|
.editor .children {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor .header,
|
||||||
.editor .outline {
|
.editor .outline {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -68,12 +69,30 @@
|
|||||||
.editor .outline:empty:hover:after {
|
.editor .outline:empty:hover:after {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXX header */
|
||||||
|
.editor .header {
|
||||||
|
}
|
||||||
|
.editor .header:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* XXX needs more work... *//*
|
||||||
|
.editor .header span {
|
||||||
|
display: inline;
|
||||||
|
max-width: 10rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
.editor .outline .block {
|
.editor .outline .block {
|
||||||
position: relative;
|
position: relative;
|
||||||
outline: none;
|
outline: none;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.editor.block-offsets .outline .block {
|
||||||
|
border-left: solid 1px silver;
|
||||||
|
}
|
||||||
.editor .outline .block .block {
|
.editor .outline .block .block {
|
||||||
margin-left: var(--item-indent);
|
margin-left: var(--item-indent);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -675,6 +675,8 @@ var Outline = {
|
|||||||
return value },
|
return value },
|
||||||
|
|
||||||
|
|
||||||
|
get header(){
|
||||||
|
return this.dom.querySelector('.header') },
|
||||||
get code(){
|
get code(){
|
||||||
return this.dom.querySelector('.code') },
|
return this.dom.querySelector('.code') },
|
||||||
get outline(){
|
get outline(){
|
||||||
@ -683,17 +685,29 @@ var Outline = {
|
|||||||
return this.dom.querySelector('.toolbar') },
|
return this.dom.querySelector('.toolbar') },
|
||||||
|
|
||||||
|
|
||||||
path: function(node='focused'){
|
path: function(node='focused', mode='index'){
|
||||||
|
if(['index', 'text', 'node'].includes(node)){
|
||||||
|
mode = node
|
||||||
|
node = 'focused' }
|
||||||
var outline = this.outline
|
var outline = this.outline
|
||||||
var path = []
|
var path = []
|
||||||
var node = this.get(node)
|
var node = this.get(node)
|
||||||
while(node != outline){
|
while(node != outline){
|
||||||
path.unshift(this.get(node, 'siblings').indexOf(node))
|
path.unshift(
|
||||||
|
mode == 'index' ?
|
||||||
|
this.get(node, 'siblings').indexOf(node)
|
||||||
|
: mode == 'text' ?
|
||||||
|
node.querySelector('.view').innerText
|
||||||
|
: node)
|
||||||
node = this.get(node, 'parent') }
|
node = this.get(node, 'parent') }
|
||||||
return path },
|
return path },
|
||||||
|
|
||||||
//
|
//
|
||||||
// .get([<offset>])
|
// .get(<index>)[, <offset>]
|
||||||
|
// .get(<path>[, <offset>])
|
||||||
|
// .get(<id>[, <offset>)
|
||||||
|
// -> <node>
|
||||||
|
//
|
||||||
// .get('focused'[, <offset>])
|
// .get('focused'[, <offset>])
|
||||||
// -> <node>
|
// -> <node>
|
||||||
//
|
//
|
||||||
@ -736,6 +750,10 @@ var Outline = {
|
|||||||
: offset
|
: offset
|
||||||
var outline = this.outline
|
var outline = this.outline
|
||||||
|
|
||||||
|
// id...
|
||||||
|
if(typeof(node) == 'string' && node[0] == '#'){
|
||||||
|
node = outline.querySelector(node) }
|
||||||
|
|
||||||
// root nodes...
|
// root nodes...
|
||||||
if(node == 'top'){
|
if(node == 'top'){
|
||||||
return [...outline.children] }
|
return [...outline.children] }
|
||||||
@ -847,13 +865,19 @@ var Outline = {
|
|||||||
var elem = this.get(...arguments)
|
var elem = this.get(...arguments)
|
||||||
?? this.get(0)
|
?? this.get(0)
|
||||||
if(elem){
|
if(elem){
|
||||||
|
var cur = this.get()
|
||||||
|
var blocks = this.get('visible')
|
||||||
elem.focus({preventScroll: true})
|
elem.focus({preventScroll: true})
|
||||||
;(elem.classList.contains('code') ?
|
;(elem.classList.contains('code') ?
|
||||||
elem
|
elem
|
||||||
: elem.querySelector('.code'))
|
: elem.querySelector('.code'))
|
||||||
.scrollIntoView({
|
.scrollIntoView({
|
||||||
block: 'nearest',
|
block: 'nearest',
|
||||||
//behavior: 'smooth',
|
// smooth for long jumps and instant for short jumps...
|
||||||
|
behavior: (cur == null
|
||||||
|
|| Math.abs(blocks.indexOf(cur) - blocks.indexOf(elem)) > 2) ?
|
||||||
|
'smooth'
|
||||||
|
: 'instant'
|
||||||
}) }
|
}) }
|
||||||
return elem },
|
return elem },
|
||||||
edit: function(node='focused', offset){
|
edit: function(node='focused', offset){
|
||||||
@ -1049,23 +1073,35 @@ var Outline = {
|
|||||||
this.__change__()
|
this.__change__()
|
||||||
return this },
|
return this },
|
||||||
|
|
||||||
|
|
||||||
// crop...
|
// crop...
|
||||||
// XXX add crop/path indicator...
|
// XXX add crop-level/path indicator...
|
||||||
__crop_stack: undefined,
|
__crop_stack: undefined,
|
||||||
crop: function(node='focused'){
|
crop: function(node='focused'){
|
||||||
|
var that = this
|
||||||
var stack = this.__crop_stack ??= []
|
var stack = this.__crop_stack ??= []
|
||||||
stack.push([this.json(), this.path()])
|
var path = this.path()
|
||||||
|
// XXX make this linkable...
|
||||||
|
var header = '/ '
|
||||||
|
+ this.path(path.slice(0,-1), 'text')
|
||||||
|
.map(function(text, i){
|
||||||
|
return `<span>${text.split(/\n/)[0]}</span>` })
|
||||||
|
.join(' / ')
|
||||||
|
|
||||||
|
stack.push([this.json(), path])
|
||||||
this.load(this.data())
|
this.load(this.data())
|
||||||
|
|
||||||
|
this.header.innerHTML = header
|
||||||
this.dom.classList.add('crop')
|
this.dom.classList.add('crop')
|
||||||
return this },
|
return this },
|
||||||
// XXX use JSON API...
|
// XXX use JSON API...
|
||||||
|
// XXX add depth argument + 'all'
|
||||||
uncrop: function(){
|
uncrop: function(){
|
||||||
if(this.__crop_stack == null){
|
if(this.__crop_stack == null){
|
||||||
return this}
|
return this}
|
||||||
var [state, path] = this.__crop_stack.pop()
|
var [state, path] = this.__crop_stack.pop()
|
||||||
if(this.__crop_stack.length == 0){
|
if(this.__crop_stack.length == 0){
|
||||||
this.__crop_stack = undefined
|
this.__crop_stack = undefined
|
||||||
|
this.header.innerHTML = ''
|
||||||
this.dom.classList.remove('crop') }
|
this.dom.classList.remove('crop') }
|
||||||
// update state...
|
// update state...
|
||||||
path
|
path
|
||||||
@ -1073,7 +1109,9 @@ var Outline = {
|
|||||||
.reduce(function(res, i){
|
.reduce(function(res, i){
|
||||||
return res[i].children }, state)
|
return res[i].children }, state)
|
||||||
.splice(path.at(-1), 1, ...this.json())
|
.splice(path.at(-1), 1, ...this.json())
|
||||||
|
|
||||||
this.load(state)
|
this.load(state)
|
||||||
|
|
||||||
return this },
|
return this },
|
||||||
|
|
||||||
// block render...
|
// block render...
|
||||||
@ -1382,6 +1420,7 @@ var Outline = {
|
|||||||
|
|
||||||
// XXX move the code here into methods/actions...
|
// XXX move the code here into methods/actions...
|
||||||
// XXX use keyboard.js...
|
// XXX use keyboard.js...
|
||||||
|
__overtravel_timeout: undefined,
|
||||||
keyboard: {
|
keyboard: {
|
||||||
// vertical navigation...
|
// vertical navigation...
|
||||||
// XXX this is a bit hacky but it works -- the caret blinks at
|
// XXX this is a bit hacky but it works -- the caret blinks at
|
||||||
@ -1389,6 +1428,18 @@ var Outline = {
|
|||||||
// nice po prevent this...
|
// nice po prevent this...
|
||||||
ArrowUp: function(evt){
|
ArrowUp: function(evt){
|
||||||
var that = this
|
var that = this
|
||||||
|
|
||||||
|
// overtravel...
|
||||||
|
var overtravel =
|
||||||
|
this.__overtravel_timeout != null
|
||||||
|
&& this.get() === this.get(0)
|
||||||
|
this.__overtravel_timeout != null
|
||||||
|
&& clearTimeout(this.__overtravel_timeout)
|
||||||
|
this.__overtravel_timeout = setTimeout(function(){
|
||||||
|
that.__overtravel_timeout = undefined }, 100)
|
||||||
|
if(overtravel){
|
||||||
|
return }
|
||||||
|
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
if(edited){
|
if(edited){
|
||||||
var line = edited.getTextGeometry().line
|
var line = edited.getTextGeometry().line
|
||||||
@ -1400,6 +1451,18 @@ var Outline = {
|
|||||||
this.focus('focused', -1) } },
|
this.focus('focused', -1) } },
|
||||||
ArrowDown: function(evt){
|
ArrowDown: function(evt){
|
||||||
var that = this
|
var that = this
|
||||||
|
|
||||||
|
// overtravel...
|
||||||
|
var overtravel =
|
||||||
|
this.__overtravel_timeout != null
|
||||||
|
&& this.get() === this.get(-1)
|
||||||
|
this.__overtravel_timeout != null
|
||||||
|
&& clearTimeout(this.__overtravel_timeout)
|
||||||
|
this.__overtravel_timeout = setTimeout(function(){
|
||||||
|
that.__overtravel_timeout = undefined }, 100)
|
||||||
|
if(overtravel){
|
||||||
|
return }
|
||||||
|
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
if(edited){
|
if(edited){
|
||||||
var {line, lines} = edited.getTextGeometry()
|
var {line, lines} = edited.getTextGeometry()
|
||||||
@ -1433,6 +1496,19 @@ var Outline = {
|
|||||||
this.toggleCollapse(true)
|
this.toggleCollapse(true)
|
||||||
: this.focus('parent') },
|
: this.focus('parent') },
|
||||||
ArrowRight: function(evt){
|
ArrowRight: function(evt){
|
||||||
|
var that = this
|
||||||
|
|
||||||
|
// overtravel...
|
||||||
|
var overtravel =
|
||||||
|
this.__overtravel_timeout != null
|
||||||
|
&& this.get() === this.get(-1)
|
||||||
|
this.__overtravel_timeout != null
|
||||||
|
&& clearTimeout(this.__overtravel_timeout)
|
||||||
|
this.__overtravel_timeout = setTimeout(function(){
|
||||||
|
that.__overtravel_timeout = undefined }, 100)
|
||||||
|
if(overtravel){
|
||||||
|
return }
|
||||||
|
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
if(edited){
|
if(edited){
|
||||||
// move caret to next element...
|
// move caret to next element...
|
||||||
@ -1692,18 +1768,21 @@ var Outline = {
|
|||||||
var elem = evt.target
|
var elem = evt.target
|
||||||
if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){
|
if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){
|
||||||
return }
|
return }
|
||||||
// update element state...
|
|
||||||
if(elem.classList.contains('code')){
|
|
||||||
setTimeout(function(){
|
|
||||||
that.update(elem.parentElement)
|
|
||||||
elem.updateSize() }, 0) }
|
|
||||||
// handle keyboard...
|
// handle keyboard...
|
||||||
evt.key in that.keyboard
|
evt.key in that.keyboard
|
||||||
&& that.keyboard[evt.key].call(that, evt) })
|
&& that.keyboard[evt.key].call(that, evt) })
|
||||||
// update code block...
|
// update code block...
|
||||||
outline.addEventListener('keyup',
|
outline.addEventListener('keyup',
|
||||||
function(evt){
|
function(evt){
|
||||||
that.runPlugins('__keyup__', evt, that, evt.target) })
|
var elem = evt.target
|
||||||
|
// update element state...
|
||||||
|
if(elem.classList.contains('code')){
|
||||||
|
// NOTE: for some reason setting the timeout here to 0
|
||||||
|
// makes FF sometimes not see the updated text...
|
||||||
|
setTimeout(function(){
|
||||||
|
that.update(elem.parentElement)
|
||||||
|
elem.updateSize() }, 0) }
|
||||||
|
that.runPlugins('__keyup__', evt, that, elem) })
|
||||||
|
|
||||||
// toggle view/code of nodes...
|
// toggle view/code of nodes...
|
||||||
outline.addEventListener('focusin',
|
outline.addEventListener('focusin',
|
||||||
|
|||||||
@ -32,6 +32,8 @@ var setup = function(){
|
|||||||
</head>
|
</head>
|
||||||
<body onload="setup()">
|
<body onload="setup()">
|
||||||
<div class="editor" autofocus>
|
<div class="editor" autofocus>
|
||||||
|
<!-- header -->
|
||||||
|
<div class="header"></div>
|
||||||
<!-- code -->
|
<!-- code -->
|
||||||
<textarea class="code">
|
<textarea class="code">
|
||||||
- # Outline editor prototype
|
- # Outline editor prototype
|
||||||
@ -46,20 +48,10 @@ var setup = function(){
|
|||||||
-
|
-
|
||||||
- ## Bugs:
|
- ## Bugs:
|
||||||
focused:: true
|
focused:: true
|
||||||
- BUG: editor: FF seems to update the style every other key press -- should be live...
|
|
||||||
- BUG: mobile browsers behave quite chaotically ignoring parts of the styling...
|
- BUG: mobile browsers behave quite chaotically ignoring parts of the styling...
|
||||||
-
|
-
|
||||||
- ## ToDo:
|
- ## ToDo:
|
||||||
- crop: show crop path (and depth)
|
- crop: show crop path (and depth)
|
||||||
- identify a block:
|
|
||||||
- DONE index (flat)
|
|
||||||
- DONE path (index)
|
|
||||||
- id
|
|
||||||
- _the id attr is done, but we still need to get the node via id_
|
|
||||||
- focus:
|
|
||||||
- DONE `<editor>.autofocus`
|
|
||||||
- DONE `focused:: true` attr (`.text(..)`/`.json(..)`/`.load(..)`)
|
|
||||||
- focusing editor -> focus focused block
|
|
||||||
- undo
|
- undo
|
||||||
collapsed:: true
|
collapsed:: true
|
||||||
- edit stack (position, action, ...)
|
- edit stack (position, action, ...)
|
||||||
@ -88,9 +80,9 @@ var setup = function(){
|
|||||||
- Nerd fonts (option???)
|
- Nerd fonts (option???)
|
||||||
- multiple node selection
|
- multiple node selection
|
||||||
- smooth scrolling
|
- smooth scrolling
|
||||||
- _...this is more complicated that adding `behavior: "smooth"` to `.scrollIntoView(..)` as scrolling animation will get interrupted by next user input..._
|
- _...this is more complicated than adding `behavior: "smooth"` to `.scrollIntoView(..)` as scrolling animation will get interrupted by next user input..._
|
||||||
- need to cancel animation of things are moving too fast...
|
- need to cancel animation if things are moving too fast...
|
||||||
- make this generic
|
- make this generic (???)
|
||||||
- FEATURE? block templates...
|
- FEATURE? block templates...
|
||||||
collapsed:: true
|
collapsed:: true
|
||||||
- something like: `TPL: [_] <editable/> -- <editable/>`
|
- something like: `TPL: [_] <editable/> -- <editable/>`
|
||||||
@ -118,6 +110,19 @@ var setup = function(){
|
|||||||
block text
|
block text
|
||||||
- NOTE: this is only a problem if making list-items manually -- disable???
|
- NOTE: this is only a problem if making list-items manually -- disable???
|
||||||
- empty item height is a bit off...
|
- empty item height is a bit off...
|
||||||
|
- search?
|
||||||
|
- DONE over-travel pause -- when going fast over start/end stop...
|
||||||
|
- DONE focus:
|
||||||
|
collapsed:: true
|
||||||
|
- DONE `<editor>.autofocus`
|
||||||
|
- DONE `focused:: true` attr (`.text(..)`/`.json(..)`/`.load(..)`)
|
||||||
|
- DONE focusing editor -> focus focused block
|
||||||
|
- DONE identify a block:
|
||||||
|
collapsed:: true
|
||||||
|
- DONE index (flat)
|
||||||
|
- DONE path (index)
|
||||||
|
- DONE id
|
||||||
|
- _the id attr is done, but we still need to get the node via id_
|
||||||
- DONE pgup/pgdown/home/end buttons
|
- DONE pgup/pgdown/home/end buttons
|
||||||
- DONE FEATURE: "crop" -- view block tree separately...
|
- DONE FEATURE: "crop" -- view block tree separately...
|
||||||
- DONE unify attr parsing
|
- DONE unify attr parsing
|
||||||
@ -339,6 +344,7 @@ var setup = function(){
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<button onclick="editor.dom.classList.toggle('show-click-zones')">show/hide click zones</button>
|
<button onclick="editor.dom.classList.toggle('show-click-zones')">show/hide click zones</button>
|
||||||
|
<button onclick="editor.dom.classList.toggle('block-offsets')">show/hide block offsets</button>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user