diff --git a/css/styles.css b/css/styles.css index f4c22d5..fceddde 100644 --- a/css/styles.css +++ b/css/styles.css @@ -7,6 +7,10 @@ thead { border-bottom: 3px solid black; } +thead th { + position: relative; +} + tbody tr:nth-child(even) { background-color: #ededed; } @@ -15,10 +19,10 @@ tbody tr:nth-child(odd) { background-color: #f6f6f6; } -thead tr th:has(+th[class="formula"]), -tbody tr th:last-of-type, -thead tr th[class="formula"]:has(+th), -tbody tr td:first-of-type:has(+td) { +thead th:has(+th[class="formula"]), +tbody th:last-of-type, +thead th[class="formula"]:has(+th), +tbody td:first-of-type:has(+td) { border-right: 3px solid black; } @@ -26,7 +30,7 @@ thead .formula-input-wrapper input { width: 8rem !important; } -tbody .formula-input-wrapper input { +tbody input { width: 3.5rem !important; text-align: center; } @@ -36,6 +40,20 @@ td, th { padding: 0.5rem; height: 3.5rem; text-align: center; + min-width: 5rem; +} + +thead th button { + position: absolute; + top: 0; + right: 0; + border-top: none; + border-right: none; + border-left: 1px solid black; + border-bottom: 1px solid black; + background: transparent; + cursor: pointer; + font-size: 12px; } .bit, .variable { diff --git a/templates/formulation.xhtml.j2 b/templates/formulation.xhtml.j2 index 2cc6388..4efb22f 100644 --- a/templates/formulation.xhtml.j2 +++ b/templates/formulation.xhtml.j2 @@ -23,10 +23,7 @@ -
- -
+ {% endfor %} diff --git a/ts/.prettierignore b/ts/.prettierignore index bf6669f..cebbe43 100644 --- a/ts/.prettierignore +++ b/ts/.prettierignore @@ -1 +1 @@ -ts/parser/generated/**/* +src/parser/generated/**/* diff --git a/ts/src/formula_input_element.ts b/ts/src/formula_input_element.ts index 7acb2dc..61374f6 100644 --- a/ts/src/formula_input_element.ts +++ b/ts/src/formula_input_element.ts @@ -125,8 +125,6 @@ export class FormulaInput { this.#inputElement.addEventListener("input", () => { this.setInputValidity("valid"); }); - - this.#displayElement.addEventListener("click", () => this.viewInputField()); } } diff --git a/ts/src/intermediate_formulas.ts b/ts/src/intermediate_formulas.ts index 42e4898..c2de5dc 100644 --- a/ts/src/intermediate_formulas.ts +++ b/ts/src/intermediate_formulas.ts @@ -1,8 +1,47 @@ import { OutputFormat } from "./parser/parser"; -import { createFormulaInput, type FormulaInput } from "./formula_input_element"; +import { createFormulaInput } from "./formula_input_element"; const tableElement = document.querySelector("table")!; +function createIntermediateFormulaHeader( + formula: string, + formulaId: number, + format: OutputFormat, + data: Record, + enabled: boolean, +): HTMLTableCellElement { + const cellElement = document.createElement("th"); + + const formulaInput = createFormulaInput(formula, format, enabled); + cellElement.appendChild(formulaInput.element); + + if (enabled) { + formulaInput.inputElement.addEventListener("change", () => { + const formulas = (data["intermediate-formulas"] ?? {}) as Record; + formulas[formulaId] = formulaInput.getInput(); + data["intermediate-formulas"] = formulas; + }); + + const editButton = document.createElement("button"); + editButton.tabIndex = -1; + editButton.innerHTML = "✏️"; + + editButton.addEventListener("mousedown", (e) => { + // This prevents the edit button to trigger the blur event of the input element. + e.preventDefault(); + }); + + editButton.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + formulaInput.viewInputField(); + }); + cellElement.appendChild(editButton); + } + + return cellElement; +} + export function setupButtonToAddIntermediateFormula(attempt: object, total_rows: number, format: OutputFormat) { // @ts-expect-error Attempt object definition is not here. const data = attempt.data as Record; @@ -15,31 +54,22 @@ export function setupButtonToAddIntermediateFormula(attempt: object, total_rows: const rows = tableElement.querySelectorAll("tr"); for (const [index, row] of rows.entries()) { - let cellElement, intermediateFormulaInput; + let cellElement; if (index === 0) { // The first row of the table contains the formulas. - cellElement = document.createElement("th"); - intermediateFormulaInput = createIntermediateFormulaInput( + cellElement = createIntermediateFormulaHeader( "", intermediateFormulaId, format, - true, data, + // @ts-expect-error Attempt object definition is not here. + !attempt.readOnly, ); initValues(data, intermediateFormulaId, total_rows); } else { - cellElement = document.createElement("td"); - intermediateFormulaInput = createIntermediateResultInput( - "", - intermediateFormulaId, - index - 1, - format, - true, - data, - ); + cellElement = createIntermediateResultInput("", intermediateFormulaId, index - 1, true, data); } - cellElement.appendChild(intermediateFormulaInput.element); row.appendChild(cellElement); } @@ -58,7 +88,7 @@ function initValues(data: Record, intermediateFormulaId: number data["intermediate-results"] = intermediateResults; } -export function viewExistingIntermediateFormulas(format: OutputFormat, attempt: object) { +export function initIntermediateFormulas(format: OutputFormat, attempt: object) { // @ts-expect-error Attempt object definition is not here. const data = attempt.data as Record; // @ts-expect-error Attempt object definition is not here. @@ -69,83 +99,58 @@ export function viewExistingIntermediateFormulas(format: OutputFormat, attempt: const rows = tableElement.querySelectorAll("tr"); for (const [intermediateFormulaId, intermediateFormula] of Object.entries(intermediateFormulas)) { - const cellElement = document.createElement("th"); - const intermediateFormulaInput = createIntermediateFormulaInput( + const cellElement = createIntermediateFormulaHeader( intermediateFormula, parseInt(intermediateFormulaId), format, - isActive, data, + isActive, ); - cellElement.appendChild(intermediateFormulaInput.element); rows[0].appendChild(cellElement); for (const [index, intermediateResult] of Object.entries(intermediateResults[intermediateFormulaId])) { - const cellElement = document.createElement("td"); - const intermediateResultInput = createIntermediateResultInput( + const cellElement = createIntermediateResultInput( intermediateResult, parseInt(intermediateFormulaId), parseInt(index), - format, isActive, data, ); - cellElement.appendChild(intermediateResultInput.element); rows[parseInt(index) + 1].appendChild(cellElement); } } } -function createIntermediateFormulaInput( - formula: string, - intermediateFormulaId: number, - format: OutputFormat, - enabled: boolean, - data: Record, -): FormulaInput { - const formulaInputElement = createFormulaInput(formula, format, enabled); - formulaInputElement.inputElement.setAttribute("data-intermediate-formula", `${intermediateFormulaId}`); - - const intermediateFormulas = (data["intermediate-formulas"] ?? {}) as Record; - - if (enabled) { - formulaInputElement.inputElement.addEventListener("change", () => { - const formulaId = formulaInputElement.inputElement.getAttribute("data-intermediate-formula")!; - intermediateFormulas[formulaId] = formulaInputElement.getInput(); - data["intermediate-formulas"] = intermediateFormulas; - }); - } - - return formulaInputElement; -} - function createIntermediateResultInput( formula: string, intermediateFormulaId: number, row: number, - format: OutputFormat, enabled: boolean, data: Record, -): FormulaInput { - const resultInputElement = createFormulaInput(formula, format, enabled); - resultInputElement.inputElement.maxLength = 1; - resultInputElement.inputElement.pattern = "0|1"; - resultInputElement.inputElement.setAttribute("data-intermediate-result", `${row}`); - resultInputElement.inputElement.setAttribute("data-intermediate-result-belongs-to", `${intermediateFormulaId}`); +): HTMLTableCellElement { + const cellElement = document.createElement("td"); + // Create the input element for the result. + const resultInputElement = document.createElement("input"); + resultInputElement.value = formula; + resultInputElement.maxLength = 1; + resultInputElement.pattern = "0|1"; + resultInputElement.disabled = !enabled; const intermediateResults = (data["intermediate-results"] ?? {}) as Record; if (enabled) { - resultInputElement.inputElement.addEventListener("change", () => { - const resultId = parseInt(resultInputElement.inputElement.getAttribute("data-intermediate-result")!); - const belongsToFormulaId = resultInputElement.inputElement.getAttribute( - "data-intermediate-result-belongs-to", - )!; - - intermediateResults[belongsToFormulaId][resultId] = resultInputElement.getInput(); + resultInputElement.addEventListener("change", () => { + intermediateResults[intermediateFormulaId][row] = resultInputElement.value; data["intermediate-results"] = intermediateResults; }); + + resultInputElement.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + } + }); } - return resultInputElement; + cellElement.appendChild(resultInputElement); + return cellElement; } diff --git a/ts/src/main.ts b/ts/src/main.ts index d15de8b..953fa0a 100644 --- a/ts/src/main.ts +++ b/ts/src/main.ts @@ -1,12 +1,10 @@ import { type OutputFormat } from "./parser/parser.js"; import { renderBora } from "./utils"; -import { FormulaInput } from "./formula_input_element"; -import { setupButtonToAddIntermediateFormula, viewExistingIntermediateFormulas } from "./intermediate_formulas"; +import { initIntermediateFormulas, setupButtonToAddIntermediateFormula } from "./intermediate_formulas"; export function init(attempt: object, [format, total_rows]: [OutputFormat, number]) { initFormulaElements(format); - initFormulaInputElements(format, attempt); - viewExistingIntermediateFormulas(format, attempt); + initIntermediateFormulas(format, attempt); // @ts-expect-error Attempt object definition is not here. if (!attempt.readOnly) { setupButtonToAddIntermediateFormula(attempt, total_rows, format); @@ -18,14 +16,13 @@ function initFormulaElements(format: OutputFormat) { for (const formulaElement of formulaElements) { renderBora(formulaElement.textContent!, formulaElement, format); } -} -function initFormulaInputElements(format: OutputFormat, attempt: object) { - const wrapperElements: NodeListOf = document.querySelectorAll(".formula-input-wrapper"); - for (const wrapperElement of wrapperElements) { - const inputElement: HTMLInputElement = wrapperElement.querySelector(".formula-input")!; - const displayElement: HTMLSpanElement = wrapperElement.querySelector(".formula-display")!; - // @ts-expect-error Attempt object definition is not here. - new FormulaInput(wrapperElement, inputElement, displayElement, format, !attempt.readOnly); + const formulaInputElements = document.querySelectorAll(".formula-input") as NodeListOf; + for (const formulaInputElement of formulaInputElements) { + formulaInputElement.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + } + }); } } diff --git a/ts/src/mathjax/base.ts b/ts/src/mathjax/base.ts index 882c20b..4608811 100644 --- a/ts/src/mathjax/base.ts +++ b/ts/src/mathjax/base.ts @@ -24,7 +24,7 @@ export abstract class BaseMathJaxHelper { return this.loadPromise; } - return this.loadPromise = new Promise((resolve, reject) => { + return (this.loadPromise = new Promise((resolve, reject) => { const script = document.createElement("script"); script.type = "text/javascript"; script.src = this.cdnUrl; @@ -40,7 +40,7 @@ export abstract class BaseMathJaxHelper { script.onerror = () => reject(new Error(`Failed to load ${this.cdnUrl}.`)); document.head.appendChild(script); - }); + })); } /** diff --git a/ts/src/mathjax/index.ts b/ts/src/mathjax/index.ts index 2720710..01aaf6e 100644 --- a/ts/src/mathjax/index.ts +++ b/ts/src/mathjax/index.ts @@ -25,7 +25,7 @@ export function renderLaTeX(element: Element, inline: boolean = true): Promise { // We need to manually chain every render call. // https://docs.mathjax.org/en/v3.2/web/typeset.html#handling-asynchronous-typesetting - return this.queue = this.queue.then(() => { + return (this.queue = this.queue.then(() => { // Get the delimiters. const inlineDelimiters = this.mathjax.config.tex?.inlineMath?.[0] ?? ["\\(", "\\)"]; const displayDelimiters = this.mathjax.config.tex?.displayMath?.[0] ?? ["\\[", "\\]"]; @@ -48,6 +48,6 @@ export class MathJax3Helper extends BaseMathJaxHelper { // Perform the rendering. return this.mathjax.typesetPromise([element]); - }); + })); } } diff --git a/ts/src/mathjax/v4.ts b/ts/src/mathjax/v4.ts index b6a6c13..c6845f1 100644 --- a/ts/src/mathjax/v4.ts +++ b/ts/src/mathjax/v4.ts @@ -41,6 +41,6 @@ export class MathJax4Helper extends BaseMathJaxHelper { // Add the delimiters. element.textContent = `${openingDelimiter} ${element.textContent} ${closingDelimiter}`; - return this.mathjax.typesetPromise([element]) + return this.mathjax.typesetPromise([element]); } }