mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #216 from appwrite/feat-empty-arrays-ui
Feat: empty arrays UI
This commit is contained in:
@@ -1,17 +1,24 @@
|
||||
import type { Action } from 'svelte/action';
|
||||
import type { Props } from 'tippy.js';
|
||||
import type { Props as TippyProps } from 'tippy.js';
|
||||
import tippy from 'tippy.js';
|
||||
|
||||
type Props = TippyProps & {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const tooltip: Action<HTMLElement, Partial<Props>> = (node, config) => {
|
||||
const instance = tippy(node, config);
|
||||
if (config.disabled) instance.disable();
|
||||
|
||||
return {
|
||||
update({ content }) {
|
||||
update({ content, disabled }) {
|
||||
if (content !== instance.props.content) {
|
||||
instance.setProps({
|
||||
content
|
||||
});
|
||||
}
|
||||
|
||||
disabled ? instance.disable() : instance.enable();
|
||||
},
|
||||
destroy() {
|
||||
instance.destroy();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { FormItem, Helper } from '.';
|
||||
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let value = false;
|
||||
@@ -26,7 +27,13 @@
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>
|
||||
{label}
|
||||
{#if optionalText}
|
||||
<span class="optional">{optionalText}</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let id: string;
|
||||
export let value = '';
|
||||
export let required = false;
|
||||
@@ -38,7 +39,13 @@
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>
|
||||
{label}
|
||||
<span class:u-hide={!showLabel || !optionalText} class="optional">
|
||||
{optionalText}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { FormItem, Helper } from '.';
|
||||
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let value: number = null;
|
||||
@@ -51,7 +52,13 @@
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>
|
||||
{label}
|
||||
{#if optionalText}
|
||||
<span class="optional">{optionalText}</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
export let id: string;
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let value: string | number | boolean;
|
||||
export let placeholder = '';
|
||||
@@ -32,7 +33,13 @@
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>
|
||||
{label}
|
||||
{#if optionalText}
|
||||
<span class="optional">{optionalText}</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<div class="select">
|
||||
<select
|
||||
{id}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { last } from '$lib/helpers/array';
|
||||
import { onMount } from 'svelte';
|
||||
import { FormItem, Helper } from '.';
|
||||
|
||||
@@ -36,7 +37,7 @@
|
||||
}
|
||||
if (['Backspace', 'Delete'].includes(e.key)) {
|
||||
if (value.length === 0) {
|
||||
removeValue(tags[tags.length - 1]);
|
||||
removeValue(last(tags));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { FormItem, Helper } from '.';
|
||||
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let value = '';
|
||||
@@ -42,8 +43,8 @@
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<label class:u-hide={!showLabel} class="label" for={id}
|
||||
>{label}
|
||||
<label class:u-hide={!showLabel} class="label" for={id}>
|
||||
{label}
|
||||
{#if tooltip}
|
||||
<span
|
||||
class="icon-info"
|
||||
@@ -52,7 +53,11 @@
|
||||
content: tooltip
|
||||
}} />
|
||||
{/if}
|
||||
{#if optionalText}
|
||||
<span class="optional">{optionalText}</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Strict typed `Object.entries`
|
||||
* Extracted from https://github.com/antfu/utils
|
||||
*
|
||||
* @category Object
|
||||
*/
|
||||
export function objectEntries<T extends object>(obj: T) {
|
||||
return Object.entries(obj) as Array<[keyof T, T[keyof T]]>;
|
||||
}
|
||||
@@ -1,3 +1,14 @@
|
||||
/**
|
||||
* Capitalizes the first letter of a string
|
||||
*
|
||||
* @export
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export function capitalize(str: string): string {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, returns the singular version of it,
|
||||
* by removing the trailing 's'.
|
||||
|
||||
+47
-1
@@ -19,6 +19,7 @@
|
||||
import { page } from '$app/stores';
|
||||
import { PAGE_LIMIT } from '$lib/constants';
|
||||
import CreateAttribute from '../createAttribute.svelte';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
|
||||
export let data: PageData;
|
||||
let showCreateAttribute = false;
|
||||
@@ -33,6 +34,44 @@
|
||||
title: attribute.key
|
||||
}))
|
||||
];
|
||||
|
||||
function formatArray(array: unknown[]) {
|
||||
if (array.length === 0) return '[ ]';
|
||||
|
||||
let formattedFields: string[] = [];
|
||||
for (const item of array) {
|
||||
if (typeof item === 'string') {
|
||||
formattedFields.push(`"${item}"`);
|
||||
} else {
|
||||
formattedFields.push(`${item}`);
|
||||
}
|
||||
}
|
||||
|
||||
return `[${formattedFields.join(', ')}]`;
|
||||
}
|
||||
|
||||
function formatColumn(column: unknown) {
|
||||
let formattedColumn: string;
|
||||
|
||||
if (typeof column === 'string') {
|
||||
formattedColumn = column;
|
||||
} else if (Array.isArray(column)) {
|
||||
formattedColumn = formatArray(column);
|
||||
} else if (!column) {
|
||||
formattedColumn = 'n/a';
|
||||
} else {
|
||||
formattedColumn = `${column}`;
|
||||
}
|
||||
|
||||
return {
|
||||
value:
|
||||
formattedColumn.length > 20
|
||||
? `${formattedColumn.slice(0, 20)}...`
|
||||
: formattedColumn,
|
||||
truncated: formattedColumn.length > 20,
|
||||
whole: formattedColumn
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -70,8 +109,15 @@
|
||||
</Copy>
|
||||
</TableCell>
|
||||
{#each columns as column}
|
||||
{@const formatted = formatColumn(document[column.key])}
|
||||
<TableCell>
|
||||
{document[column.key] ?? 'n/a'}
|
||||
<div
|
||||
use:tooltip={{
|
||||
content: formatted.whole,
|
||||
disabled: !formatted.truncated
|
||||
}}>
|
||||
{formatted.value}
|
||||
</div>
|
||||
</TableCell>
|
||||
{/each}
|
||||
</TableRowLink>
|
||||
|
||||
+4
-1
@@ -23,7 +23,7 @@
|
||||
$createDocument.attributes = $attributes.filter((a) => a.status === 'available');
|
||||
$attributes.forEach((attr) => {
|
||||
if (attr.array) {
|
||||
$createDocument.document[attr.key] = [null];
|
||||
$createDocument.document[attr.key] = [];
|
||||
} else {
|
||||
$createDocument.document[attr.key] = null;
|
||||
}
|
||||
@@ -39,12 +39,15 @@
|
||||
$createDocument.document,
|
||||
$createDocument.permissions
|
||||
);
|
||||
|
||||
addNotification({
|
||||
message: 'Document has been created',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent('submit_document_create');
|
||||
invalidate(Dependencies.DOCUMENTS);
|
||||
|
||||
createDocument.reset();
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
|
||||
+3
@@ -9,6 +9,7 @@
|
||||
|
||||
export let id: string;
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let value: string | number | boolean | null;
|
||||
export let attribute:
|
||||
| Models.AttributeBoolean
|
||||
@@ -44,6 +45,7 @@
|
||||
{id}
|
||||
{label}
|
||||
{attribute}
|
||||
{optionalText}
|
||||
bind:value />
|
||||
{:else}
|
||||
<svelte:component
|
||||
@@ -51,6 +53,7 @@
|
||||
{id}
|
||||
{label}
|
||||
{attribute}
|
||||
{optionalText}
|
||||
bind:value />
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<script lang="ts">
|
||||
import CustomId from '$lib/components/customId.svelte';
|
||||
import { FormList } from '$lib/elements/forms';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import type { Attributes } from '../store';
|
||||
import Attribute from './attribute.svelte';
|
||||
|
||||
export let attributes: Attributes[] = [];
|
||||
export let formValues: object = {};
|
||||
export let customId: string | null | undefined = undefined;
|
||||
export let gap: '16' | '32' = '32';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
function removeArrayItem(key: string, index: number) {
|
||||
formValues = {
|
||||
...formValues,
|
||||
[key]: formValues[key].filter((_, i) => i !== index)
|
||||
};
|
||||
}
|
||||
|
||||
function addArrayItem(key: string) {
|
||||
formValues = {
|
||||
...formValues,
|
||||
[key]: [...formValues[key], null]
|
||||
};
|
||||
}
|
||||
|
||||
function getAttributeType(attribute: Attributes) {
|
||||
if ('format' in attribute && attribute.format === 'enum') return 'Enum';
|
||||
return `${capitalize(attribute.type)}${attribute.array ? '[]' : ''}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if attributes.length}
|
||||
<ul class={`form-list u-gap-${gap}`}>
|
||||
{#each attributes as attribute}
|
||||
{@const label = attribute.required ? `${attribute.key}*` : attribute.key}
|
||||
{#if attribute.array}
|
||||
{#if formValues[attribute.key].length === 0}
|
||||
<div class="u-flex u-cross-center u-main-space-between">
|
||||
<span class="label u-margin-0">
|
||||
{label}
|
||||
<span class="optional">
|
||||
{getAttributeType(attribute)}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<Button text noMargin on:click={() => addArrayItem(attribute.key)}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text"> Add item</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if formValues[attribute.key].length !== 0}
|
||||
<ul class="u-grid u-gap-8">
|
||||
{#each [...formValues[attribute.key].keys()] as index}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<Attribute
|
||||
{attribute}
|
||||
id={`${attribute.key}-${index}`}
|
||||
optionalText={index === 0
|
||||
? getAttributeType(attribute)
|
||||
: undefined}
|
||||
label={index === 0 ? label : ''}
|
||||
bind:value={formValues[attribute.key][index]} />
|
||||
</div>
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
on:click={() => removeArrayItem(attribute.key, index)}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<Button text noMargin on:click={() => addArrayItem(attribute.key)}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text"> Add item</span>
|
||||
</Button>
|
||||
{/if}
|
||||
{:else}
|
||||
<FormList>
|
||||
<Attribute
|
||||
{attribute}
|
||||
{label}
|
||||
optionalText={getAttributeType(attribute)}
|
||||
id={attribute.key}
|
||||
bind:value={formValues[attribute.key]} />
|
||||
</FormList>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if customId !== undefined}
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" /><span class="text">
|
||||
Document ID
|
||||
</span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Document" bind:id={customId} />
|
||||
{/if}
|
||||
{/if}
|
||||
</ul>
|
||||
{/if}
|
||||
+3
-1
@@ -6,12 +6,14 @@
|
||||
export let label: string;
|
||||
export let value: boolean;
|
||||
export let attribute: Models.AttributeBoolean;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
{#if attribute?.array}
|
||||
<InputSelect
|
||||
{id}
|
||||
{label}
|
||||
{optionalText}
|
||||
showLabel={!!label?.length}
|
||||
placeholder="Select a value"
|
||||
required={attribute.required}
|
||||
@@ -21,5 +23,5 @@
|
||||
]}
|
||||
bind:value />
|
||||
{:else}
|
||||
<InputCheckbox {id} {label} required={attribute.required} bind:value />
|
||||
<InputCheckbox {id} {label} {optionalText} required={attribute.required} bind:value />
|
||||
{/if}
|
||||
|
||||
+8
-1
@@ -6,6 +6,13 @@
|
||||
export let label: string;
|
||||
export let value: string;
|
||||
export let attribute: Models.AttributeDatetime;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<InputDateTime {id} {label} showLabel={!!label?.length} required={attribute.required} bind:value />
|
||||
<InputDateTime
|
||||
{id}
|
||||
{label}
|
||||
{optionalText}
|
||||
showLabel={!!label?.length}
|
||||
required={attribute.required}
|
||||
bind:value />
|
||||
|
||||
+2
@@ -6,6 +6,7 @@
|
||||
export let label: string;
|
||||
export let value: string;
|
||||
export let attribute: Models.AttributeEnum;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
|
||||
$: options = attribute.elements.map((element) => {
|
||||
return {
|
||||
@@ -20,5 +21,6 @@
|
||||
{options}
|
||||
{id}
|
||||
{label}
|
||||
{optionalText}
|
||||
placeholder="Select a value"
|
||||
showLabel={!!label?.length} />
|
||||
|
||||
+2
@@ -6,11 +6,13 @@
|
||||
export let label: string;
|
||||
export let value: number;
|
||||
export let attribute: Models.AttributeInteger;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<InputNumber
|
||||
{id}
|
||||
{label}
|
||||
{optionalText}
|
||||
required={attribute.required}
|
||||
min={attribute.min}
|
||||
max={attribute.max}
|
||||
|
||||
+2
@@ -6,11 +6,13 @@
|
||||
export let label: string;
|
||||
export let value: string;
|
||||
export let attribute: Models.AttributeString;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<InputText
|
||||
{id}
|
||||
{label}
|
||||
{optionalText}
|
||||
showLabel={!!label?.length}
|
||||
required={attribute.required}
|
||||
maxlength={attribute.size}
|
||||
|
||||
+14
-91
@@ -11,27 +11,27 @@
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import Attribute from './attribute.svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import AttributeForm from './attributeForm.svelte';
|
||||
|
||||
let disableUpdate = true;
|
||||
let currentDoc: string;
|
||||
const databaseId = $page.params.database;
|
||||
const collectionId = $page.params.collection;
|
||||
const documentId = $page.params.document;
|
||||
|
||||
const work = writable(
|
||||
Object.keys($doc)
|
||||
.filter(
|
||||
(key) =>
|
||||
![
|
||||
'$id',
|
||||
'$collection',
|
||||
'$collectionId',
|
||||
'$databaseId',
|
||||
'$createdAt',
|
||||
'$updatedAt'
|
||||
].includes(key)
|
||||
)
|
||||
.filter((key) => {
|
||||
return ![
|
||||
'$id',
|
||||
'$collection',
|
||||
'$collectionId',
|
||||
'$databaseId',
|
||||
'$createdAt',
|
||||
'$updatedAt'
|
||||
].includes(key);
|
||||
})
|
||||
.reduce((obj, key) => {
|
||||
obj[key] = $doc[key];
|
||||
return obj;
|
||||
@@ -42,7 +42,7 @@
|
||||
currentDoc = JSON.stringify($work);
|
||||
});
|
||||
|
||||
$: if (currentDoc && $work) {
|
||||
$: {
|
||||
if (currentDoc !== JSON.stringify($work)) {
|
||||
disableUpdate = false;
|
||||
} else {
|
||||
@@ -77,90 +77,13 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addArrayItem(key: string) {
|
||||
work.update((n) => {
|
||||
if (!Array.isArray(n[key])) {
|
||||
n[key] = [];
|
||||
}
|
||||
n[key].push(null);
|
||||
|
||||
return n;
|
||||
});
|
||||
}
|
||||
|
||||
function removeArrayItem(key: string, index: number) {
|
||||
work.update((n) => {
|
||||
n[key].splice(index, 1);
|
||||
|
||||
return n;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Update Data</Heading>
|
||||
<p>Update document data based on the attributes created earlier.</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<form class="form u-grid u-gap-16">
|
||||
{#each $collection.attributes.filter((a) => a.status === 'available') as attribute}
|
||||
{#if attribute.array}
|
||||
<ul class="form-list">
|
||||
{#each [...$work[attribute.key].keys()] as index}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<Attribute
|
||||
{attribute}
|
||||
id={`${attribute.key}-${index}`}
|
||||
label={index === 0 ? attribute.key : ''}
|
||||
bind:value={$work[attribute.key][index]} />
|
||||
</div>
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
disabled={$work[attribute.key].length === 1}
|
||||
on:click={() => removeArrayItem(attribute.key, index)}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{:else}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<Attribute
|
||||
{attribute}
|
||||
id={`${attribute.key}-0`}
|
||||
label={attribute.key}
|
||||
bind:value={$work[attribute.key][0]} />
|
||||
</div>
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button text disabled>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<Button
|
||||
text
|
||||
noMargin
|
||||
disabled={$work[attribute.key][$work[attribute.key].length - 1] === null}
|
||||
on:click={() => addArrayItem(attribute.key)}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add item</span>
|
||||
</Button>
|
||||
{:else}
|
||||
<ul class="form-list">
|
||||
<Attribute
|
||||
{attribute}
|
||||
id={attribute.key}
|
||||
label={attribute.key}
|
||||
bind:value={$work[attribute.key]} />
|
||||
</ul>
|
||||
{/if}
|
||||
{/each}
|
||||
</form>
|
||||
<AttributeForm attributes={$collection.attributes} bind:formValues={$work} gap="16" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import { page } from '$app/stores';
|
||||
import type { Models } from '@aw-labs/appwrite-console';
|
||||
import { derived } from 'svelte/store';
|
||||
|
||||
export type Attributes =
|
||||
| Models.AttributeBoolean
|
||||
|
||||
+5
-79
@@ -1,28 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { CustomId } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Button, FormList } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import AttributeForm from '../document-[document]/attributeForm.svelte';
|
||||
import { createDocument } from './store';
|
||||
import Attribute from '../document-[document]/attribute.svelte';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
function addArrayItem(key: string) {
|
||||
createDocument.update((n) => {
|
||||
n.document[key].push(null);
|
||||
|
||||
return n;
|
||||
});
|
||||
}
|
||||
|
||||
function removeArrayItem(key: string, index: number) {
|
||||
createDocument.update((n) => {
|
||||
n.document[key].splice(index, 1);
|
||||
|
||||
return n;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
@@ -31,61 +10,8 @@
|
||||
Provide document data based on attributes you created earlier.
|
||||
</svelte:fragment>
|
||||
|
||||
{#if $createDocument.attributes.length}
|
||||
<FormList>
|
||||
{#each $createDocument.attributes as attribute}
|
||||
{@const label = attribute.required ? `${attribute.key}*` : attribute.key}
|
||||
{#if attribute.array}
|
||||
{#each [...$createDocument.document[attribute.key].keys()] as index}
|
||||
<li class="form-item is-multiple">
|
||||
<div class="form-item-part u-stretch">
|
||||
<Attribute
|
||||
{attribute}
|
||||
id={`${attribute.key}-${index}`}
|
||||
label={index === 0 ? label : ''}
|
||||
bind:value={$createDocument.document[attribute.key][index]} />
|
||||
</div>
|
||||
<div class="form-item-part u-cross-child-end">
|
||||
<Button
|
||||
text
|
||||
disabled={index === 0}
|
||||
on:click={() => removeArrayItem(attribute.key, index)}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
<Button
|
||||
text
|
||||
noMargin
|
||||
disabled={$createDocument.document[attribute.key][
|
||||
$createDocument.document[attribute.key].length - 1
|
||||
] === null}
|
||||
on:click={() => addArrayItem(attribute.key)}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text"> Add item</span>
|
||||
</Button>
|
||||
{:else}
|
||||
<FormList>
|
||||
<Attribute
|
||||
{attribute}
|
||||
{label}
|
||||
id={attribute.key}
|
||||
bind:value={$createDocument.document[attribute.key]} />
|
||||
</FormList>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" /><span class="text">
|
||||
Document ID
|
||||
</span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Document" bind:id={$createDocument.id} />
|
||||
{/if}
|
||||
</FormList>
|
||||
{/if}
|
||||
<AttributeForm
|
||||
bind:formValues={$createDocument.document}
|
||||
attributes={$createDocument.attributes}
|
||||
bind:customId={$createDocument.id} />
|
||||
</WizardStep>
|
||||
|
||||
+20
-3
@@ -1,14 +1,31 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Attributes } from '../store';
|
||||
|
||||
export const createDocument = writable<{
|
||||
type CreateDocument = {
|
||||
id?: string;
|
||||
document: object;
|
||||
permissions: string[];
|
||||
attributes: Attributes[];
|
||||
}>({
|
||||
};
|
||||
|
||||
const initialCreateDocument: CreateDocument = {
|
||||
id: null,
|
||||
document: {},
|
||||
permissions: [],
|
||||
attributes: []
|
||||
});
|
||||
};
|
||||
|
||||
function createDocumentWritable() {
|
||||
const store = writable<CreateDocument>({ ...initialCreateDocument });
|
||||
|
||||
const reset = () => {
|
||||
store.set({ ...initialCreateDocument });
|
||||
};
|
||||
|
||||
return {
|
||||
...store,
|
||||
reset
|
||||
};
|
||||
}
|
||||
|
||||
export const createDocument = createDocumentWritable();
|
||||
|
||||
@@ -72,13 +72,13 @@ test('shows text input - maxlength', () => {
|
||||
});
|
||||
|
||||
test('state', async () => {
|
||||
const { component, getByLabelText } = render(InputText, {
|
||||
const { component, container } = render(InputText, {
|
||||
id: 'input',
|
||||
label: 'input',
|
||||
value: ''
|
||||
});
|
||||
const input = getByLabelText('input');
|
||||
|
||||
const input = container.querySelector('input');
|
||||
expect(component.value).toEqual('');
|
||||
await userEvent.type(input, 'lorem');
|
||||
expect(component.value).toEqual('lorem');
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { objectEntries } from '$lib/helpers/object';
|
||||
|
||||
test('objectEntries', () => {
|
||||
const object = {
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3
|
||||
};
|
||||
|
||||
expect(objectEntries(object)).toEqual(Object.entries(object));
|
||||
});
|
||||
Reference in New Issue
Block a user