Merge pull request #609 from appwrite/feat-generic-filters

Refactor filters to be generic
This commit is contained in:
Torsten Dittmann
2023-11-06 12:24:57 +01:00
committed by GitHub
15 changed files with 84 additions and 72 deletions
@@ -4,8 +4,11 @@
import InputText from '$lib/elements/forms/inputText.svelte';
import { Query } from '@appwrite.io/console';
import { createEventDispatcher } from 'svelte';
import { columns } from '../store';
import { tags, type Column, type Operator, queries } from './store';
import { tags, type Operator, queries } from './store';
import type { Column } from '$lib/helpers/types';
import type { Writable } from 'svelte/store';
export let columns: Writable<Column[]>;
const dispatch = createEventDispatcher<{
clear: void;
@@ -31,22 +34,22 @@
'greater than': {
toQuery: (attr, input) => Query.greaterThan(attr, Number(input)),
toTag: (attribute, input) => `**${attribute}** greater than **${input}**`,
types: ['integer', 'double']
types: ['integer', 'double', 'datetime']
},
'greater than or equal to': {
toQuery: (attr, input) => Query.greaterThanEqual(attr, Number(input)),
toTag: (attribute, input) => `**${attribute}** greater than or equal to **${input}**`,
types: ['integer', 'double']
types: ['integer', 'double', 'datetime']
},
'less than': {
toQuery: Query.lessThan,
toTag: (attribute, input) => `**${attribute}** less than **${input}**`,
types: ['integer', 'double']
types: ['integer', 'double', 'datetime']
},
'less than or equal to': {
toQuery: Query.lessThanEqual,
toTag: (attribute, input) => `**${attribute}** less than or equal to **${input}**`,
types: ['integer', 'double']
types: ['integer', 'double', 'datetime']
},
equal: {
toQuery: Query.equal,
@@ -97,19 +100,17 @@
// This Map is keyed by tags, and has a query as the value
function addFilter() {
if (column && operator) {
queries.addFilter({ column, operator, value });
value = null;
}
if (!column || !operator) return;
queries.addFilter({ column, operator, value: value ?? '' });
value = null;
}
function tagFormat(node: HTMLElement) {
node.innerHTML = node.innerHTML.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
}
$: isDisabled = (function getDisabled() {
return !operator || (!operator?.hideInput && !value);
})();
$: isDisabled = !operator;
</script>
<div>
@@ -134,6 +135,16 @@
<div class="u-margin-block-start-8">
{#if column.type === 'integer' || column.type === 'double'}
<InputNumber id="value" bind:value placeholder="Enter value" />
{:else if column.type === 'boolean'}
<InputSelect
id="value"
placeholder="Select a value"
required={true}
options={[
{ label: 'True', value: true },
{ label: 'False', value: false }
].filter(Boolean)}
bind:value />
{:else}
<InputText id="value" bind:value placeholder="Enter value" />
{/if}
@@ -2,8 +2,16 @@
import { beforeNavigate } from '$app/navigation';
import { Drop, Modal } from '$lib/components';
import { Button } from '$lib/elements/forms';
import type { Column } from '$lib/helpers/types';
import type { Writable } from 'svelte/store';
import Content from './content.svelte';
import { queries, queriesAreDirty, tags } from './store';
import { queries, queriesAreDirty, queryParamToMap, tags } from './store';
export let query = '[]';
export let columns: Writable<Column[]>;
const parsedQueries = queryParamToMap(query);
queries.set(parsedQueries);
// We need to separate them so we don't trigger Drop's handlers
let showFiltersDesktop = false;
@@ -33,6 +41,7 @@
<div class="dropped card">
<p>Apply filter rules to refine the table view</p>
<Content
{columns}
on:apply={(e) => (applied = e.detail.applied)}
on:clear={() => (applied = 0)} />
<hr />
@@ -61,7 +70,10 @@
description="Apply filter rules to refine the table view"
bind:show={showFiltersMobile}
size="big">
<Content on:apply={(e) => (applied = e.detail.applied)} on:clear={() => (applied = 0)} />
<Content
{columns}
on:apply={(e) => (applied = e.detail.applied)}
on:clear={() => (applied = 0)} />
<svelte:fragment slot="footer">
<Button text on:click={queries.clearAll}>Clear all</Button>
<Button on:click={queries.apply} disabled={!$queriesAreDirty}>Apply</Button
+1
View File
@@ -0,0 +1 @@
export { default as filters } from './filters.svelte';
@@ -1,17 +1,9 @@
import { goto } from '$app/navigation';
import { derived, get, writable, type Writable } from 'svelte/store';
import type { columns } from '../store';
import { derived, get, writable } from 'svelte/store';
import { page } from '$app/stores';
import deepEqual from 'deep-equal';
const columnTypes = ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'] as const;
type ColumnType = (typeof columnTypes)[number];
type StoreValues<Store> = Store extends Writable<infer T> ? T : never;
export type Column = Omit<StoreValues<typeof columns>[number], 'type'> & {
type: ColumnType;
};
import type { Column, ColumnType } from '$lib/helpers/types';
export type Operator = {
toTag: (attribute: string, input?: string | number) => string;
+1 -9
View File
@@ -1,12 +1,3 @@
<script context="module" lang="ts">
export type Column = {
id: string;
title: string;
show: boolean;
width?: number;
};
</script>
<script lang="ts">
import { Button, InputChoice } from '$lib/elements/forms';
import { DropList } from '.';
@@ -16,6 +7,7 @@
import { onMount } from 'svelte';
import { View } from '$lib/helpers/load';
import { tooltip } from '$lib/actions/tooltip';
import type { Column } from '$lib/helpers/types';
export let columns: Writable<Column[]>;
export let view: View;
+4
View File
@@ -32,6 +32,10 @@ export function getSearch(url: URL): string | undefined {
return url.searchParams.get('search') ?? undefined;
}
export function getQuery(url: URL): string | undefined {
return url.searchParams.get('query') ?? undefined;
}
type TabElement = { href: string; title: string; hasChildren?: boolean };
export function isTabSelected(
+10
View File
@@ -12,3 +12,13 @@ export function isHTMLInputElement(el: unknown): el is HTMLInputElement {
// eslint-disable-next-line @typescript-eslint/ban-types
export type Prettify<T> = T & {};
const columnTypes = ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'] as const;
export type ColumnType = (typeof columnTypes)[number];
export type Column = {
id: string;
title: string;
type: ColumnType;
show: boolean;
width?: number;
};
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { Heading, ViewSelector } from '$lib/components';
import type { View } from '$lib/helpers/load';
import type { Column } from '$lib/components/viewSelector.svelte';
import type { Column } from '$lib/helpers/types';
import type { Writable } from 'svelte/store';
export let title: string;
@@ -1,15 +1,9 @@
import type { Column } from '$lib/helpers/types';
import { writable } from 'svelte/store';
export type Column = {
id: string;
title: string;
show: boolean;
width?: number;
};
export const columns = writable<Column[]>([
{ id: '$id', title: 'Database ID', show: true, width: 150 },
{ id: 'name', title: 'Name', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', show: true, width: 120 }
{ id: '$id', title: 'Database ID', type: 'string', show: true, width: 150 },
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', type: 'string', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', type: 'string', show: true, width: 120 }
]);
@@ -1,14 +1,15 @@
<script lang="ts">
import { page } from '$app/stores';
import { Empty, EmptySearch, Heading, PaginationWithLimit } from '$lib/components';
import Filters from '$lib/components/filters/filters.svelte';
import { hasPageQueries, queries } from '$lib/components/filters/store';
import ViewSelector from '$lib/components/viewSelector.svelte';
import { Button } from '$lib/elements/forms';
import type { ColumnType } from '$lib/helpers/types';
import { Container } from '$lib/layout';
import { preferences } from '$lib/stores/preferences';
import { wizard } from '$lib/stores/wizard';
import type { PageData } from './$types';
import Filters from './(filters)/filters.svelte';
import { hasPageQueries, queries } from './(filters)/store';
import CreateAttributeDropdown from './attributes/createAttributeDropdown.svelte';
import type { Option } from './attributes/store';
import CreateAttribute from './createAttribute.svelte';
@@ -27,7 +28,7 @@
$collection.attributes.map((attribute) => ({
id: attribute.key,
title: attribute.key,
type: attribute.type,
type: attribute.type as ColumnType,
show: selected?.includes(attribute.key) ?? true
}))
);
@@ -54,7 +55,7 @@
</Button>
</div>
<Filters />
<Filters query={data.query} {columns} />
<div class="u-flex u-main-end u-gap-16">
<ViewSelector
@@ -1,10 +1,9 @@
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
import { getLimit, getPage, getView, pageToOffset, View } from '$lib/helpers/load';
import { getLimit, getPage, getQuery, getView, pageToOffset, View } from '$lib/helpers/load';
import { sdk } from '$lib/stores/sdk';
import { Query } from '@appwrite.io/console';
import type { PageLoad } from './$types';
import { queries, queryParamToMap } from './(filters)/store';
import { queries, queryParamToMap } from '$lib/components/filters/store';
export const load: PageLoad = async ({ params, depends, url, route }) => {
depends(Dependencies.DOCUMENTS);
@@ -12,6 +11,7 @@ export const load: PageLoad = async ({ params, depends, url, route }) => {
const limit = getLimit(url, route, PAGE_LIMIT);
const view = getView(url, route, View.Grid);
const offset = pageToOffset(page, limit);
const query = getQuery(url);
const paramQueries = url.searchParams.get('query');
const parsedQueries = queryParamToMap(paramQueries || '[]');
@@ -21,6 +21,7 @@ export const load: PageLoad = async ({ params, depends, url, route }) => {
offset,
limit,
view,
query,
documents: await sdk.forProject.databases.listDocuments(
params.database,
params.collection,
@@ -1,4 +1,5 @@
import { page } from '$app/stores';
import type { Column } from '$lib/helpers/types';
import type { Models } from '@appwrite.io/console';
import { derived, writable } from 'svelte/store';
@@ -16,14 +17,6 @@ export type Attributes =
type Collection = Omit<Models.Collection, 'attributes'> & {
attributes: Array<Attributes>;
};
export type Column = {
id: string;
title: string;
show: boolean;
type?: string;
twoWay?: string;
width?: number;
};
export const collection = derived(page, ($page) => $page.data.collection as Collection);
export const attributes = derived(
@@ -29,6 +29,7 @@
import RelationshipsModal from './relationshipsModal.svelte';
import { attributes, collection, columns } from './store';
import { clickOnEnter } from '$lib/helpers/a11y';
import type { ColumnType } from '$lib/helpers/types';
export let data: PageData;
@@ -89,7 +90,7 @@
$collection.attributes.map((attribute) => ({
id: attribute.key,
title: attribute.key,
type: attribute.type,
type: attribute.type as ColumnType,
show: selected?.includes(attribute.key) ?? true
}))
);
@@ -1,5 +1,5 @@
import { page } from '$app/stores';
import type { Column } from '$lib/components/viewSelector.svelte';
import type { Column } from '$lib/helpers/types';
import type { Models } from '@appwrite.io/console';
import { derived, writable } from 'svelte/store';
@@ -7,8 +7,8 @@ export const database = derived(page, ($page) => $page.data.database as Models.D
export const showCreate = writable(false);
export const columns = writable<Column[]>([
{ id: '$id', title: 'Collection ID', show: true, width: 150 },
{ id: 'name', title: 'Name', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', show: true, width: 120 }
{ id: '$id', title: 'Collection ID', type: 'string', show: true, width: 150 },
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 120 }
]);
@@ -1,9 +1,9 @@
import type { Column } from '$lib/components/viewSelector.svelte';
import type { Column } from '$lib/helpers/types';
import { writable } from 'svelte/store';
export const columns = writable<Column[]>([
{ id: '$id', title: 'Database ID', show: true, width: 150 },
{ id: 'name', title: 'Name', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', show: true, width: 120 }
{ id: '$id', title: 'Database ID', type: 'string', show: true, width: 150 },
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 120 }
]);