Add Link, stop auto-remove when input empty, merge NodeList and Editor
This commit is contained in:
@@ -10,7 +10,7 @@ body {
|
||||
font-family: 'Courier', monospace;
|
||||
}
|
||||
|
||||
#definition {
|
||||
.editor {
|
||||
list-style: none;
|
||||
|
||||
margin: 0;
|
||||
@@ -22,7 +22,11 @@ body {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#definition li {
|
||||
.editor:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.editor li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
@@ -38,32 +42,31 @@ body {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#definition li.node {
|
||||
.editor li.node {
|
||||
background-color: #daf0db;
|
||||
}
|
||||
|
||||
#definition li.group {
|
||||
.editor li.group {
|
||||
background-color: #d8e6f4;
|
||||
}
|
||||
|
||||
#definition li:focus {
|
||||
.editor li:focus {
|
||||
border-color: red;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#definition li input {
|
||||
.editor li input {
|
||||
background-color: rgba(255,255,255,0.8);
|
||||
border: 3px solid black;
|
||||
padding: 4px;
|
||||
font-family: 'Courier', monospace;
|
||||
}
|
||||
|
||||
#definition li input:focus {
|
||||
.editor li input:focus {
|
||||
border-color: red;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#definition .nodelist {
|
||||
width: 100%;
|
||||
.editor .editor {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
196
architype.js
196
architype.js
@@ -22,12 +22,17 @@ class List {
|
||||
constructor(container) {
|
||||
this.container_ = container;
|
||||
this.minEntries_ = 0;
|
||||
this.maxEntries_ = Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
setMinEntries(min) {
|
||||
this.minEntries_ = min;
|
||||
}
|
||||
|
||||
setMaxEntries(max) {
|
||||
this.maxEntries_ = max;
|
||||
}
|
||||
|
||||
getEntries() {
|
||||
let ret = [];
|
||||
for (let elem of this.container_.children) {
|
||||
@@ -36,6 +41,10 @@ class List {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mayAdd() {
|
||||
return this.container_.children.length < this.maxEntries_;
|
||||
}
|
||||
|
||||
getSelected() {
|
||||
let iter = document.activeElement;
|
||||
while (iter) {
|
||||
@@ -184,62 +193,63 @@ class List {
|
||||
}
|
||||
}
|
||||
|
||||
class NodeList extends List {
|
||||
constructor(container) {
|
||||
class Editor extends List {
|
||||
static NODE = 1;
|
||||
static GROUP = 2;
|
||||
static LINK = 3;
|
||||
|
||||
constructor(container, allowedTypes) {
|
||||
super(container);
|
||||
|
||||
this.allowedTypes_ = new Set(allowedTypes ||
|
||||
[Editor.NODE, Editor.GROUP, Editor.LINK]);
|
||||
|
||||
this.container_.classList.add('editor');
|
||||
// Needs to accept focus to receive keydown, but shouldn't be in the normal
|
||||
// tab flow.
|
||||
this.container_.tabIndex = 99999;
|
||||
this.container_.addEventListener('keydown', e => { this.onKeyDown(e); });
|
||||
this.container_.focus();
|
||||
}
|
||||
|
||||
isAllowed(type) {
|
||||
return this.mayAdd() && this.allowedTypes_.has(type);
|
||||
}
|
||||
|
||||
addNodeAfter() {
|
||||
Node.addAfter(this.container_, this.getSelected());
|
||||
}
|
||||
|
||||
addNodeBefore() {
|
||||
Node.addBefore(this.container_, this.getSelected());
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
switch (e.key) {
|
||||
case 'n':
|
||||
this.addNodeAfter();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
||||
case 'N':
|
||||
this.addNodeBefore();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return;
|
||||
if (this.isAllowed(Editor.NODE)) {
|
||||
Node.addAfter(this.container_, this.getSelected());
|
||||
}
|
||||
|
||||
super.onKeyDown(e);
|
||||
}
|
||||
}
|
||||
|
||||
class Editor extends List {
|
||||
constructor(container) {
|
||||
super(container);
|
||||
document.addEventListener('keydown', e => { this.onKeyDown(e); });
|
||||
}
|
||||
|
||||
addNodeAfter() {
|
||||
Node.addAfter(this.container_, this.getSelected());
|
||||
}
|
||||
|
||||
addNodeBefore() {
|
||||
Node.addBefore(this.container_, this.getSelected());
|
||||
if (this.isAllowed(Editor.NODE)) {
|
||||
Node.addBefore(this.container_, this.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
addLinkAfter() {
|
||||
if (this.isAllowed(Editor.LINK)) {
|
||||
Link.addAfter(this.container_, this.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
addLinkBefore() {
|
||||
if (this.isAllowed(Editor.LINK)) {
|
||||
Link.addBefore(this.container_, this.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
addGroupAfter() {
|
||||
Group.addAfter(this.container_, this.getSelected());
|
||||
if (this.isAllowed(Editor.GROUP)) {
|
||||
Group.addAfter(this.container_, this.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
addGroupBefore() {
|
||||
Group.addBefore(this.container_, this.getSelected());
|
||||
if (this.isAllowed(Editor.GROUP)) {
|
||||
Group.addBefore(this.container_, this.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
@@ -256,6 +266,18 @@ class Editor extends List {
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
||||
case 'l':
|
||||
this.addLinkAfter();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
||||
case 'L':
|
||||
this.addLinkBefore();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
||||
case 'n':
|
||||
this.addNodeAfter();
|
||||
e.stopPropagation();
|
||||
@@ -336,7 +358,6 @@ class Node extends EditorEntryBase {
|
||||
this.input_.type = 'text';
|
||||
this.input_.placeholder = 'node name';
|
||||
this.listen(this.input_, 'keydown', (e) => this.onInputKeyDown(e));
|
||||
this.listen(this.input_, 'blur', () => this.onInputBlur());
|
||||
this.elem_.appendChild(this.input_);
|
||||
}
|
||||
|
||||
@@ -401,13 +422,6 @@ class Node extends EditorEntryBase {
|
||||
this.elem_.focus();
|
||||
}
|
||||
|
||||
onInputBlur() {
|
||||
if (this.input_.value.length == 0 && (this.elem_.previousElementSibling ||
|
||||
this.elem_.nextElementSibling)) {
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.input_.value;
|
||||
}
|
||||
@@ -424,12 +438,10 @@ class Group extends EditorEntryBase {
|
||||
this.input_.type = 'text';
|
||||
this.input_.placeholder = 'group name';
|
||||
this.listen(this.input_, 'keydown', (e) => this.onInputKeyDown(e));
|
||||
this.listen(this.input_, 'blur', () => this.onInputBlur());
|
||||
this.elem_.appendChild(this.input_);
|
||||
|
||||
let nodeList = document.createElement('div');
|
||||
nodeList.classList.add('nodelist');
|
||||
this.nodes_ = new NodeList(nodeList);
|
||||
this.nodes_ = new Editor(nodeList, [Editor.NODE]);
|
||||
this.nodes_.setMinEntries(1);
|
||||
this.nodes_.addNodeAfter();
|
||||
this.elem_.appendChild(nodeList);
|
||||
@@ -495,12 +507,92 @@ class Group extends EditorEntryBase {
|
||||
stopEdit() {
|
||||
this.elem_.focus();
|
||||
}
|
||||
}
|
||||
|
||||
onInputBlur() {
|
||||
if (this.input_.value.length == 0) {
|
||||
this.remove();
|
||||
class Link extends EditorEntryBase {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.elem_.innerText = 'Link: ';
|
||||
this.elem_.classList.add('link');
|
||||
|
||||
this.input_ = document.createElement('input');
|
||||
this.input_.type = 'text';
|
||||
this.input_.placeholder = 'label';
|
||||
this.listen(this.input_, 'keydown', (e) => this.onInputKeyDown(e));
|
||||
this.elem_.appendChild(this.input_);
|
||||
|
||||
let nodeList = document.createElement('div');
|
||||
this.nodes_ = new Editor(nodeList, [Editor.NODE]);
|
||||
this.nodes_.setMinEntries(2);
|
||||
this.nodes_.setMaxEntries(2);
|
||||
this.nodes_.addNodeAfter();
|
||||
this.nodes_.addNodeAfter();
|
||||
this.elem_.appendChild(nodeList);
|
||||
}
|
||||
|
||||
afterDomAdd() {
|
||||
this.input_.focus();
|
||||
}
|
||||
|
||||
onInputKeyDown(e) {
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.stopEdit();
|
||||
{
|
||||
let nodes = this.nodes_.getEntries();
|
||||
if (nodes[0].getValue() == '') {
|
||||
nodes[0].startEdit();
|
||||
} else if (nodes[1].getValue() == '') {
|
||||
nodes[1].startEdit();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.stopEdit();
|
||||
break;
|
||||
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown':
|
||||
case 'PageUp':
|
||||
case 'PageDown':
|
||||
this.stopEdit();
|
||||
break;
|
||||
|
||||
default:
|
||||
e.stopPropagation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
super.onKeyDown(e);
|
||||
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
this.startEdit();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 'ArrowRight':
|
||||
this.nodes_.selectNext();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
startEdit() {
|
||||
this.input_.focus();
|
||||
}
|
||||
|
||||
stopEdit() {
|
||||
this.elem_.focus();
|
||||
}
|
||||
}
|
||||
|
||||
new Editor(document.getElementById('definition'));
|
||||
|
||||
Reference in New Issue
Block a user