Add column filter/search to Table cell data view

Adds a search input at the top of the Table view that filters columns
by name with regex support.
This commit is contained in:
David Pivoňka
2025-12-08 14:31:38 +01:00
parent d220525ac7
commit 190c610466

View File

@@ -9,6 +9,9 @@
import EditCellDataModal from '../modals/EditCellDataModal.svelte'; import EditCellDataModal from '../modals/EditCellDataModal.svelte';
import ShowFormButton from '../formview/ShowFormButton.svelte'; import ShowFormButton from '../formview/ShowFormButton.svelte';
import { openJsonDocument } from '../tabs/JsonTab.svelte'; import { openJsonDocument } from '../tabs/JsonTab.svelte';
import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte';
import SearchInput from '../elements/SearchInput.svelte';
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
export let selection; export let selection;
@@ -20,6 +23,8 @@
$: realColumnUniqueNames = selection?.realColumnUniqueNames || []; $: realColumnUniqueNames = selection?.realColumnUniqueNames || [];
$: setCellValue = selection?.setCellValue; $: setCellValue = selection?.setCellValue;
let filter = '';
$: orderedFields = realColumnUniqueNames $: orderedFields = realColumnUniqueNames
.map(colName => { .map(colName => {
const col = columns.find(c => c.uniqueName === colName); const col = columns.find(c => c.uniqueName === colName);
@@ -34,6 +39,16 @@
}) })
.filter(Boolean); .filter(Boolean);
$: filteredFields = orderedFields.filter(field => {
if (!filter) return true;
try {
const regex = new RegExp(filter, 'i');
return regex.test(field.columnName);
} catch (e) {
return field.columnName.toLowerCase().includes(filter.toLowerCase());
}
});
let editingColumn = null; let editingColumn = null;
let editValue = ''; let editValue = '';
let domEditor = null; let domEditor = null;
@@ -102,12 +117,12 @@
} }
function moveToNextField(field, reverse) { function moveToNextField(field, reverse) {
const currentIndex = orderedFields.findIndex(f => f.uniqueName === field.uniqueName); const currentIndex = filteredFields.findIndex(f => f.uniqueName === field.uniqueName);
const nextIndex = reverse ? currentIndex - 1 : currentIndex + 1; const nextIndex = reverse ? currentIndex - 1 : currentIndex + 1;
if (nextIndex < 0 || nextIndex >= orderedFields.length) return; if (nextIndex < 0 || nextIndex >= filteredFields.length) return;
tick().then(() => { tick().then(() => {
const nextField = orderedFields[nextIndex]; const nextField = filteredFields[nextIndex];
if (isJsonValue(nextField.value)) { if (isJsonValue(nextField.value)) {
openEditModal(nextField); openEditModal(nextField);
} else { } else {
@@ -116,6 +131,14 @@
}); });
} }
function handleSearchKeyDown(e) {
if (e.keyCode === keycodes.backspace && (e.metaKey || e.ctrlKey)) {
filter = '';
e.stopPropagation();
e.preventDefault();
}
}
function handleBlur(field) { function handleBlur(field) {
if (isChangedRef.get()) { if (isChangedRef.get()) {
saveValue(field); saveValue(field);
@@ -152,11 +175,19 @@
</script> </script>
<div class="outer"> <div class="outer">
{#if rowData}
<div class="search-wrapper" on:keydown={handleSearchKeyDown}>
<SearchBoxWrapper noMargin>
<SearchInput placeholder="Filter columns (regex)" bind:value={filter} />
<CloseSearchButton bind:filter />
</SearchBoxWrapper>
</div>
{/if}
<div class="inner"> <div class="inner">
{#if !rowData} {#if !rowData}
<div class="no-data">No data selected</div> <div class="no-data">No data selected</div>
{:else} {:else}
{#each orderedFields as field (field.uniqueName)} {#each filteredFields as field (field.uniqueName)}
<div class="field"> <div class="field">
<div class="field-name">{field.columnName}</div> <div class="field-name">{field.columnName}</div>
<div <div
@@ -210,15 +241,17 @@
.outer { .outer {
flex: 1; flex: 1;
position: relative; position: relative;
display: flex;
flex-direction: column;
}
.search-wrapper {
padding: 4px 4px 0 4px;
} }
.inner { .inner {
overflow: auto; overflow: auto;
position: absolute; flex: 1;
left: 0;
top: 0;
right: 0;
bottom: 0;
padding: 4px; padding: 4px;
} }