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 .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);
} }

View File

@ -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',

View File

@ -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>