1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-25 17:25:16 +03:00

Replace all 4 instances of jBox-style modal dialog boxes with HTML dialog element

Notes:
- The Power & Battery (2), OSD, and CLI tabs have been updated accordingly
- HTML default control behavior was used whereever possible
- HTML indent for the dialog tag is deliberately off to minimize diff noise in GitHub
- Layout is not 100% identical, but spiratually the same - in most cases no changes were needed
- OSD’s Font Manager dialog didn’t transition cleanly, so includes some slight restyling
- OSD Font Manager previously had a green background/color for the graphic - that “just works” now
- Specific cleanup (i.e., calling destroy()) does not seem necessary and was removed
- Removal of jBox from setup.js and global scope seems not to affect jBox tooltips
- Replacement of single quotes with double in main.js was done automatically by Prettier
This commit is contained in:
David Anson 2025-05-19 05:01:58 +00:00
parent f068b1a67c
commit f6cc33756f
13 changed files with 143 additions and 120 deletions

View file

@ -5320,6 +5320,10 @@
"message": "Font", "message": "Font",
"description": "Content of the selector for the OSD Font in the preview" "description": "Content of the selector for the OSD Font in the preview"
}, },
"osdSetupFontManagerTitle": {
"message": "OSD Font Manager",
"description": "Dialog title"
},
"osdSetupFontTypeDefault": { "osdSetupFontTypeDefault": {
"message": "Default", "message": "Default",
"description": "Font Default" "description": "Font Default"

View file

@ -41,6 +41,13 @@ a.disabled {
cursor: default; cursor: default;
color: #999; color: #999;
} }
.html-dialog {
border-color: var(--primary-500);
padding: 0px;
}
.html-dialog-content {
padding: 15px 15px;
}
.background_paper { .background_paper {
background-color: var(--surface-200); background-color: var(--surface-200);
background-image: background-image:

View file

@ -75,18 +75,17 @@
color: white; color: white;
} }
} }
.jBox-container { textarea#preview {
textarea#preview { background-color: rgba(0, 0, 0, 0.75);
background-color: rgba(0, 0, 0, 0.75); width: 100%;
width: 100%; resize: none;
resize: none; overflow-y: scroll;
overflow-y: scroll; overflow-x: hidden;
overflow-x: hidden; font-family: monospace;
font-family: monospace; color: white;
color: white; box-sizing: border-box;
padding: 5px; padding: 5px;
margin-bottom: 5px; margin-bottom: 5px;
}
} }
.cli-textcomplete-dropdown { .cli-textcomplete-dropdown {
border: 1px solid var(--surface-500); border: 1px solid var(--surface-500);

View file

@ -2,17 +2,14 @@
--context-menu-z-index: 10001; --context-menu-z-index: 10001;
.info { .info {
margin: 10px 0 0 0;
position: relative; position: relative;
width: 100%; width: 100%;
.progressLabel { .progressLabel {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 26px; height: 26px;
top: 0; margin-top: -22px;
left: 0;
text-align: center; text-align: center;
line-height: 24px;
color: white; color: white;
font-weight: bold; font-weight: bold;
a { a {
@ -68,12 +65,6 @@
width: 18px; width: 18px;
height: 18px; height: 18px;
} }
ul {
li {
list-style: circle;
margin-left: 30px;
}
}
.options { .options {
position: relative; position: relative;
margin-bottom: 10px; margin-bottom: 10px;
@ -382,13 +373,14 @@
font-weight: normal; font-weight: normal;
} }
} }
.grid-col { #font-logo {
margin: 0px; display: flex;
margin-bottom: 2em;
} }
#font-logo-preview-container { #font-logo-preview-container {
background: rgba(0, 255, 0, 0.4); background: rgba(0, 255, 0, 0.4);
margin-bottom: 10px; margin-bottom: 10px;
box-sizing: border-box; padding: 10px;
} }
#font-logo-preview { #font-logo-preview {
background: rgba(0, 255, 0, 1); background: rgba(0, 255, 0, 1);
@ -396,7 +388,8 @@
margin: auto; margin: auto;
} }
#font-logo-info { #font-logo-info {
font-size: 125%; flex: 1;
margin-left: 2em;
line-height: 150%; line-height: 150%;
h3 { h3 {
margin-bottom: 0.2em; margin-bottom: 0.2em;
@ -431,11 +424,11 @@
font-size: 9pt !important; font-size: 9pt !important;
cursor: pointer; cursor: pointer;
} }
.font-preview {
padding-bottom: 1.25rem;
}
.fontpresets_wrapper { .fontpresets_wrapper {
display: inline-block; padding: 1rem 0;
position: absolute;
right: 1.2em;
top: 0.8em;
} }
.fontpresets { .fontpresets {
background: var(--surface-200); background: var(--surface-200);

View file

@ -1,6 +1,5 @@
import "./jqueryPlugins"; import "./jqueryPlugins";
import $ from "jquery"; import $ from "jquery";
import "jbox";
import "../components/init.js"; import "../components/init.js";
import { gui_log } from "./gui_log.js"; import { gui_log } from "./gui_log.js";
// same, msp seems to be everywhere used from global scope // same, msp seems to be everywhere used from global scope

View file

@ -8,11 +8,11 @@ import { reinitializeConnection } from "../serial_backend";
import CONFIGURATOR from "../data_storage"; import CONFIGURATOR from "../data_storage";
import CliAutoComplete from "../CliAutoComplete"; import CliAutoComplete from "../CliAutoComplete";
import { gui_log } from "../gui_log"; import { gui_log } from "../gui_log";
import jBox from "jbox";
import $ from "jquery"; import $ from "jquery";
import { serial } from "../serial"; import { serial } from "../serial";
import FileSystem from "../FileSystem"; import FileSystem from "../FileSystem";
import { ispConnected } from "../utils/connection"; import { ispConnected } from "../utils/connection";
import { initializeModalDialog } from "../utils/initializeModalDialog";
const cli = { const cli = {
lineDelayMs: 5, lineDelayMs: 5,
@ -151,20 +151,16 @@ cli.initialize = function (callback) {
function previewCommands(result, fileName) { function previewCommands(result, fileName) {
if (!self.GUI.snippetPreviewWindow) { if (!self.GUI.snippetPreviewWindow) {
self.GUI.snippetPreviewWindow = new jBox("Modal", { self.GUI.snippetPreviewWindow = initializeModalDialog(
id: "snippetPreviewWindow", null,
width: "auto", "#snippetpreviewdialog",
height: "auto", "cliConfirmSnippetDialogTitle",
closeButton: "title", { fileName: fileName },
animation: false, );
isolateScroll: false, $("#snippetpreviewcontent a.confirm").click(() => executeSnippet(fileName));
title: i18n.getMessage("cliConfirmSnippetDialogTitle", { fileName: fileName }),
content: $("#snippetpreviewcontent"),
onCreated: () => $("#snippetpreviewcontent a.confirm").click(() => executeSnippet(fileName)),
});
} }
previewArea.val(result); previewArea.val(result);
self.GUI.snippetPreviewWindow.open(); self.GUI.snippetPreviewWindow.showModal();
} }
const file = await FileSystem.pickOpenFile(i18n.getMessage("fileSystemPickerFiles", { typeof: "TXT" }), ".txt"); const file = await FileSystem.pickOpenFile(i18n.getMessage("fileSystemPickerFiles", { typeof: "TXT" }), ".txt");
@ -530,10 +526,6 @@ cli.supportWarningDialog = function (onAccept) {
}; };
cli.cleanup = function (callback) { cli.cleanup = function (callback) {
if (TABS.cli.GUI.snippetPreviewWindow) {
TABS.cli.GUI.snippetPreviewWindow.destroy();
TABS.cli.GUI.snippetPreviewWindow = null;
}
if (!(CONFIGURATOR.connectionValid && CONFIGURATOR.cliValid && CONFIGURATOR.cliActive)) { if (!(CONFIGURATOR.connectionValid && CONFIGURATOR.cliValid && CONFIGURATOR.cliActive)) {
if (callback) { if (callback) {
callback(); callback();

View file

@ -16,6 +16,7 @@ import debounce from "lodash.debounce";
import $ from "jquery"; import $ from "jquery";
import FileSystem from "../FileSystem"; import FileSystem from "../FileSystem";
import { have_sensor } from "../sensor_helpers"; import { have_sensor } from "../sensor_helpers";
import { initializeModalDialog } from "../utils/initializeModalDialog";
const FONT = {}; const FONT = {};
const SYM = {}; const SYM = {};
@ -3254,16 +3255,8 @@ osd.initialize = function (callback) {
$(".display-layout .preview").css("zoom", previewZoom); $(".display-layout .preview").css("zoom", previewZoom);
} }
// Open modal window // Enable font manager dialog
OSD.GUI.fontManager = new jBox("Modal", { OSD.GUI.fontManager = initializeModalDialog("#fontmanager", "#fontmanagerdialog", "osdSetupFontManagerTitle");
width: 750,
height: 455,
closeButton: "title",
animation: false,
attach: $("#fontmanager"),
title: "OSD Font Manager",
content: $("#fontmanagercontent"),
});
$(".elements-container div.cf_tip").attr("title", i18n.getMessage("osdSectionHelpElements")); $(".elements-container div.cf_tip").attr("title", i18n.getMessage("osdSectionHelpElements"));
$(".videomode-container div.cf_tip").attr("title", i18n.getMessage("osdSectionHelpVideoMode")); $(".videomode-container div.cf_tip").attr("title", i18n.getMessage("osdSectionHelpVideoMode"));
@ -4093,10 +4086,6 @@ osd.initialize = function (callback) {
}; };
osd.cleanup = function (callback) { osd.cleanup = function (callback) {
if (OSD.GUI.fontManager) {
OSD.GUI.fontManager.destroy();
}
// unbind "global" events // unbind "global" events
$(document).unbind("keypress"); $(document).unbind("keypress");
$(document).off("click", "span.progressLabel a"); $(document).off("click", "span.progressLabel a");

View file

@ -5,8 +5,8 @@ import { mspHelper } from "../msp/MSPHelper";
import FC from "../fc"; import FC from "../fc";
import MSP from "../msp"; import MSP from "../msp";
import MSPCodes from "../msp/MSPCodes"; import MSPCodes from "../msp/MSPCodes";
import jBox from "jbox";
import $ from "jquery"; import $ from "jquery";
import { initializeModalDialog } from "../utils/initializeModalDialog";
const power = { const power = {
supported: false, supported: false,
@ -20,13 +20,6 @@ power.initialize = function (callback) {
GUI.active_tab = "power"; GUI.active_tab = "power";
} }
if (GUI.calibrationManager) {
GUI.calibrationManager.destroy();
}
if (GUI.calibrationManagerConfirmation) {
GUI.calibrationManagerConfirmation.destroy();
}
function load_status() { function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_voltage_meters); MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_voltage_meters);
} }
@ -223,9 +216,6 @@ power.initialize = function (callback) {
} }
$(".tab-power").addClass("supported"); $(".tab-power").addClass("supported");
$("#calibrationmanagercontent").hide();
$("#calibrationmanagerconfirmcontent").hide();
// battery // battery
const templateBatteryState = $("#tab-power-templates .battery-state .battery-state"); const templateBatteryState = $("#tab-power-templates .battery-state .battery-state");
const destinationBatteryState = $(".tab-power .battery-state"); const destinationBatteryState = $(".tab-power .battery-state");
@ -347,33 +337,21 @@ power.initialize = function (callback) {
//calibration manager //calibration manager
let calibrationconfirmed = false; let calibrationconfirmed = false;
GUI.calibrationManager = new jBox("Modal", { GUI.calibrationManager = initializeModalDialog(
width: 400, "#calibrationmanager",
height: 230, "#calibrationmanagerdialog",
closeButton: "title", "powerCalibrationManagerTitle",
animation: false, null,
attach: $("#calibrationmanager"), () => calibrationconfirmed || TABS.power.initialize(),
title: i18n.getMessage("powerCalibrationManagerTitle"), );
content: $("#calibrationmanagercontent"),
onCloseComplete: function () {
if (!calibrationconfirmed) {
TABS.power.initialize();
}
},
});
GUI.calibrationManagerConfirmation = new jBox("Modal", { GUI.calibrationManagerConfirmation = initializeModalDialog(
width: 400, "#calibrate",
height: 230, "#calibrationmanagerconfirmdialog",
closeButton: "title", "powerCalibrationManagerConfirmationTitle",
animation: false, null,
attach: $("#calibrate"), () => GUI.calibrationManager.close(),
title: i18n.getMessage("powerCalibrationManagerConfirmationTitle"), );
content: $("#calibrationmanagerconfirmcontent"),
onCloseComplete: function () {
GUI.calibrationManager.close();
},
});
$("a.calibrationmanager").click(function () { $("a.calibrationmanager").click(function () {
if (FC.BATTERY_CONFIG.voltageMeterSource == 1 && FC.BATTERY_STATE.voltage > 0.1) { if (FC.BATTERY_CONFIG.voltageMeterSource == 1 && FC.BATTERY_STATE.voltage > 0.1) {
@ -564,13 +542,6 @@ power.initialize = function (callback) {
power.cleanup = function (callback) { power.cleanup = function (callback) {
if (callback) callback(); if (callback) callback();
if (GUI.calibrationManager) {
GUI.calibrationManager.destroy();
}
if (GUI.calibrationManagerConfirmation) {
GUI.calibrationManagerConfirmation.destroy();
}
}; };
TABS.power = power; TABS.power = power;

View file

@ -0,0 +1,64 @@
import $ from "jquery";
import { i18n } from "../localization";
/**
* Gets the title bar for a modal dialog.
* @param {string} messageId Localized message identifier.
* @param {object} [messageParameters] Localized message parameters
* @param {() => void} onClose Function invoked by the close button.
* @returns {JQuery<HTMLElement>} Dialog title bar.
*/
function getDialogTitleBar(messageId, messageParameters, onClose) {
// HTML structure
const dialogTitleBar = $(`
<div style="display: flex; height: 47px; background: var(--surface-300); border-bottom: 1px solid var(--surface-950);">
<div style="flex: 1; display: flex; align-items: center;">
<div style="padding: 15px;">${i18n.getMessage(messageId, messageParameters || undefined)}</div>
</div>
<div id="dialogclose" style="flex: 0 0 47px; display: flex; align-items: center; justify-content: center; cursor: pointer;">
<svg width="10" height="10" version="1.1" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="0" x2="10" y2="10" stroke="var(--surface-950)" stroke-width="2"/>
<line x1="0" y1="10" x2="10" y2="0" stroke="var(--surface-950)" stroke-width="2"/>
</svg>
</div>
</div>
`);
// Handle close button
dialogTitleBar.find("#dialogclose").on("click", onClose);
// Return title bar
return dialogTitleBar;
}
/**
* Initializes a modal dialog from an HTML dialog element.
* @param {JQuery.Selector|null} activationSelector JQuery selector for the activation element.
* @param {JQuery.Selector} dialogSelector JQuery selector for the dialog element.
* @param {string} messageId Localized message identifier.
* @param {object} [messageParameters] Localized message parameters
* @param {() => void} [onClose] Function invoked when the dialog is closed.
* @returns {HTMLDialogElement} HTML dialog element.
*/
export function initializeModalDialog(activationSelector, dialogSelector, messageId, messageParameters, onClose) {
// Get dialog references
const dialog = $(dialogSelector);
const dialogElement = dialog.get(0);
const dialogContainerElement = dialog.children().first().get(0);
// Add dialog title bar
dialog.prepend(
getDialogTitleBar(messageId, messageParameters, () => {
dialogElement.close();
}),
);
// Handle close event
dialogElement.addEventListener("close", () => {
onClose && onClose();
});
// Handle activation button click
$(activationSelector).on("click", () => {
dialogElement.showModal();
// Reset any previous scrolling
dialogContainerElement.scroll(0, 0);
});
// Return dialog element
return dialogElement;
}

View file

@ -13,15 +13,17 @@
</div> </div>
<!-- Snippet preview dialog --> <!-- Snippet preview dialog -->
<div id="snippetpreviewcontent" style="display: none"> <dialog closedby="any" id="snippetpreviewdialog" class="html-dialog">
<div id="snippetpreviewcontent" class="html-dialog-content">
<div class="note"> <div class="note">
<p i18n="cliConfirmSnippetNote"></p> <p i18n="cliConfirmSnippetNote"></p>
</div> </div>
<textarea id="preview" cols="120" rows="20"></textarea> <textarea id="preview" rows="20"></textarea>
<div class="default_btn"> <div class="default_btn">
<a class="confirm" href="#" i18n="cliConfirmSnippetBtn"></a> <a class="confirm" href="#" i18n="cliConfirmSnippetBtn"></a>
</div> </div>
</div> </div>
</dialog>
<dialog class="supportWarningDialog"> <dialog class="supportWarningDialog">
<h3 i18n="supportWarningDialogTitle"></h3> <h3 i18n="supportWarningDialogTitle"></h3>

View file

@ -151,7 +151,8 @@
</div> </div>
<!-- Font Manager dialog --> <!-- Font Manager dialog -->
<div id="fontmanagercontent" style="display:none; width:720px;"> <dialog closedby="any" id="fontmanagerdialog" class="html-dialog" style="width: 750px;">
<div class="html-dialog-content" style="height: 430px; overflow-y: scroll;">
<div class="font-picker" style="margin-bottom: 10px;"> <div class="font-picker" style="margin-bottom: 10px;">
<h1 class="tab_title" i18n="osdSetupFontPresets"></h1> <h1 class="tab_title" i18n="osdSetupFontPresets"></h1>
<!-- Font preview and list --> <!-- Font preview and list -->
@ -167,13 +168,13 @@
</div> </div>
<!-- Boot logo setup --> <!-- Boot logo setup -->
<h1 class="tab_title" i18n="osdSetupCustomLogoTitle"></h1> <h1 class="tab_title" i18n="osdSetupCustomLogoTitle"></h1>
<div class="grid-row"> <div id="font-logo">
<div id="font-logo-preview-container" class="content_wrapper grid-col col6"> <div id="font-logo-preview-container">
<div id="font-logo-preview"> <div id="font-logo-preview">
<!-- this will be resized at runtime --> <!-- this will be resized at runtime -->
</div> </div>
</div> </div>
<div id="font-logo-info grid-col col6"> <div id="font-logo-info">
<h3 i18n="osdSetupCustomLogoInfoTitle"></h3> <h3 i18n="osdSetupCustomLogoInfoTitle"></h3>
<ul> <ul>
<li id="font-logo-info-size" i18n="osdSetupCustomLogoInfoImageSize"></li> <li id="font-logo-info-size" i18n="osdSetupCustomLogoInfoImageSize"></li>
@ -183,22 +184,22 @@
</div> </div>
</div> </div>
<!-- Replace logo button --> <!-- Replace logo button -->
<div class="default_btn" style="width:100%; float:left;"> <div class="default_btn">
<a class="replace_logo" i18n="osdSetupCustomLogoOpenImageButton"></a> <a class="replace_logo" i18n="osdSetupCustomLogoOpenImageButton"></a>
</div> </div>
<!-- Upload progress bar --> <!-- Upload progress bar -->
<div class="info"> <div class="info">
<a name="progressbar"></a> <a name="progressbar"></a>
<progress class="progress" value="0" min="0" max="100"></progress> <progress class="progress" value="0" min="0" max="100"></progress>
<div class="progressLabel" style="margin-top: -21px; width: 95%; text-align: center; position: absolute;"></div> <div class="progressLabel"></div>
</div> </div>
</div> </div>
<!-- Upload button --> <!-- Upload button -->
<div class="default_btn green" style="width:100%; float:left; <div class="default_btn green">
">
<a class="flash_font active" i18n="osdSetupUploadFont"></a> <a class="flash_font active" i18n="osdSetupUploadFont"></a>
</div> </div>
</div> </div>
</dialog>
</div> </div>
</div> </div>

View file

@ -197,7 +197,8 @@
</div> </div>
</div> </div>
<div class="tab-power" id="calibrationmanagercontent"> <dialog closedby="any" id="calibrationmanagerdialog" class="html-dialog">
<div id="calibrationmanagercontent" class="html-dialog-content">
<div class = "note"> <div class = "note">
<p i18n="powerCalibrationManagerHelp"></p> <p i18n="powerCalibrationManagerHelp"></p>
</div> </div>
@ -228,8 +229,10 @@
<a class="calibrate" id="calibrate" href="#" i18n="powerCalibrationSave"></a> <a class="calibrate" id="calibrate" href="#" i18n="powerCalibrationSave"></a>
</div> </div>
</div> </div>
</dialog>
<div class="tab-power" id="calibrationmanagerconfirmcontent"> <dialog closedby="any" id="calibrationmanagerconfirmdialog" class="html-dialog">
<div id="calibrationmanagerconfirmcontent" class="html-dialog-content">
<div class = "note"> <div class = "note">
<p i18n="powerCalibrationConfirmHelp"></p> <p i18n="powerCalibrationConfirmHelp"></p>
</div> </div>
@ -257,3 +260,4 @@
<a class="discardcalibration" id="discardcalibration" href="#" i18n="powerCalibrationDiscard"></a> <a class="discardcalibration" id="discardcalibration" href="#" i18n="powerCalibrationDiscard"></a>
</div> </div>
</div> </div>
</dialog>

View file

@ -1,14 +1,12 @@
import { JSDOM } from "jsdom"; import { JSDOM } from "jsdom";
import $ from "jquery"; import $ from "jquery";
import { vi } from "vitest"; import { vi } from "vitest";
import jBox from "jbox";
// Note: this can go away once jquery is used as module everywhere // Note: this can go away once jquery is used as module everywhere
const { window } = new JSDOM(""); const { window } = new JSDOM("");
$(window); $(window);
globalThis.$ = $; globalThis.$ = $;
globalThis.jQuery = $; globalThis.jQuery = $;
globalThis.jBox = jBox;
Object.defineProperty(window, "matchMedia", { Object.defineProperty(window, "matchMedia", {
writable: true, writable: true,