hochladen

This commit is contained in:
2025-07-08 09:32:19 +02:00
parent 1c471b4239
commit f335f7a13f
4 changed files with 262 additions and 183 deletions

View File

@@ -1,126 +1,101 @@
<script lang="ts">
import Edit from '$lib/icons/Edit.svelte';
import Trash from '$lib/icons/Trash.svelte';
import { validateInput } from '$lib/helper/error-utils';
import { tick } from 'svelte';
interface ListItem {
name: string;
token?: string;
// add other properties as needed
}
let {
list,
editedName = $bindable(),
currentName,
onSave = () => {},
onDelete = () => {}
} = $props();
type Variant = '' | 'casename' | 'crimename';
let names = list.map((l: ListItem) => l.name);
let localName = $state(currentName);
//let names = list;
let wasCancelled = $state(false);
export let inputValue: string = '';
export let variant: Variant = '';
export let existings: ListItem[];
export let id: number;
export let editable: boolean = true;
export let editing: boolean;
// Automatisch berechneter Fehler
let error: string = $derived(validateName(localName));
//CustomEvents:
export let editStart: (payload: {
id: number;
inputValue: string;
variant: Variant;
editing: boolean;
}) => void;
// Manuell steuerbarer Fehlerstatus
let manualError = $state('');
export let save: (payload: { newValue: string; oldValue: string; variant: Variant }) => void;
export let deleteItem: (payload: {
inputValue: string;
variant: Variant;
customEvent: Event;
}) => void;
export let cancel: () => void = () => {};
const existingNames = existings.map((item) => item.name);
console.log('EditableItem: Beginn', names, editedName);
export { classNames as class };
let isEditing = $state(false);
let inputRef: HTMLInputElement;
let internalValue = inputValue;
let oldValue = inputValue;
let showWarning = false;
// Validierungsfunktion
function validateName(name: string) {
const trimmed = name.trim();
$: errors = validateInput(oldValue, internalValue, { minLength: 3, existingNames });
function startEdit() {
oldValue = inputValue;
internalValue = inputValue;
editing = true;
editStart({ id, inputValue, variant, editing });
}
function cancelEdit() {
internalValue = oldValue;
editing = false;
showWarning = false;
console.log('Abgebrochen');
cancel();
}
function saveEdit(ev?: MouseEvent | KeyboardEvent) {
ev?.preventDefault();
ev?.stopPropagation();
const name = internalValue.trim();
if (errors.length !== 0) {
showWarning = true;
console.log('Abgebrochen', errors);
return;
}
if (!name) {
showWarning = true;
console.log('Abgebrochen', showWarning);
return;
if (!trimmed) {
return 'Name darf nicht leer sein.';
}
if (existingNames.includes(name) && name !== oldValue.trim()) {
showWarning = true;
console.log('Abgebrochen', showWarning);
return;
}
const duplicate = list.some(
(item: ListItem) => item.name === trimmed && item.name !== currentName
);
editing = false;
showWarning = false;
save({ newValue: internalValue, oldValue, variant });
if (duplicate) return 'Name existiert bereits.';
return '';
}
function handleKey(e: KeyboardEvent) {
if (e.key === 'Enter') saveEdit(e);
if (e.key === 'Escape') cancelEdit();
// Speichern, wenn gültig und zurück an Eltern
function commitIfValid() {
if (!validateName(localName) && !wasCancelled && localName != currentName) {
editedName = localName.trim();
onSave(editedName, currentName); //Eltern benachrichtigen
} else {
resetEdit();
}
}
// Abbrechen Eingabe zurücksetzen
function resetEdit() {
wasCancelled = false;
manualError = '';
inputRef?.blur();
isEditing = false;
}
// Tastatursteuerung
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault();
commitIfValid();
} else if (event.key === 'Escape') {
event.preventDefault();
editedName = currentName;
resetEdit();
}
}
async function startEdit() {
isEditing = true;
await tick();
inputRef?.focus();
}
</script>
<div class="">
<div class="flex gap-x-2">
{#if editing}
<input
bind:value={internalValue}
on:keydown|preventDefault={handleKey}
on:blur|preventDefault|stopPropagation={cancelEdit}
class=""
class:bg-red-200={(showWarning && editing) || errors.length !== 0}
/>
{:else}
<span>{inputValue}</span>
{#if !editing && editable}
<button on:click|preventDefault|stopPropagation={startEdit}>
<Edit />
</button>
<button
on:click|preventDefault={(e: Event) => {
deleteItem({ inputValue, variant, customEvent: e });
}}
>
<Trash />
</button>
{/if}
{/if}
</div>
{#if editing && errors}
<p class="text-red-600 text-sm mt-1 font-medium">{errors[0]}</p>
<div>
<input
bind:this={inputRef}
bind:value={localName}
onblur={commitIfValid}
onkeydown={handleKeydown}
/>
<button onclick={startEdit}><Edit /></button>
<button onclick={() => onDelete(currentName)}><Trash /></button>
{#if manualError || error}
<p style="color: red;">{manualError || error}</p>
{/if}
</div>