WoodyLabBuild with UIMS

Documentazione · src/engine/resolver

I resolver

Undici file, un contratto solo. Un blocco entra, esce un descrittore — o niente, che è una risposta legittima.

Il contratto: un descrittore, sempre lo stesso

Ogni resolver riceve (block, context) e restituisce la stessa forma: Component (chi disegna), ui (le props strutturali — classi, tag, attributi), slots (i figli già proiettati, per apertura), items (le righe, quando il blocco ne ha), data (tutto il dominio).

La separazione fra data e le chiavi strutturali non è ordine per l’ordine. È ciò che permette a un componente di essere scritto senza sapere quale blocco lo userà: legge da data quello che gli serve per nome, e le chiavi strutturali le consuma il primitivo comune. Mescolare le due cose — mettere il testo dentro ui — funzionerebbe per un componente e romperebbe la sostituibilità di tutti.

resolver/contract.ts

la forma del descrittore, e il tipo di una funzione resolver

Il descrittore, unica forma che il renderer sa disegnare

interface UIMSDescriptor {
  Component?: any      // assente per le composizioni, come `section`
  ui?: UIMSUi          // tag e props di sistema
  slots?: Record<string, any>
  items?: any[]        // accordion, tabs, swiper: sotto-descrittori
  data?: Record<string, any>   // l'unico canale per i dati di dominio
}
resolver/index.ts

il registro: quale resolver per quale blockType

Il resolver di default fa quasi tutto

La maggior parte dei blocchi non ha un resolver proprio e passa da resolveViewMode: trova il viewMode per nome nel tema attivo (con fallback su uims), lo fonde con l’override del tema, ne ricava componente, classi, tag e slot, proietta i figli nelle aperture giuste e mette tutto il resto in data.

Un resolver dedicato serve solo quando il blocco ha bisogno di qualcosa che non è nel blocco stesso: un documento da dereferenziare, dei record da leggere, un form da iniettare. Sono i casi delle sezioni successive — sette in tutto su una trentina di tipi di blocco.

resolver/resolveViewMode.ts

il default: viewMode → componente, classi, slot; il resto in data

Una sezione non è un componente

Una sezione è una composizione: sezione contiene contenitore contiene contenuto. Non ha un componente proprio, e infatti il suo descrittore non porta Component — è così che il renderer distingue le due cose senza un flag.

La conseguenza è che una sezione non si può disegnare «da sola»: è una struttura di tre livelli, ognuno con il suo viewMode e le sue classi. Ed è il motivo per cui i seeder del sito costruiscono le pagine con un helper sec({ section, container, content }) invece che con un blocco piatto.

resolver/sectionResolver.ts

la composizione a tre livelli; nessun Component

resolver/sectionRefResolver.ts

dereferenzia la sezione e restituisce ciò che il resolver di sopra ha prodotto

I riferimenti sono delega pura

Un blocco reference non ha una forma propria: punta a un documento, e ciò che va disegnato è quel documento con la forma che il suo resolver gli dà. Il resolver quindi non fa altro che risolvere il riferimento e restituire il descrittore altrui.

La lettura è deduplicata per richiesta e messa in cache con il tag del documento: dieci blocchi che puntano allo stesso record leggono una volta sola, e la modifica di quel record invalida esattamente quelle pagine. È il pezzo di DAL che rende sostenibile una pagina composta di riferimenti.

resolver/referenceResolver.ts

risolve il documento riferito e restituisce il suo descrittore

Le viste: una sezione con righe

Una vista è una sezione con righe: la tab Data della sezione dichiara cosa caricare, il suo container impagina, il contenuto ripete con repeatFor e legge con dataPathValue. Inline nel layout è usa e getta; come documento sections, citato da sectionRef, è riusabile. Con params e uno state diventa viva: il client chiede al server di rifare la sola sezione.

Un solo passo di binding per tutto: bindData espande le copie di repeatFor e legge i percorsi, dentro una copia relativi all’elemento; withOwnData carica le righe di una sezione e lega il suo contenuto.

La navigazione ha un caso a parte perché non ha bisogno di mappatura: le voci del menu arrivano già nella forma giusta e finiscono in data.navigation, che è quello che il componente legge.

resolver/sectionResolver.ts

le righe della sezione, il binding del contenuto, il wrapper vivo

data/withOwnData.ts

carica le righe di un elemento e lega il suo contenuto

data/bindData.ts

il passo di binding: repeatFor ripete, dataPathValue legge

resolver/navigationResolver.ts

le voci del menu in data.navigation

I form

Il blocco form è un contenitore con dentro il form riferito: i dati operativi (dove si invia, cosa si fa dopo) restano in data, gli input finiscono negli slot come figli. Il componente Form monta il contesto e i controlli si registrano da soli.

La forma dei singoli input viene dai viewMode come per qualunque altro blocco: un campo di testo si può ridisegnare senza toccare la logica di invio, che sta altrove.

resolver/formResolver.ts

il form riferito iniettato nel contenitore; dati operativi in data

Le reazioni risolte prima del client

Sull’istanza di un blocco si dichiara «quando succede X, fai Y»: un trigger e una conseguenza. La conseguenza spesso nomina una ricetta di animazione, che è un dato — vive nello store, non nel codice.

resolveReactions traduce quelle dichiarazioni nella forma che il nodo reattivo applicherà sul client, andando a prendere le ricette dal registro. Una reazione la cui ricetta non esiste viene lasciata cadere in silenzio: un’animazione mancante non è una ragione per non disegnare la pagina.

resolver/resolveReactions.ts

dalle dichiarazioni dell’istanza alla forma che applica UIMSReactive