You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
5.4 KiB
126 lines
5.4 KiB
<script lang="ts">
|
|
import { getContext } from 'svelte'
|
|
import type { CatalogEditorStore } from '@/vcen-edit/stores/catalog-editor.svelte.js'
|
|
import { labelOf } from '@/vcen-edit/lib/label.ts'
|
|
import AttDetail from './AttDetail.svelte'
|
|
import SidebarTree from './SidebarTree.svelte'
|
|
|
|
const editor = getContext<CatalogEditorStore>('catalog-editor')
|
|
const sel = $derived(editor.selection)
|
|
const cat = $derived(editor.cat)
|
|
const ctx = $derived(sel.kind === 'sec-atts'
|
|
? { objId: sel.objId, secId: sel.secId, sec: editor.findSec(sel.objId, sel.secId), attId: sel.attId }
|
|
: null)
|
|
|
|
let creating = $state(false)
|
|
let newAttName = $state('')
|
|
let newAttSlug = $state('')
|
|
let attSlugError = $state('')
|
|
|
|
function slugify(text: string): string {
|
|
return text.toLowerCase()
|
|
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
|
.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
|
|
}
|
|
|
|
function confirmAdd() {
|
|
if (!ctx?.sec) return
|
|
const slug = newAttSlug.trim()
|
|
if (!slug) { attSlugError = 'El ID es obligatorio'; return }
|
|
if (editor.isAttSlugTaken(slug)) { attSlugError = 'ID ya existe'; return }
|
|
editor.addAtt(ctx.objId, ctx.secId, newAttName.trim() || 'Atributo', slug)
|
|
creating = false; newAttName = ''; newAttSlug = ''; attSlugError = ''
|
|
}
|
|
function cancelAdd() {
|
|
creating = false; newAttName = ''; newAttSlug = ''; attSlugError = ''
|
|
}
|
|
</script>
|
|
|
|
{#if ctx?.sec}
|
|
{@const { objId, secId, sec, attId } = ctx}
|
|
<div class="flex h-full">
|
|
|
|
<SidebarTree title="Atributos" count={sec.atts.length} onAdd={() => { creating = true; newAttName = '' }}>
|
|
{#if sec.atts.length === 0 && !creating}
|
|
<div class="px-4 py-6 text-center text-gray-700 text-xs italic">
|
|
Sin atributos
|
|
<button
|
|
onclick={() => { creating = true; newAttName = '' }}
|
|
class="block mx-auto mt-2 px-3 py-1 rounded border border-dashed border-gray-700
|
|
text-gray-600 hover:text-indigo-400 hover:border-indigo-700 transition-colors"
|
|
>Añadir atributo</button>
|
|
</div>
|
|
{:else}
|
|
{#each sec.atts as att (att.id)}
|
|
{@const isActive = attId === att.id}
|
|
<button
|
|
onclick={() => editor.select({ kind: 'sec-atts', objId, secId, attId: att.id })}
|
|
class="w-full text-left px-4 py-2 flex items-center justify-between
|
|
cursor-pointer transition-colors border-l-2
|
|
{isActive ? 'bg-gray-800 border-blue-500' : 'border-transparent hover:bg-gray-800'}"
|
|
>
|
|
<span class="text-sm truncate flex-1 {isActive ? 'text-gray-100' : 'text-gray-300'}">
|
|
{labelOf(att.label, cat.lang) || 'Sin nombre'}
|
|
</span>
|
|
<div class="flex items-center gap-1 shrink-0 ml-2">
|
|
<span class="text-xs px-1 py-0.5 rounded bg-gray-800 text-gray-500 border border-gray-700">
|
|
{att.value.domain.kind}
|
|
</span>
|
|
{#if att.rules?.rules?.length}
|
|
<span class="text-xs text-blue-400" title="Tiene reglas">⚡</span>
|
|
{/if}
|
|
</div>
|
|
</button>
|
|
{/each}
|
|
{/if}
|
|
{#if creating}
|
|
<div class="px-3 py-2 border-t border-gray-800 bg-gray-900/60 flex flex-col gap-1.5 mt-1">
|
|
<input
|
|
type="text"
|
|
bind:value={newAttName}
|
|
oninput={e => { if (!newAttSlug || newAttSlug === slugify(newAttName.slice(0,-1))) newAttSlug = slugify(e.currentTarget.value) }}
|
|
placeholder="Nombre del atributo"
|
|
class="field-input text-xs"
|
|
autofocus
|
|
/>
|
|
<div class="flex items-center gap-1">
|
|
<span class="text-[10px] text-gray-600 shrink-0">at:</span>
|
|
<input
|
|
type="text"
|
|
bind:value={newAttSlug}
|
|
oninput={() => { attSlugError = '' }}
|
|
onkeydown={e => { if (e.key === 'Enter') confirmAdd(); if (e.key === 'Escape') cancelAdd() }}
|
|
placeholder="slug-atributo"
|
|
class="field-input text-xs font-mono flex-1"
|
|
/>
|
|
</div>
|
|
{#if attSlugError}
|
|
<p class="text-[10px] text-red-400">{attSlugError}</p>
|
|
{/if}
|
|
<div class="flex gap-1">
|
|
<button onclick={confirmAdd}
|
|
class="flex-1 px-2 py-1 text-xs rounded bg-indigo-600 hover:bg-indigo-500 text-white transition-colors">
|
|
Añadir
|
|
</button>
|
|
<button onclick={cancelAdd}
|
|
class="px-2 py-1 text-xs rounded border border-gray-700 text-gray-500 hover:text-gray-300 transition-colors">
|
|
✕
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</SidebarTree>
|
|
|
|
<!-- Att detail -->
|
|
<div class="flex-1 overflow-auto">
|
|
{#if attId}
|
|
<AttDetail />
|
|
{:else}
|
|
<div class="flex items-center justify-center h-full text-gray-600 text-sm">
|
|
Selecciona un atributo para editarlo
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/if}
|