Make fields div instead of table + implement fieldIsInInlineMode logic

This commit is contained in:
Henrik Giesel 2021-01-26 23:00:55 +01:00
parent 335267d42e
commit e481452114
3 changed files with 72 additions and 53 deletions

View file

@ -14,12 +14,6 @@
} }
} }
.clearfix::after {
content: "";
display: table;
clear: both;
}
.fname { .fname {
vertical-align: middle; vertical-align: middle;
padding: 0; padding: 0;

View file

@ -105,6 +105,10 @@ function onKeyUp(evt: KeyboardEvent): void {
} }
} }
function nodeIsBRElement(node: Node): node is HTMLBRElement {
return nodeIsElement(node) && node.tagName === "BR";
}
function nodeIsElement(node: Node): node is Element { function nodeIsElement(node: Node): node is Element {
return node.nodeType === Node.ELEMENT_NODE; return node.nodeType === Node.ELEMENT_NODE;
} }
@ -113,6 +117,10 @@ function nodeIsText(node: Node): node is Text {
return node.nodeType === Node.TEXT_NODE; return node.nodeType === Node.TEXT_NODE;
} }
function nodeIsInline(node: Node): boolean {
return nodeIsText(node) || nodeIsBRElement(node) || window.getComputedStyle(node as Element).getPropertyValue("display").startsWith("inline");
}
function inListItem(): boolean { function inListItem(): boolean {
const anchor = window.getSelection().anchorNode; const anchor = window.getSelection().anchorNode;
@ -197,7 +205,8 @@ function clearChangeTimer(): void {
} }
} }
function onFocus(elem: HTMLElement): void { function onFocus(evt: FocusEvent): void {
const elem = evt.currentTarget as HTMLElement;
if (currentField === elem) { if (currentField === elem) {
// anki window refocused; current element unchanged // anki window refocused; current element unchanged
return; return;
@ -277,19 +286,19 @@ function onBlur(): void {
} }
} }
function stripTrailingLinebreakIfInlineElements(field: HTMLDivElement) { function fieldIsInInlineMode(field: HTMLDivElement): boolean {
if ( if (field.childNodes.length === 0) {
nodeIsElement(field.lastChild) && // for now, for all practical purposes, empty fields are in block mode
field.lastChild.tagName === "BR" && ( return false;
nodeIsText(field.lastChild.previousSibling) ||
window.getComputedStyle(field.lastChild.previousSibling as Element).getPropertyValue("display").startsWith("inline")
)
) {
console.log('trimmd!', field.lastChild, field.lastChild.previousSibling)
return field.innerHTML.slice(0, -4);
} }
return field.innerHTML; for (const child of field.children) {
if (!nodeIsInline(child)) {
return false;
}
}
return true;
} }
function saveField(type: "blur" | "key"): void { function saveField(type: "blur" | "key"): void {
@ -299,7 +308,11 @@ function saveField(type: "blur" | "key"): void {
return; return;
} }
const fieldText = stripTrailingLinebreakIfInlineElements(currentField); const fieldText = fieldIsInInlineMode(currentField) && currentField.innerHTML.endsWith("<br>")
// trim trailing <br>
? currentField.innerHTML.slice(0, -4)
: currentField.innerHTML
pycmd(`${type}:${currentFieldOrdinal()}:${currentNoteId}:${fieldText}`); pycmd(`${type}:${currentFieldOrdinal()}:${currentNoteId}:${fieldText}`);
} }
@ -374,44 +387,58 @@ function onCutOrCopy(): boolean {
return true; return true;
} }
function createField(index: number, label: string, color: string, content: string): [HTMLDivElement, HTMLDivElement] {
const name = document.createElement("div");
name.id = `name${index}`;
name.className = "fname";
const fieldname = document.createElement("span");
fieldname.className = "fieldname";
fieldname.innerText = label;
name.appendChild(fieldname);
const field = document.createElement("div");
field.id = `f${index}`;
field.className = "field";
field.setAttribute("contenteditable", "true");
field.style.color = color;
field.addEventListener("keydown", onKey);
field.addEventListener("keyup", onKeyUp);
field.addEventListener("input", onInput);
field.addEventListener("focus", onFocus);
field.addEventListener("blur", onBlur);
field.addEventListener("paste", onPaste);
field.addEventListener("copy", onCutOrCopy);
field.addEventListener("oncut", onCutOrCopy);
field.innerHTML = content;
if (fieldIsInInlineMode(field)) {
field.appendChild(document.createElement("br"));
}
return [name, field];
}
function setFields(fields: [string, string][]): void { function setFields(fields: [string, string][]): void {
let txt = "";
// webengine will include the variable after enter+backspace // webengine will include the variable after enter+backspace
// if we don't convert it to a literal colour // if we don't convert it to a literal colour
const color = window const color = window
.getComputedStyle(document.documentElement) .getComputedStyle(document.documentElement)
.getPropertyValue("--text-fg"); .getPropertyValue("--text-fg");
for (let i = 0; i < fields.length; i++) {
const n = fields[i][0]; const elements = fields.flatMap(
let f = fields[i][1]; ([name, fieldcontent], index: number) => createField(index, name, color, fieldcontent)
txt += ` )
<tr>
<td class=fname id="name${i}"> const fieldsContainer = document.getElementById("fields")
<span class="fieldname">${n}</span> // can be replaced with ParentNode.replaceChildren in Chrome 86+
</td> while (fieldsContainer.firstChild) {
</tr> fieldsContainer.removeChild(fieldsContainer.firstChild);
<tr>
<td width=100%>
<div id="f${i}"
onkeydown="onKey(window.event);"
onkeyup="onKeyUp(window.event);"
oninput="onInput();"
onmouseup="onKey(window.event);"
onfocus="onFocus(this);"
onblur="onBlur();"
class="field clearfix"
onpaste="onPaste(this);"
oncopy="onCutOrCopy(this);"
oncut="onCutOrCopy(this);"
contentEditable
style="color: ${color}"
>${f}</div>
</td>
</tr>`;
} }
$("#fields").html( for (const element of elements) {
`<table cellpadding=0 width=100% style='table-layout: fixed;'>${txt}</table>` fieldsContainer.appendChild(element);
); }
maybeDisableButtons(); maybeDisableButtons();
} }
@ -474,8 +501,6 @@ let filterHTML = function (
outHtml = outHtml.replace(/[\n\t ]+/g, " "); outHtml = outHtml.replace(/[\n\t ]+/g, " ");
} }
outHtml = outHtml.trim(); outHtml = outHtml.trim();
//console.log(`input html: ${html}`);
//console.log(`outpt html: ${outHtml}`);
return outHtml; return outHtml;
}; };

View file

@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "es6",
"module": "commonjs", "module": "commonjs",
"lib": ["es6", "dom", "dom.iterable"], "lib": ["es2019", "dom", "dom.iterable"],
"strict": true, "strict": true,
"noImplicitAny": false, "noImplicitAny": false,
"strictNullChecks": false, "strictNullChecks": false,