Skip to content

Commit

Permalink
Search UI (#74)
Browse files Browse the repository at this point in the history
- select facility or milestone search
  • Loading branch information
hiddewie authored Jul 21, 2024
1 parent 85dbbaf commit 6fd6f27
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 93 deletions.
56 changes: 3 additions & 53 deletions proxy/css/ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,61 +24,11 @@ body {
font-family: monospace;
font-size: 0.8em;
}
#cancel-search {
margin: 20px;
padding: 0.4rem;
border: 2px solid rgba(255, 255, 255, .6);
border-radius: 4px;
cursor: pointer;
}
.search-container {
position: absolute;
z-index: 101;
background: transparent;
width: 400px;
max-width: 90%;
left: 50%;
transform: translate(-50%, 0);
top: 15vh;
}
.search-container input[type=search] {
padding: 6px;
font-family: sans-serif;
width: 14rem;
}
.search-container .input-group-append {
background-color: white;
}
#search-results {
#search-results, #search-milestones-form {
display: none;
background-color: white;
padding: .3rem 0;
max-height: 300px;
overflow-y: auto;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
box-shadow: 0 0 0 2px rgba(0,0,0,.1);
}
#search-results .result {
border-bottom: 1px solid #eee;
padding: 0 .5rem;
cursor: pointer;
}
#search-results .result:nth-child(2n) {
background: #f8f8f8;
}
#search-results .result:hover, #search-results .result:nth-child(2n):hover {
background: #eee;
}
.search-container .search-icon {
position: absolute;
top: 12px;
right: 14px;
z-index: 30;
cursor: pointer;
}
.search-container #cancel-icon {
display: none;
#search-results .no-results {
text-align: center;
}

#legend {
Expand Down
95 changes: 78 additions & 17 deletions proxy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,85 @@
</head>

<body>
<div id="search-backdrop" class="modal backdrop">
<svg role="presentation" onclick="hideSearch()" id="cancel-search" class="search-icon" viewBox="0 0 32 32" width="32" height="32" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="3">
<path d="M4 4 L28 28" />
<path d="M4 28 L28 4" />
</svg>
<div class="search-container">
<div class="input-group">
<input type="search" id="search" class="form-control" placeholder="Search..." autocomplete="no">
<svg role="presentation" onclick="searchFor(search.value)" id="search-icon" class='search-icon' viewBox="0 0 32 32" width="14" height="14" fill="none" stroke="#57ae00" stroke-linecap="round" stroke-linejoin="round" stroke-width="4">
<circle cx="14" cy="14" r="12" />
<path d="M23 23 L30 30" />
</svg>
<svg role="presentation" onclick="hideSearchResults()" id="cancel-icon" class="search-icon" viewBox="0 0 32 32" width="14" height="14" fill="none" stroke="#ae0000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3">
<path d="M4 4 L28 28" />
<path d="M4 28 L28 4" />
</svg>
<div id="search-backdrop" class="modal backdrop" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header bg-light">
<h5 class="modal-title">Search</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="hideSearch()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-pills mb-2">
<li class="nav-item">
<a id="search-facilities-tab" class="nav-link active" href="javascript:searchFacilities()">Facilities</a>
</li>
<li class="nav-item">
<a id="search-milestones-tab" class="nav-link" href="javascript:searchMilestones()">Milestones</a>
</li>
</ul>
<form id="search-facilities-form">
<div class="form-group">
<label class="mb-0">Search by</label>
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="type" id="search-all" value="all" checked>
<label class="form-check-label" for="search-all">All</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="type" id="search-name" value="name">
<label class="form-check-label" for="search-name">Name</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="type" id="search-ref" value="ref">
<label class="form-check-label" for="search-ref">Reference</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="type" id="search-uic-ref" value="uic_ref">
<label class="form-check-label" for="search-uic-ref">UIC reference</label>
</div>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="search" id="facility-term" name="term" class="form-control" placeholder="Bruxelles, EAHS, 8000294" minlength="2" required>
<div class="input-group-append">
<button class="btn btn-success" type="submit">Search</button>
</div>
</div>
</div>
</form>
<form id="search-milestones-form">
<div class="row">
<div class="col">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text" for="milestone-ref">Reference</label>
</div>
<input type="text" id="milestone-ref" name="ref" class="form-control" placeholder="6020" required>
</div>
</div>
</div>
<div class="col">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text" for="milestone-position">Position</label>
</div>
<input type="number" id="milestone-position" name="position" class="form-control" placeholder="22.7" min="0" step="0.1" required>
</div>
</div>
</div>
<div class="col flex-grow-0">
<button class="btn btn-success" type="submit">Search</button>
</div>
</div>
</form>
<div class="list-group" id="search-results"></div>
</div>
</div>
<div id="search-results"></div>
</div>
</div>
<div id="configuration-backdrop" class="modal backdrop" tabindex="-1" role="dialog">
Expand Down
111 changes: 88 additions & 23 deletions proxy/js/ui.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
const searchBackdrop = document.getElementById('search-backdrop');
const search = document.getElementById('search');
const searchFacilitiesTab = document.getElementById('search-facilities-tab');
const searchMilestonesTab = document.getElementById('search-milestones-tab');
const searchFacilitiesForm = document.getElementById('search-facilities-form');
const searchMilestonesForm = document.getElementById('search-milestones-form');
const searchFacilityTermField = document.getElementById('facility-term');
const searchMilestoneRefField = document.getElementById('milestone-ref');
const searchResults = document.getElementById('search-results');
const searchIcon = document.getElementById('search-icon');
const cancelIcon = document.getElementById('cancel-icon');
const configurationBackdrop = document.getElementById('configuration-backdrop');
const backgroundSaturationControl = document.getElementById('backgroundSaturation');
const backgroundOpacityControl = document.getElementById('backgroundOpacity');
const backgroundRasterUrlControl = document.getElementById('backgroundRasterUrl');
const legend = document.getElementById('legend')
const legendMapContainer = document.getElementById('legend-map')

function searchFor(term) {
if (!term || term.length < 3) {
function facilitySearchQuery(type, term) {
const encoded = encodeURIComponent(term)

switch (type) {
case 'name':
return `name=${encoded}`;
case 'ref':
return `ref=${encoded}`;
case 'uic_ref':
return `uic_ref=${encoded}`;
case 'all':
default:
return `q=${encoded}`;
}
}

function searchForFacilities(type, term) {
if (!term || term.length < 2) {
hideSearchResults();
} else {
// or ref=...
fetch(`${location.origin}/api/facility?name=${term}`)
const queryString = facilitySearchQuery(type, term)
fetch(`${location.origin}/api/facility?${queryString}`)
.then(result => result.json())
.then(result => {
console.info('result', result, result.body)
showSearchResults(result)
console.info('facility search result', result)
showSearchResults(result, item => item.name)
})
.catch(error => {
hideSearchResults();
Expand All @@ -29,37 +48,76 @@ function searchFor(term) {
}
}

function showSearchResults(results) {
function searchForMilestones(ref, position) {
if (!ref || !position) {
hideSearchResults();
} else {
fetch(`${location.origin}/api/milestone?ref=${encodeURIComponent(ref)}&position=${encodeURIComponent(position)}`)
.then(result => result.json())
.then(result => {
console.info('milestone search result', result)
showSearchResults(result, item => `Ref: ${item.ref}, KM: ${item.position}`)
})
.catch(error => {
hideSearchResults();
hideSearch();
console.error(error);
});
}
}

function showSearchResults(results, renderItem) {
let content = '';
if (results.length === 0) {
content += `<div class="result"><i>No results</i></div>`
content += `<div class="list-group-item no-results"><i>No results</i></div>`
} else {
results.forEach(result => {
content += `<div class="result" onclick="hideSearchResults(); map.easeTo({center: [${result.latitude}, ${result.longitude}], zoom: 15}); hideSearch()">${result.name}</div>`
content += `<a class="list-group-item list-group-item-action" href="javascript:hideSearchResults(); map.easeTo({center: [${result.latitude}, ${result.longitude}], zoom: 15}); hideSearch()">${renderItem(result)}</a>`
})
}
searchResults.innerHTML = content;
searchResults.style.display = 'block';
cancelIcon.style.display = 'block';
searchIcon.style.display = 'none';
}

function hideSearchResults() {
searchResults.style.display = 'none';
cancelIcon.style.display = 'none';
searchIcon.style.display = 'block';
}

function showSearch() {
searchBackdrop.style.display = 'block';
search.focus();
search.select();
if (searchFacilitiesForm.style.display === 'block') {
searchFacilityTermField.focus();
searchFacilityTermField.select();
} else if (searchMilestonesForm.style.display === 'block') {
searchMilestoneRefField.focus();
searchMilestoneRefField.select();
}
}

function hideSearch() {
searchBackdrop.style.display = 'none';
}

function searchFacilities() {
searchFacilitiesTab.classList.add('active')
searchMilestonesTab.classList.remove('active')
searchFacilitiesForm.style.display = 'block';
searchMilestonesForm.style.display = 'none';
searchFacilityTermField.focus();
searchFacilityTermField.select();
hideSearchResults();
}

function searchMilestones() {
searchFacilitiesTab.classList.remove('active')
searchMilestonesTab.classList.add('active')
searchFacilitiesForm.style.display = 'none';
searchMilestonesForm.style.display = 'block';
searchMilestoneRefField.focus();
searchMilestoneRefField.select();
hideSearchResults();
}

function showConfiguration() {
backgroundSaturationControl.value = configuration.backgroundSaturation ?? defaultConfiguration.backgroundSaturation;
backgroundOpacityControl.value = configuration.backgroundOpacity ?? defaultConfiguration.backgroundOpacity;
Expand All @@ -79,11 +137,18 @@ function toggleLegend() {
}
}

search.addEventListener('keydown', event => {
if (event.key === 'Enter') {
searchFor(event.target.value);
}
});
searchFacilitiesForm.addEventListener('submit', event => {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
searchForFacilities(data.type, data.term)
})
searchMilestonesForm.addEventListener('submit', event => {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
searchForMilestones(data.ref, data.position)
})
searchBackdrop.onclick = event => {
if (event.target === event.currentTarget) {
hideSearch();
Expand Down

0 comments on commit 6fd6f27

Please sign in to comment.