Merge pull request #2352 from appwrite/accordions-for-relations

This commit is contained in:
Darshan
2025-09-11 16:08:18 +05:30
committed by GitHub
31 changed files with 184 additions and 63 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@2cf27e0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@fb8b1ed",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0",
"@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@2cf27e0
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@2cf27e0(svelte@5.25.3)
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)
'@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@fb8b1ed
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@fb8b1ed(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0(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@2cf27e0':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@2cf27e0}
'@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}
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@fb8b1ed':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@fb8b1ed}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0}
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@2cf27e0(svelte@5.25.3)':
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d8326d0(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@fb8b1ed(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d8326d0(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
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = undefined;
export let id: string;
@@ -16,6 +17,7 @@
export let step: number | 'any' = 0.001;
export let min: string = undefined;
export let max: string = undefined;
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -49,6 +51,7 @@
{readonly}
{min}
{max}
{leadingIcon}
type="date"
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
+3 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { type ComponentType, onMount } from 'svelte';
import { Input, Layout, Selector } from '@appwrite.io/pink-svelte';
export let id: string;
@@ -13,6 +13,7 @@
export let autocomplete = false;
export let step: number | 'any' = 0.001;
export let type: 'date' | 'time' | 'datetime-local' = 'date';
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
let element: HTMLInputElement;
@@ -54,6 +55,7 @@
{step}
{type}
helper={error}
{leadingIcon}
on:change={onChange}
autocomplete={autocomplete ? 'on' : 'off'}>
{#if nullable}
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = null;
export let id: string;
@@ -13,6 +14,7 @@
export let autofocus = false;
export let autocomplete = false;
export let maxlength: number | undefined = undefined;
export let leadingIcon: ComponentType | undefined = undefined;
// https://www.geeksforgeeks.org/how-to-validate-a-domain-name-using-regular-expression/
const pattern = String.raw`(?!-)[A-Za-z0-9\-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,18}`;
@@ -50,6 +52,7 @@
{nullable}
{pattern}
{maxlength}
{leadingIcon}
on:input
on:invalid={handleInvalid}
type="text"
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = null;
export let id: string;
@@ -12,6 +13,7 @@
export let readonly = false;
export let autofocus = false;
export let autocomplete = false;
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -42,6 +44,7 @@
{required}
{readonly}
{label}
{leadingIcon}
{nullable}
on:input
on:invalid={handleInvalid}
+3
View File
@@ -1,9 +1,11 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let value = '';
export let autofocus = true;
export let required = false;
export let leadingIcon: ComponentType | undefined = undefined;
let error = false;
const pattern = String.raw`^[a-zA-Z0-9][a-zA-Z0-9._\-]*$`;
@@ -26,6 +28,7 @@
{pattern}
{autofocus}
{required}
{leadingIcon}
id="id"
placeholder="Enter ID"
maxlength={36}
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = null;
export let id: string;
@@ -15,6 +16,7 @@
export let max: number = null;
export let step: number | 'any' = 1;
export let helper: string = undefined;
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -56,6 +58,7 @@
{label}
{step}
{nullable}
{leadingIcon}
bind:value
autofocus={autofocus || undefined}
helper={error || helper}
+3 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { type ComponentType, onMount } from 'svelte';
import { Input } from '@appwrite.io/pink-svelte';
export let label: string = undefined;
@@ -15,6 +15,7 @@
export let maxlength: number = null;
export let autofocus = false;
export let autocomplete = false;
export let leadingIcon: ComponentType | undefined = undefined;
let element: HTMLInputElement;
let error: string;
@@ -60,6 +61,7 @@
{label}
{pattern}
{readonly}
{leadingIcon}
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
helper={error}
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let id: string;
export let label: string;
@@ -12,6 +13,7 @@
export let autocomplete = false;
export let minlength = 8;
export let maxlength: number = null;
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -42,6 +44,7 @@
{minlength}
{maxlength}
{placeholder}
{leadingIcon}
state={error ? 'error' : 'default'}
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = null;
export let id: string;
@@ -14,6 +15,7 @@
export let minlength: number = null;
export let maxlength: number = 15;
export let nullable = false;
export let leadingIcon: ComponentType | undefined = undefined;
const pattern = String.raw`^\+[1-9]\d{1,14}$`;
@@ -55,6 +57,7 @@
{label}
{nullable}
{pattern}
{leadingIcon}
on:input
on:invalid={handleInvalid}
type="text"
@@ -1,8 +1,10 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let value = '';
export let autofocus = true;
export let leadingIcon: ComponentType | undefined = undefined;
let error = false;
@@ -25,6 +27,7 @@
<Input.Text
{pattern}
{autofocus}
{leadingIcon}
id="id"
placeholder="Enter ID"
maxlength={36}
+3 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { IconX, IconSearch } from '@appwrite.io/pink-icons-svelte';
import { Input, Icon } from '@appwrite.io/pink-svelte';
import { createEventDispatcher } from 'svelte';
import { type ComponentType, createEventDispatcher } from 'svelte';
import { onDestroy } from 'svelte';
export let value = '';
@@ -11,6 +11,7 @@
export let disabled = false;
export let autofocus = false;
export let label: string = '';
export let leadingIcon: ComponentType | undefined = undefined;
const dispatch = createEventDispatcher();
let element: HTMLInputElement;
@@ -48,6 +49,7 @@
{required}
{autofocus}
{label}
{leadingIcon}
bind:value
on:input={valueChange}>
<svelte:fragment slot="start">
+3 -1
View File
@@ -1,10 +1,12 @@
<script lang="ts">
import type { ComponentType } from 'svelte';
import { Input } from '@appwrite.io/pink-svelte';
export let id: string;
export let label: string = '';
export let value = '';
export let placeholder = '';
export let leadingIcon: ComponentType | undefined = undefined;
</script>
<Input.Password {id} {label} {value} {placeholder} readonly />
<Input.Password {id} {label} {value} {placeholder} {leadingIcon} readonly />
@@ -19,6 +19,7 @@
leadingHtml?: string;
badge?: string;
}[];
export let leadingIcon: ComponentType | undefined = undefined;
let element: HTMLSelectElement;
let error: string;
@@ -56,6 +57,7 @@
{placeholder}
{disabled}
{autofocus}
{leadingIcon}
helper={error ?? helper}
{required}
state={error ? 'error' : 'default'}
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let id: string;
export let label: string;
@@ -9,6 +10,7 @@
export let disabled = false;
export let helper: string | undefined = undefined;
export let pattern: string | undefined = undefined;
export let leadingIcon: ComponentType | undefined = undefined;
let value = '';
let error: string;
@@ -37,6 +39,7 @@
{disabled}
{pattern}
{required}
{leadingIcon}
bind:value={tags}
helper={error || helper}
on:invalid={handleInvalid}
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = undefined;
export let id: string;
@@ -16,6 +17,7 @@
export let autofocus = false;
export let autocomplete = false;
export let maxlength: number = null;
export let leadingIcon: ComponentType | undefined = undefined;
export let error: string = null;
@@ -54,6 +56,7 @@
{nullable}
{readonly}
{pattern}
{leadingIcon}
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
helper={error || helper}
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label = '';
export let id: string;
@@ -15,6 +16,7 @@
export let maxlength: number = null;
export let rows = 3;
export let helper: string = '';
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -43,6 +45,7 @@
{label}
{nullable}
{rows}
{leadingIcon}
autofocus={autofocus || undefined}
helper={error || helper}
state={error ? 'error' : 'default'}
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string = undefined;
export let id: string;
@@ -16,6 +17,7 @@
export let step: number | 'any' = 0.001;
export let min: string = undefined;
export let max: string = undefined;
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -49,6 +51,7 @@
{readonly}
{min}
{max}
{leadingIcon}
type="time"
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
+3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let label: string;
export let id: string;
@@ -13,6 +14,7 @@
export let autofocus = false;
export let autocomplete = false;
export let maxlength: number = null;
export let leadingIcon: ComponentType | undefined = undefined;
let error: string;
@@ -46,6 +48,7 @@
{maxlength}
{label}
{nullable}
{leadingIcon}
type="url"
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
@@ -41,9 +41,7 @@
tables: []
});
const sortedTables = $derived.by(() =>
tables?.tables?.slice().sort((a, b) => a.name.localeCompare(b.name))
);
const sortedTables = $derived.by(() => tables?.tables);
const selectedTable = $derived.by(() =>
sortedTables?.find((table: Models.Table) => table.$id === tableId)
@@ -1,6 +1,7 @@
<script lang="ts">
import { InputSelect } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { IconToggle } from '@appwrite.io/pink-icons-svelte';
export let id: string;
export let label: string;
@@ -29,4 +30,6 @@
{optionalText}
autofocus={limited}
placeholder="Select a value"
required={column.required} />
required={column.required}
leadingIcon={!limited ? IconToggle : undefined}>
</InputSelect>
@@ -1,6 +1,7 @@
<script lang="ts">
import { InputDateTime } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { InputDateTime } from '$lib/elements/forms';
import { IconCalendar } from '@appwrite.io/pink-icons-svelte';
export let id: string;
export let label: string;
@@ -16,4 +17,12 @@
}
</script>
<InputDateTime {id} {label} {type} bind:value {nullable} {autofocus} required={column.required} />
<InputDateTime
{id}
{label}
{type}
bind:value
{nullable}
{autofocus}
required={column.required}
leadingIcon={!limited ? IconCalendar : undefined} />
@@ -1,6 +1,7 @@
<script lang="ts">
import InputEmail from '$lib/elements/forms/inputEmail.svelte';
import type { Models } from '@appwrite.io/console';
import { IconMail } from '@appwrite.io/pink-icons-svelte';
import InputEmail from '$lib/elements/forms/inputEmail.svelte';
export let id: string;
export let label: string;
@@ -18,5 +19,6 @@
bind:value
{nullable}
{autofocus}
placeholder="Enter URL"
required={column.required} />
placeholder="name@example.com"
required={column.required}
leadingIcon={!limited ? IconMail : undefined} />
@@ -1,6 +1,7 @@
<script lang="ts">
import { InputSelect } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { IconList } from '@appwrite.io/pink-icons-svelte';
export let id: string;
export let label: string;
@@ -50,5 +51,6 @@
{optionalText}
autofocus={limited}
required={column.required}
placeholder="Select a value" />
placeholder="Select a value"
leadingIcon={!limited ? IconList : undefined} />
{/if}
@@ -2,6 +2,7 @@
import { InputNumber } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { isWithinSafeRange } from '$lib/helpers/numbers';
import { IconHashtag } from '@appwrite.io/pink-icons-svelte';
export let id: string;
export let label: string;
@@ -30,4 +31,5 @@
min={column.min}
max={column.max}
required={column.required}
step={column.type === 'double' ? 'any' : 1} />
step={column.type === 'double' ? 'any' : 1}
leadingIcon={!limited ? IconHashtag : undefined} />
@@ -6,7 +6,7 @@
import { Query, type Models } from '@appwrite.io/console';
import { type ComponentType, onMount } from 'svelte';
import { isRelationshipToMany } from '../../store';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
import { IconPlus, IconRelationship, IconX } from '@appwrite.io/pink-icons-svelte';
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
type SelectOption = {
@@ -201,6 +201,8 @@
{#if !limited}
<Layout.Stack direction="row" alignContent="space-between">
<Layout.Stack gap="xxs" direction="row" alignItems="center">
<Icon icon={IconRelationship} size="s" color="--fgcolor-neutral-weak" />
<Typography.Text variant="m-500">{label}</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
{optionalText}
@@ -229,7 +231,8 @@
on:change={() => {
if (!relatedList[0]) relatedList = [''];
updateRelatedList();
}} />
}}
leadingIcon={!limited ? IconRelationship : undefined} />
</Layout.Stack>
{/if}
@@ -245,7 +248,9 @@
options={getAvailableOptions(actualIndex)}
bind:value={relatedList[actualIndex]}
placeholder={`Select ${column.key}`}
on:change={updateRelatedList} />
on:change={updateRelatedList}
leadingIcon={!limited ? IconRelationship : undefined} />
{#if !limited}
{#if relatedList[actualIndex]}
<div style:padding-block-start="0.5rem">
@@ -271,7 +276,9 @@
required
options={getAvailableOptions(i)}
bind:value={item}
on:change={updateRelatedList} />
on:change={updateRelatedList}
leadingIcon={!limited ? IconRelationship : undefined} />
<div style:padding-block-start="0.5rem">
<Button
icon
@@ -297,7 +304,9 @@
placeholder={`Select ${column.key}`}
bind:value={newItemValue}
options={getAvailableOptions()}
on:change={addNewItem} />
on:change={addNewItem}
leadingIcon={!limited ? IconRelationship : undefined} />
<div style:padding-block-start="0.5rem">
<Button icon extraCompact on:click={cancelAddItem}>
<Icon icon={IconX} size="s" />
@@ -333,7 +342,8 @@
} else {
value = rowList.rows.find((row) => row.$id === singleRel);
}
}} />
}}
leadingIcon={!limited ? IconRelationship : undefined} />
{#if !limited && singleRel}
<div style:padding-block-start="2.25rem">
@@ -1,6 +1,7 @@
<script lang="ts">
import type { Models } from '@appwrite.io/console';
import { Layout, Link } from '@appwrite.io/pink-svelte';
import { IconText } from '@appwrite.io/pink-icons-svelte';
import { InputText, InputTextarea } from '$lib/elements/forms';
let {
@@ -132,7 +133,8 @@
{autofocus}
bind:value={stringValue}
required={column.required}
placeholder={getPlaceholder()}>
placeholder={getPlaceholder()}
leadingIcon={!limited ? IconText : undefined}>
<Layout.Stack direction="column" alignItems="flex-start" slot="end">
{#if array}
<Link.Button on:click size="s" variant="quiet">Advanced edit</Link.Button>
@@ -148,5 +150,6 @@
{maxlength}
bind:value={stringValue}
placeholder="Enter string"
required={column.required} />
required={column.required}
leadingIcon={!limited ? IconText : undefined} />
{/if}
@@ -1,6 +1,7 @@
<script lang="ts">
import InputURL from '$lib/elements/forms/inputURL.svelte';
import type { Models } from '@appwrite.io/console';
import InputURL from '$lib/elements/forms/inputURL.svelte';
import { IconLink } from '@appwrite.io/pink-icons-svelte';
export let id: string;
export let label: string;
@@ -19,4 +20,5 @@
{nullable}
{autofocus}
placeholder="Enter URL"
required={column.required} />
required={column.required}
leadingIcon={!limited ? IconLink : undefined} />
@@ -11,8 +11,9 @@
import { type Columns, PROHIBITED_ROW_KEYS } from '../store';
import ColumnItem from './columns/columnItem.svelte';
import { buildWildcardColumnsQuery, isRelationship, isRelationshipToMany } from './store';
import { Divider, Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
import { Accordion, Layout, Skeleton } from '@appwrite.io/pink-svelte';
import { deepClone } from '$lib/helpers/object';
import { preferences } from '$lib/stores/preferences';
const databaseId = page.params.database;
@@ -192,6 +193,7 @@
const relatedIds = currentColumn.map((doc: string | Record<string, unknown>) =>
typeof doc === 'string' ? doc : doc.$id
);
return !symmetricDifference(workIds, relatedIds).length;
} else {
const workId = typeof workColumn === 'string' ? workColumn : workColumn?.$id;
@@ -312,6 +314,20 @@
};
}
function getAccordionTitle(row: Models.Row): string {
const names = preferences.getDisplayNames(row.$tableId).filter((name) => name !== '$id');
const values = names
.map((name) => row?.[name])
.filter((value) => value != null && typeof value === 'string' && value !== '');
if (!values.length) {
return row.$id;
}
return `${values.join(' | ')} (...${row.$id.slice(-5)})`;
}
$effect(() => {
if (rows && tableId) {
loadRelatedRow().then(() => {
@@ -322,16 +338,26 @@
</script>
{#if loading}
<div style:margin-block="" style:margin-inline-end="2.25rem">
<div style:margin-inline-end="2.25rem">
<Skeleton variant="line" height={40} width="auto" />
</div>
{:else if relatedTable?.columns?.length && fetchedRows.length}
<!-- we should not show current table column items in this view -->
{@const twoWayKeys = new Set(
relatedTable.columns
.filter((column: Models.ColumnRelationship) => column.twoWay)
.map((c) => c.key)
)}
<!-- render the filtered ones -->
{@const columnsToRender = relatedTable.columns.filter((c) => !twoWayKeys.has(c.key))}
<div bind:this={columnFormWrapper}>
{#if fetchedRows.length === 1}
{@const workStore = getStore(fetchedRows[0].$id)}
{#if workStore}
<Layout.Stack direction="column" gap="l">
{#each relatedTable.columns as column}
{#each columnsToRender as column}
{@const label = column.key}
<ColumnItem
{column}
@@ -344,28 +370,35 @@
{/if}
{:else}
<Layout.Stack direction="column" gap="m" class="column-item-stack">
<Typography.Text variant="l-400">{relatedTable.name}</Typography.Text>
<Layout.Stack direction="column" gap="l" class="column-item-stack">
<Layout.Stack direction="column" gap="xs" class="column-item-stack">
{#each fetchedRows as row, index (row.$id)}
{@const workStore = getStore(row.$id)}
{#if workStore}
{#each relatedTable.columns as column}
{@const label = column.key}
<ColumnItem
{column}
{label}
editing
formValues={workStore}
onUpdateFormValues={handleFormUpdate(row.$id)} />
{/each}
{/if}
{#if index < fetchedRows.length - 1}
<Divider />
{/if}
<Accordion
title={getAccordionTitle(row)}
hideDivider={index >= fetchedRows.length - 1}>
{#if workStore}
<Layout.Stack direction="column" gap="m">
{#each columnsToRender as column}
{@const label = column.key}
<ColumnItem
{column}
{label}
editing
formValues={workStore}
onUpdateFormValues={handleFormUpdate(row.$id)} />
{/each}
</Layout.Stack>
{/if}
</Accordion>
{/each}
</Layout.Stack>
</Layout.Stack>
{/if}
</div>
{/if}
<style lang="scss">
:global(.column-item-stack .divider) {
margin-inline-start: 4px;
}
</style>
@@ -40,7 +40,8 @@
spreadsheetRenderKey,
expandTabs,
databaseRelatedRowSheetOptions,
rowPermissionSheet
rowPermissionSheet,
type Columns
} from './store';
import type { Column, ColumnType } from '$lib/helpers/types';
import {
@@ -465,9 +466,11 @@
}
}
function openSideSheetForRelationsToMany(tableId: string, rows: string | Models.Row[]) {
$databaseRelatedRowSheetOptions.tableId = tableId;
function openSideSheetForRelationsToMany(rows: string | Models.Row[], column?: Columns) {
const relatedCol = column as Models.ColumnRelationship;
$databaseRelatedRowSheetOptions.rows = rows;
$databaseRelatedRowSheetOptions.tableId = relatedCol.relatedTable;
$databaseRelatedRowSheetOptions.show = true;
}
@@ -950,11 +953,16 @@
{/if}
<svelte:fragment slot="cell-editor" let:close>
{@const isRelatedToMany = isRelationshipToMany(rowColumn)}
{@const hasItems = isRelatedToMany
? row[columnId]?.length
: false}
<EditRowCell
{row}
column={rowColumn}
onRowStructureUpdate={updateRowContents}
noInlineEdit={isRelationshipToMany(rowColumn)}
noInlineEdit={isRelatedToMany && hasItems}
onChange={(row) => paginatedRows.update(index, row)}
onRevert={(row) => paginatedRows.update(index, row)}
openSideSheet={() => {
@@ -962,8 +970,8 @@
if (isRelationshipToMany(rowColumn)) {
openSideSheetForRelationsToMany(
columnId,
row[columnId]
row[columnId],
rowColumn
);
} else {
onSelectSheetOption('update', null, 'row', row);