2021-04-20 12:45:30 -07:00
|
|
|
import debounce from 'just-debounce-it';
|
2021-05-07 11:07:06 -07:00
|
|
|
import { getElements, getRowValues, findFirstAdjacent, isTruthy } from './util';
|
2021-04-17 18:16:13 -07:00
|
|
|
|
2021-05-07 11:07:06 -07:00
|
|
|
/**
|
|
|
|
* Change the display value and hidden input values of the search filter based on dropdown
|
|
|
|
* selection.
|
|
|
|
*
|
|
|
|
* @param event "click" event for each dropdown item.
|
|
|
|
* @param button Each dropdown item element.
|
|
|
|
*/
|
2021-08-23 22:31:36 -07:00
|
|
|
function handleSearchDropdownClick(event: Event, button: HTMLButtonElement): void {
|
2021-05-07 11:07:06 -07:00
|
|
|
const dropdown = event.currentTarget as HTMLButtonElement;
|
|
|
|
const selectedValue = findFirstAdjacent<HTMLSpanElement>(dropdown, 'span.search-obj-selected');
|
|
|
|
const selectedType = findFirstAdjacent<HTMLInputElement>(dropdown, 'input.search-obj-type');
|
|
|
|
const searchValue = dropdown.getAttribute('data-search-value');
|
|
|
|
let selected = '' as string;
|
2021-03-13 02:31:57 -07:00
|
|
|
|
2021-05-07 11:07:06 -07:00
|
|
|
if (selectedValue !== null && selectedType !== null) {
|
|
|
|
if (isTruthy(searchValue) && selected !== searchValue) {
|
|
|
|
selected = searchValue;
|
|
|
|
selectedValue.innerHTML = button.textContent ?? 'Error';
|
|
|
|
selectedType.value = searchValue;
|
|
|
|
} else {
|
|
|
|
selected = '';
|
2021-05-07 16:21:31 -07:00
|
|
|
selectedValue.innerHTML = 'All Objects';
|
2021-05-07 11:07:06 -07:00
|
|
|
selectedType.value = '';
|
|
|
|
}
|
|
|
|
}
|
2021-03-13 02:31:57 -07:00
|
|
|
}
|
|
|
|
|
2021-05-07 11:07:06 -07:00
|
|
|
/**
|
|
|
|
* Initialize Search Bar Elements.
|
|
|
|
*/
|
2021-08-23 22:31:36 -07:00
|
|
|
function initSearchBar(): void {
|
2021-07-30 00:35:38 -07:00
|
|
|
for (const dropdown of getElements<HTMLUListElement>('.search-obj-selector')) {
|
2021-05-07 11:07:06 -07:00
|
|
|
for (const button of dropdown.querySelectorAll<HTMLButtonElement>(
|
|
|
|
'li > button.dropdown-item',
|
|
|
|
)) {
|
|
|
|
button.addEventListener('click', event => handleSearchDropdownClick(event, button));
|
2021-03-13 02:31:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-17 18:16:13 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize Interface Table Filter Elements.
|
|
|
|
*/
|
2021-08-23 22:31:36 -07:00
|
|
|
function initInterfaceFilter(): void {
|
2021-04-20 12:45:30 -07:00
|
|
|
for (const input of getElements<HTMLInputElement>('input.interface-filter')) {
|
|
|
|
const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
|
|
|
|
const rows = Array.from(
|
|
|
|
table?.querySelectorAll<HTMLTableRowElement>('tbody > tr') ?? [],
|
|
|
|
).filter(r => r !== null);
|
2021-04-17 18:16:13 -07:00
|
|
|
/**
|
|
|
|
* Filter on-page table by input text.
|
|
|
|
*/
|
2021-08-23 22:31:36 -07:00
|
|
|
function handleInput(event: Event): void {
|
2021-04-17 18:16:13 -07:00
|
|
|
const target = event.target as HTMLInputElement;
|
|
|
|
// Create a regex pattern from the input search text to match against.
|
2021-04-20 12:45:30 -07:00
|
|
|
const filter = new RegExp(target.value.toLowerCase().trim());
|
2021-04-17 18:16:13 -07:00
|
|
|
|
|
|
|
// Each row represents an interface and its attributes.
|
2021-04-20 12:45:30 -07:00
|
|
|
for (const row of rows) {
|
2021-04-17 18:16:13 -07:00
|
|
|
// Find the row's checkbox and deselect it, so that it is not accidentally included in form
|
|
|
|
// submissions.
|
|
|
|
const checkBox = row.querySelector<HTMLInputElement>('input[type="checkbox"][name="pk"]');
|
|
|
|
if (checkBox !== null) {
|
|
|
|
checkBox.checked = false;
|
|
|
|
}
|
|
|
|
|
2021-04-20 12:45:30 -07:00
|
|
|
// The data-name attribute's value contains the interface name.
|
|
|
|
const name = row.getAttribute('data-name');
|
|
|
|
|
2021-04-17 18:16:13 -07:00
|
|
|
if (typeof name === 'string') {
|
2021-04-20 12:45:30 -07:00
|
|
|
if (filter.test(name.toLowerCase().trim())) {
|
2021-04-17 18:16:13 -07:00
|
|
|
// If this row matches the search pattern, but is already hidden, unhide it.
|
|
|
|
if (row.classList.contains('d-none')) {
|
|
|
|
row.classList.remove('d-none');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If this row doesn't match the search pattern, hide it.
|
|
|
|
row.classList.add('d-none');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-20 12:45:30 -07:00
|
|
|
input.addEventListener('keyup', debounce(handleInput, 300));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-23 22:31:36 -07:00
|
|
|
function initTableFilter(): void {
|
2021-04-20 12:45:30 -07:00
|
|
|
for (const input of getElements<HTMLInputElement>('input.object-filter')) {
|
|
|
|
// Find the first adjacent table element.
|
|
|
|
const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
|
|
|
|
|
|
|
|
// Build a valid array of <tr/> elements that are children of the adjacent table.
|
|
|
|
const rows = Array.from(
|
|
|
|
table?.querySelectorAll<HTMLTableRowElement>('tbody > tr') ?? [],
|
|
|
|
).filter(r => r !== null);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filter table rows by matched input text.
|
|
|
|
* @param event
|
|
|
|
*/
|
2021-08-23 22:31:36 -07:00
|
|
|
function handleInput(event: Event): void {
|
2021-04-20 12:45:30 -07:00
|
|
|
const target = event.target as HTMLInputElement;
|
|
|
|
|
|
|
|
// Create a regex pattern from the input search text to match against.
|
|
|
|
const filter = new RegExp(target.value.toLowerCase().trim());
|
|
|
|
|
2021-11-25 12:14:07 +01:00
|
|
|
// List of which rows which match the query
|
|
|
|
const matchedRows: Array<HTMLTableRowElement> = [];
|
|
|
|
|
2021-04-20 12:45:30 -07:00
|
|
|
for (const row of rows) {
|
|
|
|
// Find the row's checkbox and deselect it, so that it is not accidentally included in form
|
|
|
|
// submissions.
|
|
|
|
const checkBox = row.querySelector<HTMLInputElement>('input[type="checkbox"][name="pk"]');
|
|
|
|
if (checkBox !== null) {
|
|
|
|
checkBox.checked = false;
|
|
|
|
}
|
2021-11-25 12:14:07 +01:00
|
|
|
|
2021-04-20 12:45:30 -07:00
|
|
|
// Iterate through each row's cell values
|
|
|
|
for (const value of getRowValues(row)) {
|
|
|
|
if (filter.test(value.toLowerCase())) {
|
2021-11-25 12:14:07 +01:00
|
|
|
// If this row matches the search pattern, add it to the list.
|
|
|
|
matchedRows.push(row);
|
2021-04-20 12:45:30 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-25 12:14:07 +01:00
|
|
|
|
|
|
|
// Iterate the rows again to set visibility.
|
|
|
|
// This results in a single reflow instead of one for each row.
|
|
|
|
for (const row of rows) {
|
|
|
|
if (matchedRows.indexOf(row) >= 0) {
|
|
|
|
row.classList.remove('d-none');
|
|
|
|
} else {
|
|
|
|
row.classList.add('d-none');
|
|
|
|
}
|
|
|
|
}
|
2021-04-20 12:45:30 -07:00
|
|
|
}
|
|
|
|
input.addEventListener('keyup', debounce(handleInput, 300));
|
2021-04-17 18:16:13 -07:00
|
|
|
}
|
|
|
|
}
|
2021-04-20 09:34:12 -07:00
|
|
|
|
2021-08-23 22:31:36 -07:00
|
|
|
export function initSearch(): void {
|
2021-04-20 12:45:30 -07:00
|
|
|
for (const func of [initSearchBar, initTableFilter, initInterfaceFilter]) {
|
2021-04-20 09:34:12 -07:00
|
|
|
func();
|
|
|
|
}
|
|
|
|
}
|