mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
use new table virtualization
This commit updates the pink-svelte and pink-icons-svelte dependencies to new versions and refactors the collection table to use the new virtualized table component for improved performance.
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@efb9310",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
|
||||
Generated
+25
-10
@@ -24,8 +24,8 @@ importers:
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@efb9310
|
||||
version: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@efb9310(svelte@5.25.3)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -260,9 +260,8 @@ packages:
|
||||
'@appwrite.io/console@1.9.0':
|
||||
resolution: {integrity: sha512-g8+zfdBF8mz7tRUER4CGVe5FHWQVLp8TlY/UOGCZ2TgUF1qnpNu/DhiQgph8uF+QZ9jDKCLAAZlP5//9t7ckWA==}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870}
|
||||
version: 2.0.0-RC.1
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
|
||||
resolution: {integrity: sha512-iLFlV55hj8mGuAbmxJGenxN5RaZMmVT4GJb9dv/MP1xBAtYibFq7JvBcxm18qV2KU8c31Rntf+Ub4GL7HwqTYg==}
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
@@ -281,8 +280,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.3':
|
||||
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778}
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@efb9310':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@efb9310}
|
||||
version: 2.0.0-RC.2
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -1265,6 +1264,14 @@ packages:
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
'@tanstack/svelte-virtual@3.13.10':
|
||||
resolution: {integrity: sha512-MVpi/yJpliu6ab7ScrYWk3VYhRh2gwyXZxEV12G5TYZqu/hNEqvw6GUTYBcAg6xzRq4VKtxt89rQSqlGyG4OMA==}
|
||||
peerDependencies:
|
||||
svelte: ^3.48.0 || ^4.0.0 || ^5.0.0
|
||||
|
||||
'@tanstack/virtual-core@3.13.10':
|
||||
resolution: {integrity: sha512-sPEDhXREou5HyZYqSWIqdU580rsF6FGeN7vpzijmP3KTiOGjOMZASz4Y6+QKjiFQwhWrR58OP8izYaNGVxvViA==}
|
||||
|
||||
'@testing-library/dom@10.4.0':
|
||||
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3637,7 +3644,7 @@ snapshots:
|
||||
|
||||
'@appwrite.io/console@1.9.0': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
@@ -3654,12 +3661,13 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@efb9310(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3)
|
||||
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3)
|
||||
'@melt-ui/svelte': 0.86.6(svelte@5.25.3)
|
||||
'@tanstack/svelte-virtual': 3.13.10(svelte@5.25.3)
|
||||
ansicolor: 2.0.3
|
||||
d3: 7.9.0
|
||||
fuse.js: 7.1.0
|
||||
@@ -4710,6 +4718,13 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tanstack/svelte-virtual@3.13.10(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.13.10
|
||||
svelte: 5.25.3
|
||||
|
||||
'@tanstack/virtual-core@3.13.10': {}
|
||||
|
||||
'@testing-library/dom@10.4.0':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.26.2
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
} = $props();
|
||||
|
||||
let maxHeight = $state('none');
|
||||
let containerRef;
|
||||
let containerRef: HTMLDivElement = $state();
|
||||
|
||||
const calcMaxHeight = () => {
|
||||
if (containerRef) {
|
||||
@@ -67,7 +67,7 @@
|
||||
calcMaxHeight();
|
||||
});
|
||||
|
||||
let selectedColumnsNumber = $derived(
|
||||
const selectedColumnsNumber = $derived(
|
||||
$columns.reduce((acc, column) => {
|
||||
if (column.hide === true) return acc;
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ function createPreferences() {
|
||||
);
|
||||
},
|
||||
getCustomCollectionColumns: (collectionId: string): Preferences['columns'] => {
|
||||
return preferences?.collections?.[collectionId] ?? [];
|
||||
return preferences?.collections?.[collectionId] ?? null;
|
||||
},
|
||||
setLimit: (limit: Preferences['limit']) =>
|
||||
updateAndSync((n) => {
|
||||
|
||||
+11
-18
@@ -14,7 +14,6 @@
|
||||
import CreateAttribute from './createAttribute.svelte';
|
||||
import { collection, columns, isCsvImportInProgress } from './store';
|
||||
import Table from './table.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import FilePicker from '$lib/components/filePicker.svelte';
|
||||
import { page } from '$app/state';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -32,28 +31,22 @@
|
||||
let showCreateAttribute = false;
|
||||
let selectedAttribute: Option['name'] = null;
|
||||
|
||||
const filterColumns = writable<Column[]>([]);
|
||||
|
||||
$: selected = preferences.getCustomCollectionColumns(page.params.collection);
|
||||
$: columns.set(
|
||||
$collection.attributes.map((attribute) => ({
|
||||
|
||||
$: columns.set([
|
||||
{ id: '$id', width: 200, title: 'Document ID', type: 'string' },
|
||||
...$collection.attributes.map<Column>((attribute) => ({
|
||||
id: attribute.key,
|
||||
title: attribute.key,
|
||||
type: attribute.type as ColumnType,
|
||||
show: selected?.includes(attribute.key) ?? true,
|
||||
hide: !(selected?.includes(attribute.key) ?? true),
|
||||
array: attribute?.array,
|
||||
width: { min: 168 },
|
||||
format: 'format' in attribute && attribute?.format === 'enum' ? attribute.format : null,
|
||||
elements: 'elements' in attribute ? attribute.elements : null
|
||||
}))
|
||||
);
|
||||
$: filterColumns.set([
|
||||
...$columns,
|
||||
...['$id', '$createdAt', '$updatedAt'].map((id) => ({
|
||||
id,
|
||||
title: id,
|
||||
show: true,
|
||||
type: (id === '$id' ? 'string' : 'datetime') as ColumnType
|
||||
}))
|
||||
})),
|
||||
{ id: '$created', width: 200, title: 'Created', type: 'datetime' },
|
||||
{ id: '$updated', width: 200, title: 'Updated', type: 'datetime' }
|
||||
]);
|
||||
|
||||
$: hasAttributes = !!$collection.attributes.length;
|
||||
@@ -99,7 +92,7 @@
|
||||
disabled={!(hasAttributes && hasValidAttributes)}
|
||||
analyticsSource="database_documents" />
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<ViewSelector view={data.view} {columns} hideView />
|
||||
<ViewSelector view={data.view} {columns} hideView isCustomCollection />
|
||||
{#if flags.showCsvImport(data)}
|
||||
<Button
|
||||
secondary
|
||||
@@ -133,7 +126,7 @@
|
||||
|
||||
{#if hasAttributes && hasValidAttributes}
|
||||
{#if data.documents.total}
|
||||
<Table {data} />
|
||||
<Table {data} {columns} />
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Documents"
|
||||
|
||||
+43
-82
@@ -10,7 +10,7 @@
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { afterUpdate, onMount } from 'svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import {
|
||||
isRelationship,
|
||||
@@ -18,8 +18,7 @@
|
||||
isString
|
||||
} from './document-[document]/attributes/store';
|
||||
import RelationshipsModal from './relationshipsModal.svelte';
|
||||
import { attributes, collection, columns } from './store';
|
||||
import type { ColumnType } from '$lib/helpers/types';
|
||||
import { attributes, collection } from './store';
|
||||
import {
|
||||
Tooltip,
|
||||
Table,
|
||||
@@ -33,8 +32,11 @@
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import DualTimeView from '$lib/components/dualTimeView.svelte';
|
||||
import { flags } from '$lib/flags';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
|
||||
export let data: PageData;
|
||||
export let columns: Writable<Column[]>;
|
||||
|
||||
const databaseId = page.params.database;
|
||||
const collectionId = page.params.collection;
|
||||
@@ -45,23 +47,8 @@
|
||||
|
||||
onMount(async () => {
|
||||
displayNames = preferences.getDisplayNames();
|
||||
updateMaxWidth();
|
||||
});
|
||||
|
||||
afterUpdate(() => updateMaxWidth());
|
||||
|
||||
function updateMaxWidth() {
|
||||
const tableCells = Array.from(document.querySelectorAll('.less-width-truncated'));
|
||||
|
||||
const visibleColumnsCount = $columns.filter((col) => !col.hide).length;
|
||||
const newMaxWidth = Math.max(50 - (visibleColumnsCount - 1) * 5, 25);
|
||||
|
||||
tableCells.forEach((cell) => {
|
||||
const cellItem = cell as HTMLElement;
|
||||
cellItem.style.maxWidth = `${newMaxWidth}vw`;
|
||||
});
|
||||
}
|
||||
|
||||
function formatArray(array: unknown[]) {
|
||||
if (array.length === 0) return '[ ]';
|
||||
|
||||
@@ -100,24 +87,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
$: selected = preferences.getCustomCollectionColumns(page.params.collection);
|
||||
|
||||
$: {
|
||||
columns.set(
|
||||
$collection.attributes.map((attribute) => ({
|
||||
id: attribute.key,
|
||||
title: attribute.key,
|
||||
type: attribute.type as ColumnType,
|
||||
show: selected?.includes(attribute.key) ?? true,
|
||||
array: attribute?.array,
|
||||
width: { min: 168 },
|
||||
format:
|
||||
'format' in attribute && attribute?.format === 'enum' ? attribute.format : null,
|
||||
elements: 'elements' in attribute ? attribute.elements : null
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
let selectedRows: string[] = [];
|
||||
let showDelete = false;
|
||||
|
||||
@@ -173,41 +142,42 @@
|
||||
const showEncrypt = flags.showAttributeEncrypt(data);
|
||||
</script>
|
||||
|
||||
<Table.Root
|
||||
let:root
|
||||
allowSelection
|
||||
bind:selectedRows
|
||||
columns={[
|
||||
{ id: '$id', width: 200 },
|
||||
...$columns,
|
||||
{ id: '$created', width: 200 },
|
||||
{ id: '$updated', width: 200 }
|
||||
]}>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="$id" {root}>Document ID</Table.Header.Cell>
|
||||
{#each $columns as column}
|
||||
<Table.Header.Cell column={column.id} {root}>{column.title}</Table.Header.Cell>
|
||||
<Table.VirtualRoot let:root let:virtualizer allowSelection bind:selectedRows columns={$columns}>
|
||||
<svelte:fragment slot="header" let:root let:virtualizer>
|
||||
{#each virtualizer.getVirtualItems() as item (item.index)}
|
||||
{@const column = $columns[item.index]}
|
||||
<Table.VirtualCell column={column.id} {root} virtualItem={item}>
|
||||
{column.title}
|
||||
</Table.VirtualCell>
|
||||
{/each}
|
||||
<Table.Header.Cell column="$created" {root}>Created</Table.Header.Cell>
|
||||
<Table.Header.Cell column="$updated" {root}>Updated</Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each data.documents.documents as document (document.$id)}
|
||||
<Table.Row.Link
|
||||
{root}
|
||||
id={document.$id}
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}/collection-${$collection.$id}/document-${document.$id}`}>
|
||||
<Table.Cell column="$id" {root}>
|
||||
{#key document.$id}
|
||||
<Id value={document.$id}>
|
||||
{document.$id}
|
||||
</Id>
|
||||
{/key}
|
||||
</Table.Cell>
|
||||
|
||||
{#each $columns as { id } (id)}
|
||||
{@const attr = $attributes.find((n) => n.key === id)}
|
||||
{#if attr}
|
||||
<Table.Cell column={id} {root}>
|
||||
{#each virtualizer.getVirtualItems() as item (item.index)}
|
||||
{@const column = $columns[item.index]}
|
||||
{#if column.id === '$id'}
|
||||
<Table.VirtualCell column="$id" {root} virtualItem={item}>
|
||||
{#key document.$id}
|
||||
<Id value={document.$id}>
|
||||
{document.$id}
|
||||
</Id>
|
||||
{/key}
|
||||
</Table.VirtualCell>
|
||||
{:else if column.id === '$created'}
|
||||
<Table.VirtualCell column="$created" {root} virtualItem={item}>
|
||||
<DualTimeView time={document.$createdAt} />
|
||||
</Table.VirtualCell>
|
||||
{:else if column.id === '$updated'}
|
||||
<Table.VirtualCell column="$updated" {root} virtualItem={item}>
|
||||
<DualTimeView time={document.$updatedAt} />
|
||||
</Table.VirtualCell>
|
||||
{:else}
|
||||
{@const id = column.id}
|
||||
{@const attr = $attributes.find((n) => n.key === id)}
|
||||
<Table.VirtualCell column={id} {root} virtualItem={item}>
|
||||
{#if isRelationship(attr)}
|
||||
{@const args = displayNames?.[attr.relatedCollection] ?? ['$id']}
|
||||
{#if !isRelationshipToMany(attr)}
|
||||
@@ -253,23 +223,20 @@
|
||||
</Button.Button>
|
||||
{/if}
|
||||
{:else}
|
||||
{@const datetime = document[id]}
|
||||
{@const formatted = formatColumn(document[id])}
|
||||
{@const isDatetimeAttribute = attr.type === 'datetime'}
|
||||
{@const isEncryptedAttribute = isString(attr) && attr.encrypt}
|
||||
{#if isDatetimeAttribute}
|
||||
<DualTimeView time={datetime}>
|
||||
<DualTimeView time={document[id]}>
|
||||
<span slot="title">Timestamp</span>
|
||||
{toLocaleDateTime(datetime, true)}
|
||||
{toLocaleDateTime(document[id], true)}
|
||||
</DualTimeView>
|
||||
{:else if isEncryptedAttribute && showEncrypt}
|
||||
<button on:click={(e) => e.preventDefault()}>
|
||||
<InteractiveText
|
||||
copy={false}
|
||||
variant="secret"
|
||||
isVisible={false}
|
||||
text={formatted.value} />
|
||||
</button>
|
||||
<InteractiveText
|
||||
copy={false}
|
||||
variant="secret"
|
||||
isVisible={false}
|
||||
text={formatted.value} />
|
||||
{:else if formatted.truncated}
|
||||
<Tooltip placement="bottom" disabled={!formatted.truncated}>
|
||||
<Typography.Text truncate>{formatted.value}</Typography.Text>
|
||||
@@ -287,18 +254,12 @@
|
||||
<Typography.Text truncate>{formatted.value}</Typography.Text>
|
||||
{/if}
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
</Table.VirtualCell>
|
||||
{/if}
|
||||
{/each}
|
||||
<Table.Cell column="$created" {root}>
|
||||
<DualTimeView time={document.$createdAt} />
|
||||
</Table.Cell>
|
||||
<Table.Cell column="$updated" {root}>
|
||||
<DualTimeView time={document.$updatedAt} />
|
||||
</Table.Cell>
|
||||
</Table.Row.Link>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
</Table.VirtualRoot>
|
||||
|
||||
<RelationshipsModal bind:show={showRelationships} {selectedRelationship} data={relationshipData} />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user