feat: combo-box on relationship searching.

This commit is contained in:
Darshan
2025-09-11 20:03:22 +05:30
parent 259dd91f16
commit e518cf749d
3 changed files with 76 additions and 15 deletions
+2 -2
View File
@@ -24,9 +24,9 @@
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0fefd92",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0fefd92",
"@faker-js/faker": "^9.9.0",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
+10 -10
View File
@@ -18,14 +18,14 @@ importers:
specifier: 0.25.0
version: 0.25.0
'@appwrite.io/pink-icons-svelte':
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0fefd92
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0fefd92(svelte@5.25.3)
'@appwrite.io/pink-legacy':
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0fefd92
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0fefd92(svelte@5.25.3)
'@faker-js/faker':
specifier: ^9.9.0
version: 9.9.0
@@ -269,8 +269,8 @@ packages:
peerDependencies:
svelte: ^4.0.0
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0}
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0fefd92':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0fefd92}
version: 2.0.0-RC.1
peerDependencies:
svelte: ^4.0.0
@@ -284,8 +284,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.3':
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0fefd92':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0fefd92}
version: 2.0.0-RC.2
peerDependencies:
svelte: ^4.0.0
@@ -3706,7 +3706,7 @@ snapshots:
dependencies:
svelte: 5.25.3
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0(svelte@5.25.3)':
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0fefd92(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
@@ -3719,7 +3719,7 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0fefd92(svelte@5.25.3)':
dependencies:
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
'@floating-ui/dom': 1.6.13
@@ -7,7 +7,8 @@
import { type ComponentType, onMount } from 'svelte';
import { isRelationshipToMany } from '../../store';
import { IconPlus, IconRelationship, IconX } from '@appwrite.io/pink-icons-svelte';
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Input, Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { debounce } from '$lib/helpers/debounce';
type SelectOption = {
label: string;
@@ -42,6 +43,8 @@
let limit = 10;
let offset = 0;
let searchNoResultsOption = undefined;
onMount(async () => {
if (isRelationshipToMany(column)) {
if (Array.isArray(value)) {
@@ -80,7 +83,22 @@
return rows;
}
function getAvailableOptions(excludeIndex?: number) {
async function searchRows(search: string = null) {
if (!search) return;
const displayNames = preferences.getDisplayNames(column.relatedTable);
const queries = [];
displayNames.forEach((name) => queries.push(Query.contains(name, search)));
return await sdk.forProject(page.params.region, page.params.project).tablesDB.listRows({
databaseId,
tableId: column.relatedTable,
queries
});
}
function getAvailableOptions(excludeIndex?: number): SelectOption[] {
return options?.filter((option) => {
const otherItems =
excludeIndex !== undefined
@@ -166,6 +184,44 @@
return baseOptions;
}
// original list
let originalListPreSearch: Models.RowList<Models.Row>;
const debouncedSearch = debounce(async () => {
const availableOptions = getAvailableOptions();
const includesItem = availableOptions
.filter((opt) => {
const label = String(opt.label ?? '').toLowerCase();
const value = String(opt.value ?? '').toLowerCase();
return (
label.includes(newItemValue.toLowerCase()) ||
value.includes(newItemValue.toLowerCase())
);
})
.filter(Boolean);
if (!includesItem.length) {
originalListPreSearch = rowList;
try {
searchNoResultsOption = {
disabled: true,
message: 'Searching...'
};
const searchResults = await searchRows(newItemValue);
if (searchResults) {
rowList = searchResults;
}
} finally {
searchNoResultsOption = undefined;
}
} else {
// reset
rowList = originalListPreSearch;
}
}, 500);
// Reactive statements
$: getRows(search).then((res) => (rowList = res));
@@ -193,6 +249,10 @@
$: if (limit && !isRelationshipToMany(column) && limited) {
label = undefined;
}
$: if (newItemValue && newItemValue !== '') {
debouncedSearch();
}
</script>
{#if isRelationshipToMany(column)}
@@ -298,13 +358,14 @@
<!-- Input for adding new items -->
{#if showInput}
<Layout.Stack direction="row">
<InputSelect
<Input.ComboBox
{id}
required
placeholder={`Select ${column.key}`}
bind:value={newItemValue}
options={getAvailableOptions()}
on:change={addNewItem}
noResultsOption={searchNoResultsOption}
leadingIcon={!limited ? IconRelationship : undefined} />
<div style:padding-block-start="0.5rem">