WoodyLabBuild with UIMS

Presentazione

Un video guida le slide

Premi Avvia: parte il primo video con l’audio, i testi compaiono ai loro tempi, e a fine video si passa alla slide dopo. Tutto dichiarato nei dati: sotto trovi il JSON e la spiegazione.

Prima slide

Il sottotitolo arriva da sinistra

E il testo dal basso, ultimo dei tre. Cambia slide e torna qui: dovrebbero rientrare da capo.

Comparsa a 2 s

Comparsa a 4 s

Seconda slide

Stessa famiglia, stesse direzioni

Le tre reazioni sono identiche a quelle della prima slide, tranne per l’indice che aspettano.

Comparsa a 2 s

Comparsa a 4 s

Com’è fatta

Tutto dichiarato, niente scritto a mano

Un bottone, uno slider, due video, quattro testi a comparsa. Nessun componente scritto per questa pagina: solo blocchi che il pacchetto porta già, e le loro reazioni.

La regola in una riga: il video decide. Il bottone «Avvia» fa partire il primo video; la partenza vera del video, l’evento play del tag, accende i testi a comparsa della sua slide ai loro tempi; la fine vera del video, l’evento ended, chiama la slide dopo, che monta il suo video e ricomincia. Lo slider non ha autoplay: non decide niente, esegue.

Sotto, due tab. «JSON» stampa la sezione della presentazione così com’è dichiarata nel seed: lo stesso oggetto che vedi qui sopra in funzione, Payload ci aggiunge solo gli id delle righe. «Descrizione» racconta ogni pezzo una volta sola: la seconda slide e il secondo video sono identici al primo, cambiano l’indice e il file.

La sezione

Una sezione con il contenitore ContainerGrid2: due colonne da md in su, una sola sotto. Nell’intestazione del contenitore sta il bottone «Avvia»; nel contenuto, lo slider e la colonna dei video. ContainerSplit non andava bene: dichiara le colonne solo da md, e sotto la colonna si allarga quanto lo slider.

Il bottone «Avvia»

Un blocco action con actionType: "none", cioè un bottone che non naviga. Nel suo uiState.events una riga: on:click → ui:play, con scope: "#video-1". Niente debounce: il play deve avvenire dentro il click, perché i browser fanno partire l’audio solo dopo un gesto sulla pagina. Da quel gesto in poi anche gli altri video partono da soli, con il sonoro.

Sta nel gruppo events e non in effects perché il blocco action passa al suo bottone lo uiState grezzo, non le reazioni risolte: un effects su un bottone non arriva (issue #268). Da events il comando è un dispatch: nessun handler lo prende, il ponte del kernel lo consegna a chi lo ascolta.

Lo slider

Un blocco swiper con id: "testSwiper" e uiState: { store: true, nodeId: "testSwiper" }: lo stesso nome fa due cose. Con store: true lo slider scrive la slide corrente nello store, nella chiave testSwiper:index, e ogni figlio delle slide la può leggere con scope: "owner". Con quell’id risponde ai comandi indirizzati ui:next, ui:prev, ui:goTo che portano scope: "#testSwiper".

autoplay.enabled: false: lo slider non cambia slide da solo, lo fa il video. loop: true e due slide: la fine del secondo video riporta alla prima. keyboard.enabled: true: chi presenta può andare avanti e indietro con le frecce.

La slide e i suoi testi

Ogni slide ha tre testi, titolo, sottotitolo e corpo, con un binding sulla classe: scope: "owner", path: "index", op: "eq", value: "{{self.itemValue}}". {{self.itemValue}} è l’indice che lo slider fa scendere a ogni figlio, quindi la condizione è identica in tutte le slide e non nomina nessuno. Quando la slide è corrente la classe è quella di arrivo (us-fadeEnd), altrimenti quella di partenza (us-fadeTStart, us-fadeLStart, us-fadeBStart): entrano da tre direzioni, e tornando sulla slide rientrano.

I testi a comparsa

Due testi Eyebrow per slide, «Comparsa a 2 s» e «Comparsa a 4 s». La visibilità è un binding sulla classe, come per gli altri testi, ma la condizione legge una chiave propria: slide0-comparsa-2s (la comparsa a 2 s della prima slide). Il tempo sta nelle reazioni, gruppo effects.

Prima reazione: on:stateChange sulla chiave slide0-video-partito, op: "truthy", con debounce: 2000 (o 4000): quando il video della slide è partito da 2 secondi, ui:set scrive slide0-comparsa-2s: true. Seconda reazione: on:stateChange con scope: "owner", path: "index", op: "neq": quando la slide non è più corrente, la stessa chiave torna false, e al giro dopo la comparsa riparte. La comparsa a 2 s ha una terza reazione, sempre all’uscita della slide: azzera slide0-video-partito, così la partenza successiva del video è di nuovo una transizione da falso a vero.

debounce è «attesa con annullamento»: se il trigger scatta di nuovo durante l’attesa, si ricomincia a contare. È un campo di events ed effects, in millisecondi; vuoto o zero, la reazione parte subito.

Il video

Un blocco video per slide, nella colonna di destra, con id: "video-1". Fonte mediaUrl, muted: false perché vogliamo l’audio, controls: false perché qui il video è il metronomo e non un lettore, preload: "auto". Il viewMode Video è il tag <video> stesso: gli attributi finiscono sull’elemento che suona, e i trigger lo ascoltano.

Un binding hidden: scope: "#testSwiper", path: "index", op: "in", value: "0", con then: "false" ed else: "true". Il video esiste solo quando la sua slide è corrente: monta quando arriva, smonta quando se ne va, e smontare un video lo ferma. in e non eq perché il valore scritto nell’admin è testo e eq è stretto.

Tre reazioni, gruppo effects. on:mount → ui:play a #video-1, con debounce: 400, la durata della transizione dello slider: il nodo monta esattamente quando la slide diventa corrente, quindi «montato» vuol dire «slide corrente». on:mediaPlay → ui:set di slide0-video-partito: true: è la partenza vera, dall’evento del tag, e le comparse si agganciano a questa. on:mediaEnded → ui:next con scope: "#testSwiper": la fine vera del video chiama la slide dopo.

I tre verbi ui:play, ui:pause e ui:stop non sono handler: il video li ascolta dal kernel come lo slider ascolta ui:next, e risponde solo se il payload porta il suo scope. Un ui:play che il browser rifiuta, per esempio con l’audio prima di un gesto, viene detto una volta in console e non rompe niente.

Le chiavi dello store

Le chiavi le inventa il data entry: sono celle dello store, e nessuno le «decifra». Chi scrive usa payload.key, chi legge usa when.path, e i due nomi devono coincidere. Lo schema qui è slide<indice>-<cosa>-<stato>, con l’indice dello slider così com’è nello store, da zero: slide0-video-partito, slide0-comparsa-2s, slide0-comparsa-4s, e lo stesso con slide1- per la seconda. Niente punti nei nomi: un punto è un percorso dentro un oggetto.

Un refuso da una parte sola crea una cella che nessuno legge, e il testo resta nascosto senza che niente lo dica. Se una comparsa non compare, la prima cosa da guardare è che la chiave scritta e quella letta siano la stessa.

La sequenza

Caricamento: la prima slide è corrente, video-1 monta, il suo ui:play al montaggio viene rifiutato perché c’è l’audio e nessun gesto; niente si muove. Click su «Avvia»: ui:play a #video-1, il video parte, play scrive slide0-video-partito, le comparse arrivano a 2 e 4 secondi. Fine del video: ended → ui:next → lo slider va alla seconda slide; video-1 smonta, le sue comparse e la sua chiave si azzerano; video-2 monta e parte da solo, perché il gesto c’è già stato. Fine del secondo video: ui:next riporta alla prima, e si ricomincia.