Do more filter panel generation in HTML, less in JS

This commit is contained in:
Ian Renton
2025-10-13 20:41:13 +01:00
parent cfa3aaedb0
commit c051aad157
6 changed files with 106 additions and 67 deletions

View File

@@ -26,7 +26,45 @@
</div>
<div class="card-body">
<div id="filters-container" class="row row-cols-1 row-cols-md-3 g-4"></div>
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">DX Continent</h5>
<p id="dx-continent-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Sources</h5>
<p id="source-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Duration Limit <i class='fa-solid fa-circle-question' title='Some users create long-duration alerts for the period they will be generally in and around xOTA references, when they are not indending to be on the air most of the time. Use this control to restrict the maximum duration of spots that the software will display, and exclude any with a long duration, to avoid these filling up the list. By default, we allow DXpeditions to be displayed even if they are longer than this limit, because on a DXpedition the operators typically ARE on the air most of the time.'></i></h5>
<p class="card-text spothole-card-text">
Hide any alerts lasting more than:<br/>
<select id="max-duration" class="storeable-select form-select" onclick="filtersUpdated();" style="width: 8em; display: inline-block;">
<option value="10800">3 hours</option>
<option value="43200">12 hours</option>
<option value="86400" selected>24 hours</option>
<option value="604800">1 week</option>
<option value="2419200">4 weeks</option>
<option value="9999999999">No limit</option>
</select>
</p>
<p class='card-text spothole-card-text' style='line-height: 1.5em !important;'>
<input class="form-check-input storeable-checkbox" type="checkbox" value="" onclick="filtersUpdated();" id="dxpeditions_skip_max_duration_check" checked><label class="form-check-label ms-2" for="dxpeditions_skip_max_duration_check">Allow DXpeditions that are longer</label>
</p>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -22,6 +22,7 @@
</div>
<div class="col-auto">
<p class="d-inline-flex gap-1">
<button id="add-spot-button" type="button" class="btn btn-outline-success" data-bs-toggle="button" onclick="toggleAddSpotPanel();"><i class="fa-solid fa-comment"></i> Add Spot</button>
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleFiltersPanel();"><i class="fa-solid fa-filter"></i> Filters</button>
<button id="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i> Display</button>
</p>
@@ -41,8 +42,50 @@
</div>
<div class="card-body">
<div id="filters-container-1" class="row row-cols-1 g-4 mb-4"></div>
<div id="filters-container-2" class="row row-cols-1 row-cols-md-4 g-4"></div>
<div class="row row-cols-1 g-4 mb-4">
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Bands</h5>
<p id="band-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
</div>
<div class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">DX Continent</h5>
<p id="dx-continent-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">DE Continent</h5>
<p id="de-continent-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Modes</h5>
<p id="mode-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Sources</h5>
<p id="source-options" class="card-text spothole-card-text"></p>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -47,6 +47,10 @@ div.appearing-panel {
display: none;
}
button#add-spot-button {
display: none;
}
.spothole-card-text {
line-height: 2.5em !important;
}

View File

@@ -263,12 +263,8 @@ function loadOptions() {
options = jsonData;
// Populate the filters panel
$("#filters-container").append(generateMultiToggleFilterCard("DX Continent", "dx_continent", options["continents"]));
$("#filters-container").append(generateMultiToggleFilterCard("Sources", "source", options["alert_sources"]));
// Options doesn't give us anything for Max Duration as it's a free numeric input, but we generate our own
// filter card for this.
$("#filters-container").append(generateMaxDurationDropdownFilterCard(options["alert_sources"]));
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
generateMultiToggleFilterCard("#source-options", "source", options["alert_sources"]);
// Load filters from settings storage
loadSettings();
@@ -279,33 +275,6 @@ function loadOptions() {
});
}
// Generate maximum duration drop-down filter card. This one is a special case.
function generateMaxDurationDropdownFilterCard(band_options) {
let $col = $("<div class='col'>")
let $card = $("<div class='card'>");
let $card_body = $("<div class='card-body'>");
$card_body.append(`<h5 class='card-title'>Duration Limit <i class='fa-solid fa-circle-question' title='Some users create long-duration alerts for the period they will be generally in and around xOTA references, when they are not indending to be on the air most of the time. Use this control to restrict the maximum duration of spots that the software will display, and exclude any with a long duration, to avoid these filling up the list. By default, we allow DXpeditions to be displayed even if they are longer than this limit, because on a DXpedition the operators typically ARE on the air most of the time.'></i></h5>`);
$p = $("<p class='card-text spothole-card-text'>");
$p.append("Hide any alerts lasting more than:<br/>");
$p.append(`<select id="max-duration" class="storeable-select form-select" onclick="filtersUpdated();" style="width: 8em; display: inline-block;">
<option value="10800">3 hours</option>
<option value="43200">12 hours</option>
<option value="86400" selected>24 hours</option>
<option value="604800">1 week</option>
<option value="2419200">4 weeks</option>
<option value="9999999999">No limit</option>
</select>`);
$p2 = $("<p class='card-text spothole-card-text' style='line-height: 1.5em !important;'>");
$p2.append(`<input class="form-check-input storeable-checkbox" type="checkbox" value="" onclick="filtersUpdated();" id="dxpeditions_skip_max_duration_check" checked><label class="form-check-label ms-2" for="dxpeditions_skip_max_duration_check">Allow DXpeditions that are longer</label>`);
// Compile HTML elements to return
$card_body.append($p);
$card_body.append($p2);
$card.append($card_body);
$col.append($card);
return $col;
}
// Method called when any filter is changed to reload the alerts and persist the filter settings.
function filtersUpdated() {
loadAlerts();

View File

@@ -28,24 +28,14 @@ function allFilterOptionsSelected(parameter) {
}
// Generate a filter card with multiple toggle buttons plus All/None buttons
function generateMultiToggleFilterCard(displayName, filterQuery, options) {
let $col = $("<div class='col'>")
let $card = $("<div class='card'>");
let $card_body = $("<div class='card-body'>");
$card_body.append(`<h5 class='card-title'>${displayName}</h5>`);
$p = $("<p class='card-text spothole-card-text'>");
// Generate a filter card with multiple toggle buttons plus All/None buttons.
function generateMultiToggleFilterCard(elementID, filterQuery, options) {
// Create a button for each option
options.forEach(o => {
$p.append(`<input type="checkbox" class="btn-check filter-button-${filterQuery} storeable-checkbox" name="options" id="filter-button-${filterQuery}-${o}" value="${o}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" for="filter-button-${filterQuery}-${o}">${o}</label> `);
$(elementID).append(`<input type="checkbox" class="btn-check filter-button-${filterQuery} storeable-checkbox" name="options" id="filter-button-${filterQuery}-${o}" value="${o}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" for="filter-button-${filterQuery}-${o}">${o}</label> `);
});
// Create All/None buttons
$p.append(` <span style="display: inline-block"><button id="filter-button-${filterQuery}-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('${filterQuery}', true);">All</button>&nbsp;<button id="filter-button-${filterQuery}-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('${filterQuery}', false);">None</button></span>`);
// Compile HTML elements to return
$card_body.append($p);
$card.append($card_body);
$col.append($card);
return $col;
$(elementID).append(` <span style="display: inline-block"><button id="filter-button-${filterQuery}-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('${filterQuery}', true);">All</button>&nbsp;<button id="filter-button-${filterQuery}-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('${filterQuery}', false);">None</button></span>`);
}
// Method called when "All" or "None" is clicked

View File

@@ -255,11 +255,11 @@ function loadOptions() {
addBandColourCSS(options["bands"]);
// Populate the filters panel
$("#filters-container-1").append(generateBandsMultiToggleFilterCard(options["bands"]));
$("#filters-container-2").append(generateMultiToggleFilterCard("DX Continent", "dx_continent", options["continents"]));
$("#filters-container-2").append(generateMultiToggleFilterCard("DE Continent", "de_continent", options["continents"]));
$("#filters-container-2").append(generateMultiToggleFilterCard("Modes", "mode_type", options["mode_types"]));
$("#filters-container-2").append(generateMultiToggleFilterCard("Sources", "source", options["spot_sources"]));
generateBandsMultiToggleFilterCard(options["bands"]);
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
// Load settings from settings storage now all the controls are available
loadSettings();
@@ -272,6 +272,11 @@ function loadOptions() {
$("#tableShowBearing").prop('checked', false);
}
// Show the Add Spot button if spotting is allowed
if (options["spot_allowed"]) {
$("#add-spot-button").show();
}
// Load spots and set up the timer
loadSpots();
setInterval(loadSpots, REFRESH_INTERVAL_SEC * 1000);
@@ -294,25 +299,15 @@ function addBandColourCSS(band_options) {
// Generate bands filter card. This one is a special case.
function generateBandsMultiToggleFilterCard(band_options) {
let $col = $("<div class='col'>")
let $card = $("<div class='card'>");
let $card_body = $("<div class='card-body'>");
$card_body.append(`<h5 class='card-title'>Bands</h5>`);
$p = $("<p class='card-text spothole-card-text'>");
// Create a button for each option
band_options.forEach(o => {
// CSS doesn't like IDs with decimal points in, so we need to replace that in the same way as when we originally
// queried the options endpoint and set our CSS.
var cssFormattedBandName = o['name'] ? o['name'].replace('.', 'p') : "unknown";
$p.append(`<input type="checkbox" class="btn-check filter-button-band storeable-checkbox" name="options" id="filter-button-band-${cssFormattedBandName}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline" id="filter-button-label-band-${cssFormattedBandName}" for="filter-button-band-${cssFormattedBandName}">${o['name']}</label> `);
$("#band-options").append(`<input type="checkbox" class="btn-check filter-button-band storeable-checkbox" name="options" id="filter-button-band-${cssFormattedBandName}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline" id="filter-button-label-band-${cssFormattedBandName}" for="filter-button-band-${cssFormattedBandName}">${o['name']}</label> `);
});
// Create All/None buttons
$p.append(` <span style="display: inline-block"><button id="filter-button-band-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', true);">All</button>&nbsp;<button id="filter-button-band-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', false);">None</button></span>`);
// Compile HTML elements to return
$card_body.append($p);
$card.append($card_body);
$col.append($card);
return $col;
$("#band-options").append(` <span style="display: inline-block"><button id="filter-button-band-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', true);">All</button>&nbsp;<button id="filter-button-band-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', false);">None</button></span>`);
}
// Work out if the user's entered grid is a valid Maidenhead grid