|
|
|
|
<script lang="ts">
|
|
|
|
|
import type { MetadataField } from '../../lib';
|
|
|
|
|
import FieldValueEditor from './FieldValueEditor.svelte';
|
|
|
|
|
|
|
|
|
|
let {
|
|
|
|
|
fieldKey, field, locales, defaultLocale,
|
|
|
|
|
attributes = [],
|
|
|
|
|
onKeyChange, onFieldChange,
|
|
|
|
|
}: {
|
|
|
|
|
fieldKey: string;
|
|
|
|
|
field: MetadataField;
|
|
|
|
|
locales: string[];
|
|
|
|
|
defaultLocale: string;
|
|
|
|
|
attributes?: string[];
|
|
|
|
|
onKeyChange: (newKey: string) => void;
|
|
|
|
|
onFieldChange: (field: MetadataField) => void;
|
|
|
|
|
} = $props();
|
|
|
|
|
|
|
|
|
|
let editKey = $state(fieldKey);
|
|
|
|
|
let keyError = $state('');
|
|
|
|
|
|
|
|
|
|
// Sincronizar con prop si cambia externamente (p.ej. tras rename en otro sitio)
|
|
|
|
|
$effect(() => { editKey = fieldKey; });
|
|
|
|
|
|
|
|
|
|
let asBase = $derived(field as MetadataField & { hidden?: boolean; attrs?: string[] });
|
|
|
|
|
let activeAttrs = $derived(asBase.attrs ?? []);
|
|
|
|
|
|
|
|
|
|
function handleKeyCommit(): void {
|
|
|
|
|
const trimmed = editKey.trim();
|
|
|
|
|
if (!trimmed) {
|
|
|
|
|
keyError = 'La clave no puede estar vacía';
|
|
|
|
|
editKey = fieldKey; // revertir
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
keyError = '';
|
|
|
|
|
if (trimmed !== fieldKey) onKeyChange(trimmed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleKeydown(e: KeyboardEvent): void {
|
|
|
|
|
e.stopPropagation(); // evitar que suba al header de la fila
|
|
|
|
|
if (e.key === 'Enter') (e.currentTarget as HTMLInputElement).blur();
|
|
|
|
|
if (e.key === 'Escape') { editKey = fieldKey; keyError = ''; (e.currentTarget as HTMLInputElement).blur(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function update(partial: Partial<MetadataField & { hidden?: boolean; attrs?: string[] }>): void {
|
|
|
|
|
onFieldChange({ ...field, ...partial } as MetadataField);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleAttr(attr: string): void {
|
|
|
|
|
const current = asBase.attrs ?? [];
|
|
|
|
|
const next = current.includes(attr)
|
|
|
|
|
? current.filter(a => a !== attr)
|
|
|
|
|
: [...current, attr];
|
|
|
|
|
update({ attrs: next });
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col gap-4 p-4">
|
|
|
|
|
|
|
|
|
|
<!-- Key -->
|
|
|
|
|
<fieldset class="fieldset w-full">
|
|
|
|
|
<legend class="fieldset-legend">
|
|
|
|
|
Clave del campo
|
|
|
|
|
{#if keyError}
|
|
|
|
|
<span class="text-error ml-2 font-normal text-xs">{keyError}</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</legend>
|
|
|
|
|
<input
|
|
|
|
|
value={editKey}
|
|
|
|
|
oninput={(e) => { editKey = (e.currentTarget as HTMLInputElement).value; keyError = ''; }}
|
|
|
|
|
onblur={handleKeyCommit}
|
|
|
|
|
onkeydown={handleKeydown}
|
|
|
|
|
class={['input input-bordered w-full font-mono', keyError ? 'input-error' : ''].join(' ')}
|
|
|
|
|
placeholder="nombre_campo"
|
|
|
|
|
/>
|
|
|
|
|
</fieldset>
|
|
|
|
|
|
|
|
|
|
<!-- Value editor by type -->
|
|
|
|
|
<FieldValueEditor {field} {locales} {defaultLocale} {onFieldChange} />
|
|
|
|
|
|
|
|
|
|
<!-- Attributes -->
|
|
|
|
|
{#if attributes.length > 0}
|
|
|
|
|
<div class="divider my-0"></div>
|
|
|
|
|
<div class="flex flex-col gap-2">
|
|
|
|
|
<span class="text-base-content/50">Atributos</span>
|
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
|
|
|
{#each attributes as attr (attr)}
|
|
|
|
|
{@const active = activeAttrs.includes(attr)}
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onclick={() => toggleAttr(attr)}
|
|
|
|
|
class={['badge badge-md cursor-pointer select-none transition-colors',
|
|
|
|
|
active ? 'badge-primary' : 'badge-ghost border border-base-300'
|
|
|
|
|
].join(' ')}
|
|
|
|
|
>
|
|
|
|
|
{#if active}
|
|
|
|
|
<svg class="mr-1 h-3 w-3" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M2 6l3 3 5-5"/>
|
|
|
|
|
</svg>
|
|
|
|
|
{/if}
|
|
|
|
|
{attr}
|
|
|
|
|
</button>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<!-- Hidden toggle -->
|
|
|
|
|
<div class="divider my-0"></div>
|
|
|
|
|
<div class="flex items-center gap-4">
|
|
|
|
|
<span>Ocultar campo</span>
|
|
|
|
|
<input type="checkbox" checked={asBase.hidden ?? false}
|
|
|
|
|
onchange={(e) => update({ hidden: (e.currentTarget as HTMLInputElement).checked })}
|
|
|
|
|
class="toggle toggle-sm" />
|
|
|
|
|
<span class="text-base-content/50">{asBase.hidden ? 'No se muestra en la UI' : 'Visible'}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|