From 7e4fe129b0c8c6ec4a1eaccf9d4dc6843f866b3c Mon Sep 17 00:00:00 2001 From: alexis Date: Tue, 8 Aug 2023 23:17:36 -0400 Subject: [PATCH] Modularize dialog UI component #124 --- scripts/compose.js | 86 +++++++------------------ scripts/solve.js | 135 +++++++++++++++------------------------- scripts/ui/ui-dialog.js | 34 ++++++++-- scripts/ui/ui-input.js | 1 - styles/style.css | 7 +-- 5 files changed, 104 insertions(+), 159 deletions(-) delete mode 100644 scripts/ui/ui-input.js diff --git a/scripts/compose.js b/scripts/compose.js index 14c9645..286beb7 100644 --- a/scripts/compose.js +++ b/scripts/compose.js @@ -4,6 +4,7 @@ import GridSquare from "./grid/grid-square.js"; import Grid from "./grid/grid.js"; import OCButton from "./ui/ui-button.js"; import OCIcons from "./ui/ui-icons.js"; +import OCDialog from "./ui/ui-dialog.js"; ("use strict"); @@ -307,7 +308,7 @@ class EditorGrid extends Grid { classes: ["oc-builder-clue-list-button"], action: () => { this.acrossClues.push(new PuzzleClue("across", acrossClues)); - } + }, }); new OCButton({ icon: OCIcons.remove, @@ -317,7 +318,7 @@ class EditorGrid extends Grid { action: () => { acrossClues.removeChild(acrossClues.lastChild); this.acrossClues.pop(); - } + }, }); let downLabel = document.createElement("h2"); @@ -336,7 +337,7 @@ class EditorGrid extends Grid { classes: ["oc-builder-clue-list-button"], action: () => { this.downClues.push(new PuzzleClue("across", acrossClues)); - } + }, }); new OCButton({ icon: OCIcons.remove, @@ -346,7 +347,7 @@ class EditorGrid extends Grid { action: () => { acrossClues.removeChild(acrossClues.lastChild); this.downClues.pop(); - } + }, }); // Display a form for entering puzzle metadata let infoHeader = document.createElement("h2"); @@ -427,7 +428,7 @@ class EditorGrid extends Grid { this.tagsInput.inputElement.value = this.tagsInput.inputElement.value.slice(0, -2); this.languageInput.value = obj["info"]["language"]; - } catch { } + } catch {} for (const clue of Object.entries(obj["clues"]["across"])) { let NewClue = new PuzzleClue("across", acrossClues); NewClue.textElement.value = clue[1]["content"].toString(); @@ -558,7 +559,7 @@ class EditorGridSquare extends GridSquare { this.element.appendChild(this.numberInput); try { this.clueElement.remove(); - } catch { } + } catch {} this.numberInput.onclick = (e) => { e.stopPropagation(); }; @@ -568,12 +569,12 @@ class EditorGridSquare extends GridSquare { } try { this.textElement.value = answer; - } catch { } + } catch {} try { this.textElement.addEventListener("input", () => { document.onchange(); }); - } catch { } + } catch {} if (this.style !== "cell") { this.element.onclick = () => { this.checkboxElement.checked = !this.checkboxElement.checked; @@ -620,15 +621,6 @@ class PuzzleInfo { } } -class ControlButton { - constructor(title, icon, parentElement) { - this.element = document.createElement("button"); - this.element.title = title; - this.element.innerHTML = icon; - parentElement.appendChild(this.element); - } -} - function populateToolBar() { const UIContainer = document.createElement("div"); UIContainer.classList.add("oc-builder-ui-container"); @@ -655,7 +647,9 @@ function populateToolBar() { icon: OCIcons.squareCell, tooltip: "Make cell", parent: toolBarElement, - action: () => { myPuzzle.transformSquares("cell"); }, + action: () => { + myPuzzle.transformSquares("cell"); + }, }); new OCButton({ icon: OCIcons.squareBlock, @@ -738,64 +732,28 @@ async function displayShareDialog() { } function displayWordHelperDialog() { - let dialog = document.createElement("dialog"); - dialog.classList.add("oc-dialog"); - let dialogTitleBar = document.createElement("div"); - let dialogTitle = document.createElement("h2"); - dialogTitle.textContent = "Word Helper"; - let titleBarSeparator = document.createElement("hr"); let frame = document.createElement("iframe"); frame.src = `${document.baseURI}frames/word-helper.html`; - let dialogCloseButton = new ControlButton( - "Close", - OCIcons.close, - dialogTitleBar, - ); - dialogCloseButton.element.onclick = () => { - dialog.close(); - dialog.remove(); - }; - dialogTitleBar.appendChild(dialogTitle); - dialog.appendChild(dialogTitleBar); - dialog.appendChild(titleBarSeparator); - dialog.appendChild(frame); - document.body.appendChild(dialog); - dialog.showModal(); + new OCDialog({ + title: "Word Helper", + content: frame, + }); } function displayInsertDialog() { - let dialog = document.createElement("dialog"); - dialog.classList.add("oc-dialog"); - let dialogTitleBar = document.createElement("div"); - let dialogTitle = document.createElement("h2"); - dialogTitle.textContent = "Insert"; - let titleBarSeparator = document.createElement("hr"); let frame = document.createElement("iframe"); frame.src = `${document.baseURI}frames/insert.html`; - frame.name = "oc-insert-frame"; - let dialogCloseButton = new ControlButton( - "Close", - OCIcons.close, - dialogTitleBar, - ); - dialogCloseButton.element.onclick = () => { - dialog.close(); - dialog.remove(); - }; - dialogTitleBar.appendChild(dialogTitle); - dialog.appendChild(dialogTitleBar); - dialog.appendChild(titleBarSeparator); - dialog.appendChild(frame); - document.body.appendChild(dialog); - dialog.showModal(); - + let insertDialog = new OCDialog({ + title: "Insert", + content: frame, + }); // Inserting functionality frame.onload = () => { frame.contentWindow.document.getElementById("oc-insert-form").onsubmit = ( e, ) => { e.preventDefault(); - dialog.close(); + insertDialog.element.close(); myPuzzle.insertText( frame.contentWindow.document.getElementById("oc-insert-field").value, ); @@ -994,7 +952,7 @@ function refreshDisabledButtons() { } async function startOCEditor() { - String.prototype.toQueryString = function() { + String.prototype.toQueryString = function () { // Remove all characters that precede "?" return this.substring(this.indexOf("?")); }; diff --git a/scripts/solve.js b/scripts/solve.js index 91828f3..5a1378e 100644 --- a/scripts/solve.js +++ b/scripts/solve.js @@ -2,6 +2,7 @@ import decompressAndDecode from "./compression/decompress.js"; import Grid from "./grid/grid.js"; import GridSquare from "./grid/grid-square.js"; import OCButton from "./ui/ui-button.js"; +import OCDialog from "./ui/ui-dialog.js"; import OCIcons from "./ui/ui-icons.js"; ("use strict"); @@ -137,7 +138,8 @@ class Puzzle extends Grid { new InfoItem("Language".i18n(), this.obj["info"]["language"], infoList); // Language new InfoItem( "Puzzle Copyright".i18n(), - `© ${this.obj["info"]["date_published"].split("-")[0]} ${this.obj["info"]["author"] + `© ${this.obj["info"]["date_published"].split("-")[0]} ${ + this.obj["info"]["author"] }`, infoList, ); @@ -235,13 +237,13 @@ class Puzzle extends Grid { square.y === this.selectedSquare.y && square.x < this.selectedSquare.x, ) - [ - this.squares.filter( - (square) => - square.y === this.selectedSquare.y && - square.x < this.selectedSquare.x, - ).length - 1 - ].select(); + [ + this.squares.filter( + (square) => + square.y === this.selectedSquare.y && + square.x < this.selectedSquare.x, + ).length - 1 + ].select(); } } else if (this.selectionDirection === "down") { // Filters the squares array to the same x value as the selected square and a smaller x value @@ -273,13 +275,13 @@ class Puzzle extends Grid { square.x === this.selectedSquare.x && square.y < this.selectedSquare.y, ) - [ - this.squares.filter( - (square) => - square.x === this.selectedSquare.x && - square.y < this.selectedSquare.y, - ).length - 1 - ].select(); + [ + this.squares.filter( + (square) => + square.x === this.selectedSquare.x && + square.y < this.selectedSquare.y, + ).length - 1 + ].select(); } } } @@ -536,11 +538,13 @@ class ClueBar { } else { try { document.getElementById("oc-puzzle-title").style.display = "none"; // Hides the title once solving begins - } catch { } + } catch {} this.element.style.display = "flex"; - this.clueContentWrapper.innerHTML = `${puzzle.selectedClue.number - }-${puzzle.selectedClue.direction.toCapitalized().i18n()} ${puzzle.selectedClue.HTMLContent - }`; + this.clueContentWrapper.innerHTML = `${ + puzzle.selectedClue.number + }-${puzzle.selectedClue.direction.toCapitalized().i18n()} ${ + puzzle.selectedClue.HTMLContent + }`; } } @@ -625,7 +629,6 @@ function displayControlButtons() { parent: controlButtons, action: verifyPuzzle, }); - let revealButton = document.createElement("details"); revealButton.classList.add("oc-drop-down-button"); let revealButtonSummary = document.createElement("summary"); @@ -808,7 +811,7 @@ function verifyPuzzle() { if (square.style === "cell") { if ( square.textElement.value.toUpperCase() !== - square.answer.toUpperCase() && + square.answer.toUpperCase() && square.textElement.value !== "" ) { square.element.classList.add("oc-cell-invalid"); @@ -835,7 +838,7 @@ function revealWord() { if (square.element.classList.contains("highlighted")) { try { square.textElement.value = square.answer; - } catch { } + } catch {} } } } @@ -886,14 +889,7 @@ function checkPuzzle() { function showSolvedScreen() { // Display a "game over" alert endStopwatch(); - let dialog = document.createElement("dialog"); - dialog.classList.add("oc-dialog"); - let dialogTitleBar = document.createElement("div"); - let dialogTitle = document.createElement("h2"); - dialogTitle.textContent = "Congratulations".i18n(); - let titleBarSeparator = document.createElement("hr"); let solveMessageContainer = document.createElement("div"); - solveMessageContainer.classList.add("oc-dialog-content"); let solveParagraph = document.createElement("p"); solveParagraph.textContent = "You solved the puzzle in:"; let solveTimeContainer = document.createElement("div"); @@ -903,39 +899,26 @@ function showSolvedScreen() { square.textContent = character; solveTimeContainer.appendChild(square); } - let dialogCloseButton = new ControlButton( - "Close".i18n(), - OCIcons.close, - dialogTitleBar, - ); - dialogCloseButton.element.onclick = () => { - dialog.close(); - dialog.remove(); - }; solveMessageContainer.append(solveParagraph); solveMessageContainer.appendChild(solveTimeContainer); - dialogTitleBar.appendChild(dialogTitle); - dialog.appendChild(dialogTitleBar); - dialog.appendChild(titleBarSeparator); new OCButton({ tooltip: "Share Time".i18n() + "…", title: "Share Time".i18n() + "…", parent: solveMessageContainer, action: () => { - navigator - .share({ - text: "I solved %a, by %b, in %c!" - .i18n() - .replace("%a", puzzle.obj["info"]["title"]) - .replace("%b", puzzle.obj["info"]["author"]) - .replace("%c", puzzle.puzzleSeconds.toFormattedTime()), - }) - .catch(console.error); + navigator.share({ + text: "I solved %a, by %b, in %c!" + .i18n() + .replace("%a", puzzle.obj["info"]["title"]) + .replace("%b", puzzle.obj["info"]["author"]) + .replace("%c", puzzle.puzzleSeconds.toFormattedTime()), + }); }, }); - dialog.appendChild(solveMessageContainer); - document.body.appendChild(dialog); - dialog.showModal(); + new OCDialog({ + title: "Congratulations".i18n(), + content: solveMessageContainer, + }); } function showNotSolvedScreen() { @@ -960,37 +943,19 @@ function populate(obj) { } function displayInsertDialog() { - let dialog = document.createElement("dialog"); - dialog.classList.add("oc-dialog"); - let dialogTitleBar = document.createElement("div"); - let dialogTitle = document.createElement("h2"); - dialogTitle.textContent = "Insert".i18n(); - let titleBarSeparator = document.createElement("hr"); let frame = document.createElement("iframe"); frame.src = `${document.baseURI}frames/insert.html`; - let dialogCloseButton = new ControlButton( - "Close".i18n(), - OCIcons.close, - dialogTitleBar, - ); - dialogCloseButton.element.onclick = () => { - dialog.close(); - dialog.remove(); - }; - dialogTitleBar.appendChild(dialogTitle); - dialog.appendChild(dialogTitleBar); - dialog.appendChild(titleBarSeparator); - dialog.appendChild(frame); - document.body.appendChild(dialog); - dialog.showModal(); - - // Implement inserting functionality + let insertDialog = new OCDialog({ + title: "Insert".i18n(), + content: frame, + }); + // Inserting functionality frame.onload = () => { frame.contentWindow.document.getElementById("oc-insert-form").onsubmit = ( e, ) => { e.preventDefault(); - dialog.close(); + insertDialog.element.close(); puzzle.insertText( frame.contentWindow.document.getElementById("oc-insert-field").value, ); @@ -998,7 +963,7 @@ function displayInsertDialog() { }; } -Number.prototype.toFormattedTime = function() { +Number.prototype.toFormattedTime = function () { let hours = Math.floor(this / 3600); let minutes = Math.floor((this - hours * 3600) / 60); let seconds = this - hours * 3600 - minutes * 60; @@ -1019,11 +984,11 @@ Number.prototype.toFormattedTime = function() { } }; -String.prototype.toCapitalized = function() { +String.prototype.toCapitalized = function () { return this.charAt(0).toUpperCase() + this.slice(1); }; -String.prototype.i18n = function() { +String.prototype.i18n = function () { // Translates the string to the current language, if available if (l[`${this}`]) { return l[`${this}`]; @@ -1055,10 +1020,12 @@ function showSplashScreen(obj) {

OpenCrossword
Player

-

${obj["info"]["title"]} - ${obj["info"]["author"]}
${obj["info"]["description"] - }

- OpenCrossword banner +

${obj["info"]["title"]} - ${obj["info"]["author"]}
${ + obj["info"]["description"] + }

+ OpenCrossword banner
diff --git a/scripts/ui/ui-dialog.js b/scripts/ui/ui-dialog.js index 171c8b4..c9c7958 100644 --- a/scripts/ui/ui-dialog.js +++ b/scripts/ui/ui-dialog.js @@ -1,23 +1,49 @@ import OCButton from "./ui-button"; import OCIcons from "./ui-icons"; -"use strict"; +("use strict"); export default class OCDialog { constructor({ title = "", content = "", parent = document.body, + classes = [], + id = "", + open = true, + modal = true, }) { this.element = document.createElement("dialog"); - this.titleBar = document.createElement("div"); + this.titleBar = document.createElement("header"); this.closeButton = new OCButton({ + icon: OCIcons.close, tooltip: "Close", - - }) + parent: this.titleBar, + action: () => { + this.element.close(); + }, + }); this.title = document.createElement("h2"); + this.title.textContent = title; this.separator = document.createElement("hr"); this.content = document.createElement("div"); + + this.element.appendChild(this.titleBar); + this.titleBar.appendChild(this.title); + this.element.appendChild(this.separator); + this.content.appendChild(content); + this.element.appendChild(this.content); parent.appendChild(this.element); + for (const className of classes) { + this.element.classList.add(className); + } + this.element.classList.add("oc-dialog"); + this.content.classList.add("oc-dialog-content"); + this.element.id = id; + if (open && !modal) { + this.element.show(); + } else if (open && modal) { + this.element.showModal(); + } } } diff --git a/scripts/ui/ui-input.js b/scripts/ui/ui-input.js deleted file mode 100644 index 3918c74..0000000 --- a/scripts/ui/ui-input.js +++ /dev/null @@ -1 +0,0 @@ -"use strict"; diff --git a/styles/style.css b/styles/style.css index cc4340e..6cf6f21 100644 --- a/styles/style.css +++ b/styles/style.css @@ -706,12 +706,7 @@ label > * { .oc-dialog iframe { border: none; - height: 50%; -} - -.oc-dialog div { - display: flex; - align-items: center; + height: 50vh; } .oc-insert-preview {