src/views/SearchExpressions.vue
<template>
<div>
<h1>Search Ideas</h1>
<div class="form-check">
<input
id="regexSearch"
v-model="regexSearch"
class="form-check-input"
type="checkbox"
value=""
>
<label
class="form-check-label"
for="regexSearch"
>
Regex search
</label>
</div>
<table
id="table"
class="table table-hover"
>
<thead />
</table>
</div>
</template>
<script lang="ts" setup>
import DataTable, {type Api as DataTablesApi} from 'datatables.net-bs5';
import {ref, watch} from 'vue';
import {KnownUnknown} from '../../server/model/ideas/expression';
import {removeContext} from '../../server/utils/expressionStringUtils';
import * as Api from '../ts/api';
let dt: DataTablesApi;
const regexSearch = ref(false);
const columns = [
{data: 'ideaId', title: 'Idea ID'},
{data: 'languageName', title: 'Language'},
{data: 'text', title: 'Text'},
{data: 'textWithoutContext', title: 'Text (without context)'},
{data: 'known', title: 'Known'},
];
const searchTds: HTMLTableCellElement[] = [];
let searchTr: HTMLTableRowElement;
let searchResults: Array<{ideaId: number; languageName: string; text: string; known: string}>;
let languageNames: string[];
(async () => {
searchResults = await Api.getExpressions();
languageNames = (await Api.getLanguages()).map(language => language.name);
await initialize();
})();
function getSearchResults() {
return searchResults.map(({ideaId, languageName, text, known}) => ({
ideaId,
languageName,
text,
textWithoutContext: removeContext(text).trim(),
known,
}));
}
async function initialize() {
dt = new DataTable('#table', {
data: getSearchResults(),
columns,
pageLength: 100,
async initComplete() {
const thead = document.querySelector('#table thead')!;
searchTr = document.createElement('tr');
thead.appendChild(searchTr);
for (let i = 0; i < columns.length; i++) {
searchTds.push(document.createElement('td'));
searchTr.appendChild(searchTds[i]);
}
const languageSelect = createSearchSelect('searchLanguages', 1, languageNames);
const textSearch = createSearchInput('searchText', 2);
const textWithoutContextSearch = createSearchInput('searchTextWithoutContext', 3);
const searchKnown = createSearchSelect('searchKnown', 4, Object.values(KnownUnknown));
searchTds[1].appendChild(languageSelect);
searchTds[2].appendChild(textSearch);
searchTds[3].appendChild(textWithoutContextSearch);
searchTds[4].appendChild(searchKnown);
document.querySelector('.dt-search')!.remove();
},
});
initOpenIdeaOnClick();
}
function initOpenIdeaOnClick() {
dt.on('click', (e: Event) => {
const mouseEvent = e as MouseEvent;
const newTab = mouseEvent.ctrlKey || mouseEvent.button === 1;
const target = e.target as HTMLTableCellElement;
if (target instanceof HTMLTableCellElement) {
const rowData = dt.row(target).data() as {ideaId: number};
window.open(`/ideas/${rowData.ideaId}`, newTab ? '_blank' : '_self');
}
});
}
function createSearchInput(id: string, columnIndex: number) {
const input = document.createElement('input');
input.classList.add('form-control', 'form-control-sm');
input.id = id;
input.type = 'text';
input.placeholder = 'Search';
input.oninput = () => {
dt.column(columnIndex).search(input.value, {regex: regexSearch.value}).draw();
};
return input;
}
watch(regexSearch, () => {
searchTr.querySelectorAll('input').forEach(input => input.dispatchEvent(new Event('input')));
});
function createSearchSelect(id: string, columnIndex: number, options: string[]) {
const select = document.createElement('select');
select.classList.add('form-select', 'form-select-sm');
select.id = id;
select.innerHTML = '<option value=""></option>';
options.forEach(option => select.appendChild(createOption(option, option)));
select.onchange = () => dt.column(columnIndex).search(select.value, {exact: true}).draw();
adjustSelectWidth(select, searchTds[columnIndex]);
return select;
}
function createOption(value: string, text: string) {
const optionElement = document.createElement('option');
optionElement.value = value;
optionElement.textContent = text;
return optionElement;
}
function adjustSelectWidth(select: HTMLSelectElement, parentElement = document.body) {
const tempOption = document.createElement('option');
parentElement.appendChild(tempOption);
let maxWidth = 0;
select.querySelectorAll('option').forEach(option => {
tempOption.textContent = option.textContent;
const width = tempOption.offsetWidth;
maxWidth = Math.max(maxWidth, width);
});
select.style.width = `${maxWidth + 40}px`;
parentElement.removeChild(tempOption);
}
</script>
<script type="module" lang="ts">
import 'datatables.net-bs5/css/dataTables.bootstrap5.min.css';
</script>
<style>
table.dataTable td.dt-type-numeric, table.dataTable th.dt-type-numeric {
text-align: left;
}
table {
width: 100%!important;
cursor: pointer;
}
</style>