Lookup enhancements
This commit is contained in:
@@ -648,6 +648,16 @@ tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.lookup-option-selected {
|
||||
background-color: #e3f2fd;
|
||||
border-left: 3px solid #2196f3;
|
||||
padding-left: calc(0.5rem - 3px);
|
||||
}
|
||||
|
||||
.lookup-option-selected:hover {
|
||||
background-color: #bbdefb;
|
||||
}
|
||||
|
||||
.lookup-option:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@@ -234,8 +234,8 @@
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('WebSocket message received:', data);
|
||||
|
||||
// Refresh display when any PPR-related or local flight event occurs
|
||||
if (data.type && (data.type.includes('ppr_') || data.type === 'status_update' || data.type.includes('local_flight_'))) {
|
||||
// Refresh display when any PPR-related, local flight, or departure event occurs
|
||||
if (data.type && (data.type.includes('ppr_') || data.type === 'status_update' || data.type.includes('local_flight_') || data.type.includes('departure_'))) {
|
||||
console.log('Flight update detected, refreshing display...');
|
||||
loadArrivals();
|
||||
loadDepartures();
|
||||
|
||||
123
web/lookups.js
123
web/lookups.js
@@ -2,6 +2,25 @@
|
||||
* Lookup Utilities - Reusable functions for aircraft and airport lookups
|
||||
*/
|
||||
|
||||
/**
|
||||
* Format aircraft registration based on UK rules
|
||||
* - 5 alphabetic chars: add hyphen after first char (GIVYY -> G-IVYY)
|
||||
* - Otherwise: just uppercase (N123AD -> N123AD)
|
||||
*/
|
||||
function formatAircraftRegistration(input) {
|
||||
if (!input) return '';
|
||||
|
||||
const cleaned = input.trim().toUpperCase();
|
||||
|
||||
// If exactly 5 characters and all alphabetic, add hyphen
|
||||
if (cleaned.length === 5 && /^[A-Z]{5}$/.test(cleaned)) {
|
||||
return cleaned[0] + '-' + cleaned.substring(1);
|
||||
}
|
||||
|
||||
// Otherwise just return uppercase version
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reusable lookup handler
|
||||
* @param {string} fieldId - ID of the input field
|
||||
@@ -19,11 +38,15 @@ function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
};
|
||||
const config = { ...defaults, ...options };
|
||||
let debounceTimeout;
|
||||
let currentResults = [];
|
||||
let selectedIndex = -1;
|
||||
let keydownHandlerAttached = false;
|
||||
|
||||
const lookup = {
|
||||
// Main handler called by oninput
|
||||
handle: (value) => {
|
||||
clearTimeout(debounceTimeout);
|
||||
selectedIndex = -1; // Reset selection on new input
|
||||
|
||||
if (!value || value.trim().length < config.minLength) {
|
||||
lookup.clear();
|
||||
@@ -36,6 +59,75 @@ function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
}, config.debounceMs);
|
||||
},
|
||||
|
||||
// Attach keyboard handler once (for airport lookups)
|
||||
attachKeyboardHandler: () => {
|
||||
if (config.isAirport && !keydownHandlerAttached) {
|
||||
try {
|
||||
const inputField = document.getElementById(fieldId);
|
||||
if (inputField) {
|
||||
inputField.addEventListener('keydown', (e) => lookup.handleKeydown(e));
|
||||
keydownHandlerAttached = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error attaching keyboard handler:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Handle keyboard events
|
||||
handleKeydown: (event) => {
|
||||
if (!currentResults || currentResults.length === 0) return;
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
event.preventDefault();
|
||||
selectedIndex = Math.min(selectedIndex + 1, currentResults.length - 1);
|
||||
lookup.updateSelection();
|
||||
} else if (event.key === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
selectedIndex = Math.max(selectedIndex - 1, -1);
|
||||
lookup.updateSelection();
|
||||
} else if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
if (selectedIndex >= 0 && currentResults[selectedIndex]) {
|
||||
lookup.selectResult(currentResults[selectedIndex]);
|
||||
} else if (currentResults.length === 1) {
|
||||
// Auto-select if only one result and Enter pressed
|
||||
lookup.selectResult(currentResults[0]);
|
||||
}
|
||||
} else if (event.key === 'Escape') {
|
||||
lookup.clear();
|
||||
selectedIndex = -1;
|
||||
}
|
||||
},
|
||||
|
||||
// Update visual selection
|
||||
updateSelection: () => {
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
if (!resultsDiv) return;
|
||||
|
||||
const options = resultsDiv.querySelectorAll('.lookup-option');
|
||||
options.forEach((opt, idx) => {
|
||||
if (idx === selectedIndex) {
|
||||
opt.classList.add('lookup-option-selected');
|
||||
opt.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
opt.classList.remove('lookup-option-selected');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Select a result item
|
||||
selectResult: (item) => {
|
||||
const field = document.getElementById(fieldId);
|
||||
if (field) {
|
||||
field.value = item.icao;
|
||||
}
|
||||
lookup.clear();
|
||||
currentResults = [];
|
||||
selectedIndex = -1;
|
||||
if (selectCallback) selectCallback(item.icao);
|
||||
},
|
||||
|
||||
// Perform the lookup
|
||||
perform: async (searchTerm) => {
|
||||
try {
|
||||
@@ -71,9 +163,15 @@ function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
|
||||
if (config.isAircraft) {
|
||||
// Aircraft lookup: auto-populate on single match, show message on multiple
|
||||
// Aircraft lookup: auto-populate on single match, format input on no match
|
||||
if (!results || results.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="aircraft-no-match">No matches found</div>';
|
||||
// Format the aircraft registration and auto-populate
|
||||
const formatted = formatAircraftRegistration(searchTerm);
|
||||
const field = document.getElementById(fieldId);
|
||||
if (field) {
|
||||
field.value = formatted;
|
||||
}
|
||||
resultsDiv.innerHTML = ''; // Clear results, field is auto-populated
|
||||
} else if (results.length === 1) {
|
||||
// Single match - auto-populate
|
||||
const aircraft = results[0];
|
||||
@@ -108,18 +206,21 @@ function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
// Airport lookup: show list of options
|
||||
// Airport lookup: show list of options with keyboard navigation
|
||||
if (!results || results.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="lookup-no-match">No matches found - will use as entered</div>';
|
||||
currentResults = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const itemsToShow = results.slice(0, config.maxResults);
|
||||
const matchText = itemsToShow.length === 1 ? 'Match found - click to select:' : 'Multiple matches found - select one:';
|
||||
currentResults = results.slice(0, config.maxResults);
|
||||
selectedIndex = -1; // Reset selection when showing new results
|
||||
|
||||
const matchText = currentResults.length === 1 ? 'Match found - press ENTER or click to select:' : 'Multiple matches found - use arrow keys and ENTER to select:';
|
||||
|
||||
let html = `<div class="lookup-no-match" style="margin-bottom: 0.5rem;">${matchText}</div><div class="lookup-list">`;
|
||||
|
||||
itemsToShow.forEach(item => {
|
||||
currentResults.forEach((item, idx) => {
|
||||
html += `
|
||||
<div class="lookup-option" onclick="lookupManager.selectItem('${resultsId}', '${fieldId}', '${item.icao}')">
|
||||
<div class="lookup-code">${item.icao}</div>
|
||||
@@ -131,6 +232,9 @@ function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
|
||||
html += '</div>';
|
||||
resultsDiv.innerHTML = html;
|
||||
|
||||
// Attach keyboard handler (only once per lookup instance)
|
||||
lookup.attachKeyboardHandler();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -238,6 +342,13 @@ function initializeLookups() {
|
||||
{ isAircraft: true, minLength: 4, debounceMs: 300 }
|
||||
);
|
||||
lookupManager.register('local-aircraft', localAircraftLookup);
|
||||
|
||||
// Attach keyboard handlers to airport input fields
|
||||
setTimeout(() => {
|
||||
if (arrivalAirportLookup.attachKeyboardHandler) arrivalAirportLookup.attachKeyboardHandler();
|
||||
if (departureAirportLookup.attachKeyboardHandler) departureAirportLookup.attachKeyboardHandler();
|
||||
if (localOutToLookup.attachKeyboardHandler) localOutToLookup.attachKeyboardHandler();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Initialize on DOM ready or immediately if already loaded
|
||||
|
||||
Reference in New Issue
Block a user