- apps/captain-mobile: Mobile API service - apps/flow-ui: Flow UI application - apps/mindlink: Mindlink application - apps/storage: Storage API and workers - apps/tzzr-cli: TZZR CLI tool - deck-frontend/backups: Historical TypeScript versions - hst-frontend: Standalone HST frontend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
import { View } from '../View.ts';
|
|
import { getName, getFullImg, copyMrf, delegateEvent } from '@/utils/index.ts';
|
|
import { fetchChildren, fetchRelated } from '@/api/index.ts';
|
|
import type { Store } from '@/state/store.ts';
|
|
import type { AppState, Tag } from '@/types/index.ts';
|
|
|
|
export class DetailPanel extends View {
|
|
private panelEl: HTMLElement;
|
|
|
|
constructor(
|
|
container: HTMLElement,
|
|
store: Store<AppState>
|
|
) {
|
|
super(container, store);
|
|
this.panelEl = container;
|
|
}
|
|
|
|
async showDetail(mrf: string): Promise<void> {
|
|
const state = this.getState();
|
|
const tag = state.tags.find(t => t.mrf === mrf);
|
|
if (!tag) return;
|
|
|
|
this.setState({ selectedTag: tag });
|
|
this.panelEl.classList.add('open');
|
|
await this.renderDetail(tag);
|
|
}
|
|
|
|
close(): void {
|
|
this.panelEl.classList.remove('open');
|
|
this.setState({ selectedTag: null });
|
|
}
|
|
|
|
render(): void {
|
|
const state = this.getState();
|
|
if (state.selectedTag) {
|
|
this.renderDetail(state.selectedTag);
|
|
}
|
|
}
|
|
|
|
private async renderDetail(tag: Tag): Promise<void> {
|
|
const state = this.getState();
|
|
const img = getFullImg(tag);
|
|
const name = getName(tag, state.lang);
|
|
|
|
this.panelEl.innerHTML = `
|
|
<div class="detail-header">
|
|
${img
|
|
? `<img class="detail-img" src="${img}" alt="${tag.ref}">`
|
|
: `<div class="detail-placeholder">${tag.ref?.slice(0, 2) || 'T'}</div>`
|
|
}
|
|
<button class="detail-close">×</button>
|
|
</div>
|
|
<div class="detail-body">
|
|
<div class="detail-ref">${tag.ref || ''}</div>
|
|
<div class="detail-mrf" data-mrf="${tag.mrf}">${tag.mrf}</div>
|
|
<div class="detail-name">${name}</div>
|
|
<div class="detail-desc">${tag.txt || tag.alias || ''}</div>
|
|
<div id="children-section" class="detail-section" style="display:none">
|
|
<h4>Hijos</h4>
|
|
<div id="children-list" class="chip-list"></div>
|
|
</div>
|
|
<div id="related-section" class="detail-section" style="display:none">
|
|
<h4>Relacionados</h4>
|
|
<div id="related-list" class="chip-list"></div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.bindDetailEvents();
|
|
await this.loadRelations(tag.mrf);
|
|
}
|
|
|
|
private bindDetailEvents(): void {
|
|
const closeBtn = this.panelEl.querySelector('.detail-close');
|
|
closeBtn?.addEventListener('click', () => this.close());
|
|
|
|
const mrfEl = this.panelEl.querySelector('.detail-mrf');
|
|
mrfEl?.addEventListener('click', () => {
|
|
const mrf = (mrfEl as HTMLElement).dataset.mrf;
|
|
if (mrf) copyMrf(mrf);
|
|
});
|
|
|
|
delegateEvent<MouseEvent>(this.panelEl, '.tag-chip', 'click', (_, target) => {
|
|
const mrf = target.dataset.mrf;
|
|
if (mrf) this.showDetail(mrf);
|
|
});
|
|
}
|
|
|
|
private async loadRelations(mrf: string): Promise<void> {
|
|
const [children, related] = await Promise.all([
|
|
fetchChildren(mrf),
|
|
fetchRelated(mrf)
|
|
]);
|
|
|
|
const childrenSection = this.panelEl.querySelector('#children-section') as HTMLElement;
|
|
const childrenList = this.panelEl.querySelector('#children-list') as HTMLElement;
|
|
if (children.length > 0) {
|
|
childrenSection.style.display = 'block';
|
|
childrenList.innerHTML = children.map(c => {
|
|
const label = c.name_es || c.alias || c.ref || c.mrf.slice(0, 8);
|
|
return `<span class="tag-chip" data-mrf="${c.mrf}">${label}</span>`;
|
|
}).join('');
|
|
}
|
|
|
|
const relatedSection = this.panelEl.querySelector('#related-section') as HTMLElement;
|
|
const relatedList = this.panelEl.querySelector('#related-list') as HTMLElement;
|
|
if (related.length > 0) {
|
|
relatedSection.style.display = 'block';
|
|
relatedList.innerHTML = related.map(r => {
|
|
const label = r.name_es || r.alias || r.ref || r.mrf.slice(0, 8);
|
|
return `<span class="tag-chip" data-mrf="${r.mrf}" title="${r.edge_type}">${label}</span>`;
|
|
}).join('');
|
|
}
|
|
}
|
|
}
|