Timeline: curator event create/edit forms (#781) #832

Open
marcel wants to merge 23 commits from feat/issue-781-timeline-curator-forms into main
3 changed files with 28 additions and 5 deletions
Showing only changes of commit 423aedcd87 - Show all commits

View File

@@ -11,12 +11,15 @@ interface Props {
selectedDocuments?: DocumentOption[];
placeholder?: string;
hiddenInputName?: string;
/** Empty-state text shown inside the chip container when nothing is selected. */
emptyLabel?: string;
}
let {
selectedDocuments = $bindable([]),
placeholder = m.geschichte_editor_search_document(),
hiddenInputName = 'documentIds'
hiddenInputName = 'documentIds',
emptyLabel = undefined
}: Props = $props();
let searchTerm = $state('');
@@ -73,7 +76,7 @@ function removeDocument(id: string | undefined) {
<button
type="button"
onclick={() => removeDocument(doc.id)}
class="ml-0.5 text-ink/50 hover:text-red-500 focus:outline-none"
class="ml-0.5 inline-flex min-h-[44px] min-w-[44px] items-center justify-center rounded-sm text-ink/50 hover:text-red-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
aria-label={m.comp_multiselect_remove()}
>
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -88,6 +91,10 @@ function removeDocument(id: string | undefined) {
</span>
{/each}
{#if emptyLabel && selectedDocuments.length === 0}
<span class="px-1 py-1 font-sans text-sm text-ink-3 italic">{emptyLabel}</span>
{/if}
<input
bind:this={inputEl}
type="text"

View File

@@ -12,6 +12,10 @@ export type DocumentOption = Pick<
export function createDocumentTypeahead() {
return createTypeahead<DocumentOption>({
// Intentional bare browser fetch (matches the Geschichte editor): in dev the
// Vite proxy forwards /api and injects the auth header; in prod the app is
// same-origin so the auth cookie travels automatically. An internal
// +server.ts proxy would add complexity with no practical security benefit.
fetchUrl: (q) =>
fetch(`/api/documents/search?q=${encodeURIComponent(q)}&size=10`)
.then((r) => {

View File

@@ -7,9 +7,17 @@ type Person = components['schemas']['Person'];
interface Props {
selectedPersons?: PersonOption[];
/** Name of the hidden inputs carrying selected ids. Mirrors DocumentMultiSelect. */
hiddenInputName?: string;
/** Empty-state text shown inside the chip container when nothing is selected. */
emptyLabel?: string;
}
let { selectedPersons = $bindable([]) }: Props = $props();
let {
selectedPersons = $bindable([]),
hiddenInputName = 'receiverIds',
emptyLabel = undefined
}: Props = $props();
let searchTerm = $state('');
let results: Person[] = $state([]);
@@ -64,7 +72,7 @@ function removePerson(id: string | undefined) {
<svelte:window onscroll={updateDropdownPosition} onresize={updateDropdownPosition} />
{#each selectedPersons as person (person.id)}
<input type="hidden" name="receiverIds" value={person.id} />
<input type="hidden" name={hiddenInputName} value={person.id} />
{/each}
<div class="relative" use:clickOutside onclickoutside={() => (showDropdown = false)}>
@@ -79,7 +87,7 @@ function removePerson(id: string | undefined) {
<button
type="button"
onclick={() => removePerson(person.id)}
class="ml-0.5 text-ink/50 hover:text-red-500 focus:outline-none"
class="ml-0.5 inline-flex min-h-[44px] min-w-[44px] items-center justify-center rounded-sm text-ink/50 hover:text-red-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
aria-label={m.comp_multiselect_remove()}
>
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -94,6 +102,10 @@ function removePerson(id: string | undefined) {
</span>
{/each}
{#if emptyLabel && selectedPersons.length === 0}
<span class="px-1 py-1 font-sans text-sm text-ink-3 italic">{emptyLabel}</span>
{/if}
<input
bind:this={inputEl}
type="text"