Files
immich/web/src/lib/components/SchemaConfiguration.svelte
T
2026-06-11 18:05:07 -05:00

111 lines
3.4 KiB
Svelte

<script lang="ts">
import SchemaAlbumPicker from '$lib/components/SchemaAlbumPicker.svelte';
import Self from '$lib/components/SchemaConfiguration.svelte';
import type { JSONSchemaProperty, SchemaConfig } from '$lib/types';
import {
CodeBlock,
Field,
HelperText,
Input,
Label,
MultiSelect,
NumberInput,
Select,
Switch,
Text,
} from '@immich/ui';
import { t } from 'svelte-i18n';
type Props = {
schema: JSONSchemaProperty;
root?: boolean;
key?: string;
config: SchemaConfig;
};
let { schema, key = '', root = false, config = $bindable() }: Props = $props();
const label = $derived(schema.title ?? key);
const description = $derived(schema.description);
const getValue = <T,>(defaultValue?: T) => (root === true ? config : (config?.[key] ?? defaultValue)) as T;
const setValue = <T,>(value: T) => {
if (root === true) {
config = value;
} else {
if (config === undefined) {
config = {};
}
config[key] = value;
}
};
const getUiHintValue = () => {
if (schema.array) {
return getValue<string[]>([]);
}
const values = getValue<string>();
return values ? [values] : [];
};
const setUiHintValue = (values: string[]) => setValue(schema.array ? values : values[0]);
const getBoolean = (defaultValue = false) => getValue<boolean>(defaultValue);
const getString = () => getValue<string>();
const getEnum = () => getValue<string[]>([]);
const getNumber = () => getValue<number>();
</script>
<!-- Empty schema object -->
{#if Object.keys(schema).length === 0}
<!-- noop -->
<!-- nested configuration (also top level objects) -->
{:else if schema.type === 'object'}
{#if !root}
<div class="flex flex-col gap-2">
<Label size="small" class="font-medium" {label}></Label>
{#if description}
<Text color="muted" size="small">{description}</Text>
{/if}
</div>
{/if}
<div class="flex flex-col gap-4 {root ? '' : 'border-l-4 border-gray-200 ps-2'}">
{#each Object.entries(schema.properties ?? {}) as [childKey, childSchema] (childKey)}
<Self schema={childSchema} key={childKey} bind:config={getValue, setValue} />
{/each}
</div>
{:else if schema.uiHint === 'AlbumId'}
<SchemaAlbumPicker {label} {description} array={schema.array} bind:albumIds={getUiHintValue, setUiHintValue} />
{:else if schema.enum && schema.array}
<Field {label} {description}>
<MultiSelect options={schema.enum} bind:values={getEnum, setValue} />
</Field>
{:else if schema.enum}
<Field {label} {description}>
<Select options={schema.enum} bind:value={getString, setValue} />
</Field>
{:else if schema.array}
{@const values = getValue<string[]>([])}
<Field {label} {description}>
<Input value={values.join(',')} disabled />
<HelperText>{$t('unsupported_field_type')}</HelperText>
</Field>
{:else if schema.type === 'boolean'}
<Field {label} {description}>
<Switch bind:checked={() => getBoolean(schema.default ?? false), setValue} />
</Field>
{:else if schema.type === 'number'}
<Field {label} {description}>
<NumberInput bind:value={getNumber, setValue} />
</Field>
{:else if schema.type === 'string'}
<Field {label} {description}>
<Input bind:value={() => getValue<string>(), setValue} />
</Field>
{:else}
<Text>Unknown schema</Text>
<CodeBlock code={JSON.stringify(schema, null, 2)} />
{/if}