Compare commits

...

6 Commits

Author SHA1 Message Date
0e4344a711 notes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-23 23:15:26 +03:00
12bd9ffebf added basic crop path...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-23 22:55:23 +03:00
66e0f112fd tuning...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-23 22:11:10 +03:00
69701f0697 tweaking....
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-23 20:46:44 +03:00
4c9597e27b added overtravel pauses + cleanup...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-23 19:38:06 +03:00
857c1ae9a7 .get(..) can now get elements by id...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-23 19:09:29 +03:00
3 changed files with 130 additions and 26 deletions

View File

@ -50,6 +50,7 @@
.editor .children {
}
.editor .header,
.editor .outline {
display: block;
position: relative;
@ -68,12 +69,30 @@
.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 {
position: relative;
outline: none;
border: none;
}
.editor.block-offsets .outline .block {
border-left: solid 1px silver;
}
.editor .outline .block .block {
margin-left: var(--item-indent);
}

View File

@ -675,6 +675,8 @@ var Outline = {
return value },
get header(){
return this.dom.querySelector('.header') },
get code(){
return this.dom.querySelector('.code') },
get outline(){
@ -683,17 +685,29 @@ var Outline = {
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 path = []
var node = this.get(node)
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') }
return path },
//
// .get([<offset>])
// .get(<index>)[, <offset>]
// .get(<path>[, <offset>])
// .get(<id>[, <offset>)
// -> <node>
//
// .get('focused'[, <offset>])
// -> <node>
//
@ -736,6 +750,10 @@ var Outline = {
: offset
var outline = this.outline
// id...
if(typeof(node) == 'string' && node[0] == '#'){
node = outline.querySelector(node) }
// root nodes...
if(node == 'top'){
return [...outline.children] }
@ -847,13 +865,19 @@ var Outline = {
var elem = this.get(...arguments)
?? this.get(0)
if(elem){
var cur = this.get()
var blocks = this.get('visible')
elem.focus({preventScroll: true})
;(elem.classList.contains('code') ?
elem
: elem.querySelector('.code'))
.scrollIntoView({
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 },
edit: function(node='focused', offset){
@ -1049,23 +1073,35 @@ var Outline = {
this.__change__()
return this },
// crop...
// XXX add crop/path indicator...
// XXX add crop-level/path indicator...
__crop_stack: undefined,
crop: function(node='focused'){
var that = this
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.header.innerHTML = header
this.dom.classList.add('crop')
return this },
// XXX use JSON API...
// XXX add depth argument + 'all'
uncrop: function(){
if(this.__crop_stack == null){
return this}
var [state, path] = this.__crop_stack.pop()
if(this.__crop_stack.length == 0){
this.__crop_stack = undefined
this.header.innerHTML = ''
this.dom.classList.remove('crop') }
// update state...
path
@ -1073,7 +1109,9 @@ var Outline = {
.reduce(function(res, i){
return res[i].children }, state)
.splice(path.at(-1), 1, ...this.json())
this.load(state)
return this },
// block render...
@ -1382,6 +1420,7 @@ var Outline = {
// XXX move the code here into methods/actions...
// XXX use keyboard.js...
__overtravel_timeout: undefined,
keyboard: {
// vertical navigation...
// XXX this is a bit hacky but it works -- the caret blinks at
@ -1389,6 +1428,18 @@ var Outline = {
// nice po prevent this...
ArrowUp: function(evt){
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')
if(edited){
var line = edited.getTextGeometry().line
@ -1400,6 +1451,18 @@ var Outline = {
this.focus('focused', -1) } },
ArrowDown: 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')
if(edited){
var {line, lines} = edited.getTextGeometry()
@ -1433,6 +1496,19 @@ var Outline = {
this.toggleCollapse(true)
: this.focus('parent') },
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')
if(edited){
// move caret to next element...
@ -1692,18 +1768,21 @@ var Outline = {
var elem = evt.target
if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){
return }
// update element state...
if(elem.classList.contains('code')){
setTimeout(function(){
that.update(elem.parentElement)
elem.updateSize() }, 0) }
// handle keyboard...
evt.key in that.keyboard
&& that.keyboard[evt.key].call(that, evt) })
// update code block...
outline.addEventListener('keyup',
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...
outline.addEventListener('focusin',

View File

@ -32,6 +32,8 @@ var setup = function(){
</head>
<body onload="setup()">
<div class="editor" autofocus>
<!-- header -->
<div class="header"></div>
<!-- code -->
<textarea class="code">
- # Outline editor prototype
@ -46,20 +48,10 @@ var setup = function(){
-
- ## Bugs:
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...
-
- ## ToDo:
- 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
collapsed:: true
- edit stack (position, action, ...)
@ -88,9 +80,9 @@ var setup = function(){
- Nerd fonts (option???)
- multiple node selection
- smooth scrolling
- _...this is more complicated that 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...
- make this generic
- _...this is more complicated than adding `behavior: "smooth"` to `.scrollIntoView(..)` as scrolling animation will get interrupted by next user input..._
- need to cancel animation if things are moving too fast...
- make this generic (???)
- FEATURE? block templates...
collapsed:: true
- something like: `TPL: [_] <editable/> -- <editable/>`
@ -118,6 +110,19 @@ var setup = function(){
block text
- NOTE: this is only a problem if making list-items manually -- disable???
- 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 FEATURE: "crop" -- view block tree separately...
- DONE unify attr parsing
@ -339,6 +344,7 @@ var setup = function(){
<hr>
<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>