KEPLIN Docs

Eventi e SDK

Gli eventi dei widget e della schermata, l'editor di codice, le azioni predefinite e l'SDK keplin in TypeScript — dati, widget, navigazione, sessione, modali e workflow.

C'è tanta schermata che si fa senza scrivere una riga: collegare un datastore, trascinare widget, puntare un pulsante a un'altra schermata. Ma prima o poi arriva il "quando succede questo, fai quello" — salvare e tornare indietro, ricaricare una tabella dopo un filtro, confermare prima di eliminare, aprire un modale e usare ciò che ha restituito.

È a questo che servono gli eventi: punti della schermata dove gira codice tuo, scritto in TypeScript, con un SDK — l'oggetto keplin — che dà accesso a tutto ciò che la schermata ha.

Dove sono gli eventi

Nell'inspector, l'ultima categoria di un widget (e della schermata stessa) si chiama Eventi. Ha una riga per ogni evento disponibile, e in ogni riga:

  • un punto a sinistra: pieno quando quell'evento ha già del codice, vuoto quando non ne ha;
  • un pulsante … a destra, che apre l'editor.

La categoria Eventi di un Pulsante: un punto pieno segna gli eventi che hanno già codice; il … apre l'editor.
La categoria Eventi di un Pulsante: un punto pieno segna gli eventi che hanno già codice; il … apre l'editor.

I nomi degli eventi non sono tradotti — sono gli stessi in qualsiasi lingua (onClick, onRowClick, onLoad), perché sono anche i nomi che appaiono nel codice e nei registri del Radar.

Gli eventi della schermata

Clicca su una zona vuota del canvas perché l'inspector mostri la schermata. La categoria Eventi ne ha tre:

Evento Quando scatta Per che cosa
onLoad Una volta, quando la schermata si apre. Preparare lo stato, caricare cose che i datastore non caricano, dare il benvenuto.
onParamsChange Ogni volta che i parametri del percorso cambiano — e non alla prima apertura. Reagire a un cambio di record senza riaprire la schermata.
onUnload Quando la schermata se ne va. Pulire lo stato, salvare bozze.

Gli eventi della schermata stessa — onLoad, onParamsChange e onUnload — nell'inspector senza selezione.
Gli eventi della schermata stessa — onLoad, onParamsChange e onUnload — nell'inspector senza selezione.

Gli eventi di ogni widget

Ogni tipo di widget dichiara i suoi. Oltre al nome, conta il payload — i dati che l'evento porta con sé, e che il codice legge in keplin.event.

Campi di modulo

Widget Eventi keplin.event
Casella di testo, Area di testo, Numero, Sì/No, Dropdown, Data, Colore onChange { value }
File onChange, onUpload onUpload: { file, name }

Azioni e navigazione

Widget Eventi keplin.event
Pulsante onClick {}
Pulsante con menu onClick, onMenuItem onMenuItem: { id, label }
Link onClick {}
Esporta onExport, onDataLoaded onExport: { rows, filename, truncated }

Struttura e contenuto

Widget Eventi keplin.event
Schede onTabChange { tab }
Report onLoad { report }

Widget di dati

Widget Eventi keplin.event
Tabella onRowClick, onRowDoubleClick, onSelectionChange, onDataLoaded { row, index } · onSelectionChange: { row, rows }
Elenco, Card onRowClick, onDataLoaded { row, index }
Grafico onClick { name, seriesName, value, dataIndex }
KPI onClick, onDataLoaded { value, indicatorId }

Board e pianificazione

Widget Eventi keplin.event
Kanban onCardClick, onCardCreate, onCardMoved, onDataLoaded { row } · { column } · { row, from, to, index }
Calendario onEventClick, onDayClick, onRangeSelect, onRangeChange, onDataLoaded { row } · { date } · { start, end } · { start, end, view }
Gantt onBarClick, onEmptyClick, onDataLoaded { row } · { date }

Processi

Widget Eventi keplin.event
Stato del processo onDecide { task, outcome }
Le mie attività onOpen, onDecide { task, screenId }

Nota

I widget programmati da te (quelli che appaiono nella palette in Custom) dichiarano i propri eventi, e appaiono qui come tutti gli altri.

L'editor di codice

Il pulsante … di un evento apre l'editor in un modale. Il titolo dice dove sei: l'id del widget (o il nome della schermata) e il nome dell'evento — w_fic_sav1 · onClick.

L'editor dell'evento onClick del pulsante Salva: il codice salva il datastore e, se è andato bene, avvisa e torna all'elenco.
L'editor dell'evento onClick del pulsante Salva: il codice salva il datastore e, se è andato bene, avvisa e torna all'elenco.

Pulsante Che cosa fa
Inserisci azione Scrive per te il codice di un'attività comune (qui sotto).
Rimuovi handler Elimina il codice di questo evento. Il punto torna vuoto.
Annulla Chiude senza salvare.
Salva Verifica e salva.

L'editor ha suggerimenti mentre scrivi (Ctrl+Spazio): tutto il keplin è dichiarato, con i tipi giusti — e, meglio ancora, gli id dei widget di questa schermata sono lì dentro. Scrivere keplin.widgets.get(" mostra l' elenco dei widget della schermata, e un id che non esiste viene segnalato come errore prima che tu salvi.

Attenzione

Al salvataggio, il codice viene compilato. Se non è eseguibile, la piattaforma lo rifiuta — L'evento non è stato salvato: il codice non è eseguibile — e il modale resta aperto perché tu corregga. Una schermata non resta mai con codice rotto lì dentro.

Le azioni predefinite

Inserisci azione apre un elenco delle attività più comuni. Ne scegli una e il codice viene scritto in fondo a ciò che c'è già, già con i nomi reali della tua schermata — il primo datastore di record, la prima tabella, la prima casella di testo.

Inserisci azione — le azioni predefinite che scrivono il codice per te, dai datastore ai workflow.
Inserisci azione — le azioni predefinite che scrivono il codice per te, dai datastore ai workflow.

Azione Che cosa scrive
Salva datastore Valida e salva il record, con avviso di successo.
Ricarica datastore Rilegge i dati di un datastore.
Filtra datastore (per testo) Legge il testo di una casella e lo applica come filtro.
Naviga verso una schermata Salta a un altro percorso dell'app.
Ricarica una tabella Aggiorna i dati di un widget di tabella.
Filtra la tabella con il testo di un campo Il filtro interattivo classico.
Mostra/nascondi un widget Alterna la visibilità di un widget.
Conferma e mostra un toast Chiede prima di agire e avvisa alla fine.
Avvia un workflow Mette in moto un processo sul record attuale.
Visualizza e completa le attività Elenca le attività di chi sta usando l'app e ne decide una.
Invia un segnale a un workflow Sveglia i processi che erano in attesa.
Esci Esce dall'app.

Il codice inserito è un punto di partenza: diventa tuo, ed è lì per essere modificato. Non viene generato di nuovo.

Il codice si salva formattato: diviso in righe, indentato e con gli spazi giusti, senza cambiare ciò che fa. Vale per tutti gli editor di codice della piattaforma, e per il codice che gli agenti salvano via MCP. Formatta il codice, nel menu dell'editor (tasto destro) o con Shift+Alt+F, formatta subito, e Ctrl+Z lo annulla. Un codice con errori di sintassi non si formatta: resta com'è finché non lo correggi.

Come gira il codice

Ogni evento è una funzione asincrona che riceve una cosa sola: il keplin. Da lì escono tre conseguenze pratiche:

  • await funziona in cima al codice. Non serve avvolgere nulla.
  • return esce dall'evento. È il modo normale di rinunciare a metà (per esempio, quando una conferma è stata rifiutata).
  • Non ci sono parametri. Il contesto arriva dentro il keplin stesso: keplin.event porta il payload e keplin.ctx dice dove sei (ctx.widget è il widget che ha fatto scattare l'evento — null negli eventi di schermata —, ctx.event è il nome dell'evento e ctx.screen la schermata).

Finché il codice di un pulsante non finisce, il pulsante mostra tre puntini animati: chi sta usando l'app capisce che sta lavorando. Se il codice esplode, la schermata non si rompe: appare un avviso e l'errore resta registrato nel Radar, con la schermata, il widget e l'evento in cui è successo.

L'SDK keplin

Tutto ciò che il codice può fare sta sotto keplin. Queste sono le aree:

Area Per che cosa
keplin.event / keplin.ctx Il payload dell'evento e il contesto in cui sta girando.
keplin.widgets Parlare con i widget della schermata.
keplin.data I datastore: leggere, scrivere, filtrare, salvare.
keplin.nav Navigare e leggere i parametri del percorso.
keplin.ui Avvisi, conferme e modali.
keplin.state Stato condiviso fra schermate.
keplin.session Chi sta usando l'app, e che cosa può fare.
keplin.auth Login, registrazione e recupero della password (schermate di sistema).
keplin.i18n Frasi tradotte, la lingua in uso e il cambio di lingua.
keplin.storage Preferenze salvate sul dispositivo.
keplin.api Chiamare le API dell'app direttamente.
keplin.reports Aprire e scaricare report.
keplin.workflow Avviare processi, elencare e completare attività.

I widget

keplin.widgets.get("id") restituisce l'handle di un widget. Tutti gli handle hanno la stessa base:

const w = keplin.widgets.get("w_fic_tel1");
w.show();               // mostrare
w.hide();               // nascondere
w.setEnabled(false);    // disattivare
w.set("label", "Cellulare");   // cambiare qualsiasi proprietà dell'inspector
w.get("label");         // leggere il valore effettivo
w.reset();              // dimenticare le modifiche fatte in esecuzione

E poi ogni famiglia aggiunge ciò che le è proprio:

Famiglia Che cosa aggiunge
Campi di modulo getValue(), setValue(v), validate(), error
Widget di dati (Tabella, Elenco, Card, Grafico, KPI, Kanban, Calendario, Gantt) rows, total, refresh(), setFilter(where), setSort(sort)
Tabella selectedRow, selectedRows, clearSelection()
KPI value, values, valueOf(indicadorId)
Kanban columns, moveCard(id, coluna, índice?)
Calendario view, start, end, goTo(data), setView(vista)
Gantt zoom, setZoom(z)
Schede activeTab, tab("id") — e, sulla scheda, activate(), show(), hide(), setEnabled()
Etichetta / Pulsante / Link / Breadcrumb setText(t) / setLabel(t)
Markdown setContent(md)
Pagina esterna setUrl(url), reload()
Esporta export()
Report url, download()

Nota

I suggerimenti dell'editor offrono tutti i verbi di tutte le famiglie, perché l'editor non sa in anticipo che widget sia quell'id. In esecuzione esistono solo quelli del tipo reale — moveCard su un Pulsante non fa nulla di utile.

I dati

keplin.data.store("nome") restituisce un datastore della schermata per nome (vedi Datastore e dati).

In un datastore di record:

const conta = keplin.data.store("conta");
conta.get("nome");                 // leggere un campo
conta.set("estado", "ativo");      // scrivere un campo (resta da salvare)
conta.record();                    // il record intero
conta.isDirty();                   // ci sono modifiche da salvare?
conta.reset();                     // buttare via le modifiche
const ok = await conta.save();     // valida e salva; true se ha salvato

In un datastore di elenco:

const contas = keplin.data.store("contas");
contas.rows();                     // le righe caricate
contas.total();                    // il totale (quando il server lo dà)
await contas.reload();             // rileggere
contas.setWhere({ estado: { eq: "ativo" } });   // filtro extra; null pulisce
contas.setSort([{ field: "nome", direction: "ASC" }]);
contas.goToPage(2);

In entrambi, status() dice a che punto è il caricamento (idle, loading, ready, error).

reload() restituisce una promessa: con await, le righe nuove sono già in rows(). In un elenco senza Carica automaticamente, è il reload() a farlo leggere.

keplin.nav.go("/ficha-de-conta/17");   // andare a un percorso (con parametri)
keplin.nav.back();                      // tornare indietro
keplin.nav.params;                      // i parametri della schermata attuale, per nome

Avvisi, conferme e modali

keplin.ui.toast("Salvato.", "success");        // "success" | "error" | "info"
const ok = await keplin.ui.confirm("Eliminare il record?");
if (!ok) return;

La conferma è una finestra di dialogo con il tema dell'app — mai la scatola grigia del browser.

Stato, sessione e preferenze

keplin.state.set("filtroContas", "activas");   // vive finché la scheda resta aperta
keplin.state.get("filtroContas");
keplin.state.remove("filtroContas");

keplin.session.user;              // { id, username, name } — null nelle schermate pubbliche
keplin.session.roles;             // i ruoli di chi sta usando l'app
keplin.session.can("contas.editar");   // ha questa azione? (Impostazioni ▸ Autorizzazioni)
await keplin.session.logout();

keplin.storage.set("colunasContas", ["nome", "cidade"]);   // resta sul dispositivo
keplin.storage.get("colunasContas");

Suggerimento

Per decidere che cosa qualcuno può fare, chiedi keplin.session.can("...") e non hasRole("gestor"). Le azioni si dichiarano in Impostazioni ▸ Autorizzazioni e sopravvivono alle riorganizzazioni dei ruoli; il nome di un ruolo, no.

Le API e i report

const linhas = await keplin.api.query("contas", { estado: "ativo" }, ["id", "nome"]);
await keplin.api.mutate("criarConta", { nome: "Nova" }, ["id"]);

keplin.reports.open("Contactos da conta", { contaId: 17 });
keplin.reports.download("Contactos da conta", { contaId: 17 }, "xlsx");

In un argomento di enum, passa il valore così come è salvato ({ estado: "Em curso" }). Un argomento JSON accetta liste e oggetti; in una query SQL verso SQL Server, SQLite o Oracle arrivano come testo JSON, che si legge con OPENJSON, json_each o JSON_TABLE.

I valori possono andare come li conservano i campi: il testo «12» di una casella di testo va bene in un argomento numerico, un numero va bene in un argomento di testo, «true» va bene in un booleano, e una data (Date) va in ISO.

In una query, l'elenco dei campi è obbligatorio — è lui a dire che cosa vuoi portare.

Attenzione

keplin.reports.open apre una scheda nuova e perciò non può stare dietro a un await: fuori dal gesto dell'utente, il browser blocca la finestra. Apri prima, fai il resto dopo.

Workflow

const registo = keplin.data.store("oportunidade").get("id");
await keplin.workflow.start("wf_aprovacao", registo);

const tarefas = await keplin.workflow.tasks();
await keplin.workflow.complete(tarefas[0].id, "aprovar");

const { woken } = await keplin.workflow.signal("documento-recebido", registo);

Frasi tradotte

keplin.i18n.t("{n} account attivi", { n: linhas.length });
keplin.i18n.locale;
keplin.i18n.available;
await keplin.i18n.setLocale("en");

Schermate modali

Una schermata di Keplin non è modale perché è stata aperta in un certo modo — è modale perché è stata configurata così. La decisione sta nell'inspector della schermata, nella categoria Presentazione:

La sezione Presentazione della schermata: è qui che una schermata diventa Modale (al centro) o Pannello laterale (a destra).
La sezione Presentazione della schermata: è qui che una schermata diventa Modale (al centro) o Pannello laterale (a destra).

Opzione Che cosa fa
Modalità Schermata (una pagina normale), Modale (al centro) o Pannello laterale (a destra). La modalità Barra (area widget) non si apre sopra: è il contenuto di un'area widget della Navigazione dell'app.
Larghezza (px) / Altezza (px) La dimensione del modale. Il pannello laterale usa tutta l'altezza.
Pulsante di chiusura Mostra la × nell'angolo.
Clic fuori chiude / Esc chiude Le due uscite abituali. Con un elenco o un popover aperti, il primo clic fuori o Esc chiude solo quello.
Aggiorna la schermata dietro alla chiusura Alla chiusura, i datastore della schermata che l'ha chiamata rileggono. Ciò che è scritto e non ancora salvato in un modulo di quella schermata resta com'è.

Con modifiche non salvate, chiudere con la ×, con Esc o con un clic fuori chiede prima conferma. Contano i campi collegati a un datastore di record che la persona ha modificato e non ha ancora salvato; scrivere e cancellare non conta. Chiudere da codice, con keplin.ui.closeModal, non chiede nulla.

Quale dei due? Il Modale (al centro) serve per schede semplici — una voce di catalogo con descrizione e stato, una conferma. Il Pannello laterale (a destra) si apre a destra a tutta altezza ed è la scelta per schede con molti campi, pannelli e liste dentro (un cliente con i suoi indirizzi e contatti): la stessa scheda in un modale centrale risulta piccola e stretta, e la lista dietro resta visibile a fianco. La Larghezza (px) è quella del pannello.

Il suggerimento della sezione stessa riassume: Si apre SOPRA la schermata che lo chiama (Link, eventi o keplin.ui.openModal). Escluso dalla navigazione diretta.

Aprire e chiudere da codice

const resultado = await keplin.ui.openModal("/nova-conta", { setor: "banca" });
if (resultado) {
  keplin.data.store("contas").reload();
}
  • openModal riceve il percorso (o l'id) della schermata e, facoltativamente, i parametri.
  • La promise si risolve solo quando il modale si chiude, e porta il valore che il modale ha restituito.
  • Dentro il modale, keplin.ui.closeModal(valor) chiude e restituisce quel valore.
  • I modali si impilano: un modale può aprirne un altro. Con più pannelli laterali aperti, ogni pannello sottostante resta visibile in una striscia a sinistra del pannello sopra; con Clic fuori chiude attivo, un clic su quella striscia chiude il pannello sopra.
  • Chiedere la schermata che è già in cima, con gli stessi parametri, non ne apre un'altra: la chiamata restituisce la promise di quella aperta. È ciò che impedisce a un doppio clic di aprire due schede uguali.

Nota

keplin.nav.go("/rota") verso una schermata configurata come Modale (al centro) o Pannello laterale (a destra) la apre come modale invece di navigare. È voluto: una schermata modale non ha un indirizzo proprio nella navigazione.

Ricette

Salvare e tornare indietro (l'onClick del pulsante Salva della Scheda Account):

const ok = await keplin.data.store("conta").save();
if (ok) {
  keplin.ui.toast("Account salvato");
  keplin.nav.go("/contas");
}

Aprire la scheda della riga cliccata (onRowClick di una Tabella):

keplin.nav.go(`/ficha-de-conta/${keplin.event.row["id"]}`);

Filtrare una tabella con una casella di testo (onChange della casella — sostituisci gli id con quelli della tua schermata):

const texto = keplin.widgets.get("w_pesquisa").getValue();
keplin.widgets.get("w_cta_tab1").setFilter(texto ? { nome: { contains: texto } } : null);

Confermare prima di un'azione distruttiva (onClick di un pulsante):

if (!(await keplin.ui.confirm("Eliminare questo account?"))) return;

Nascondere un pulsante a chi non può (onLoad della schermata):

if (!keplin.session.can("contas.eliminar")) {
  keplin.widgets.get("w_apagar").hide();
}

Perché no…?

  • Perché non mi lascia salvare l'evento? Il codice non compila. Il messaggio è L'evento non è stato salvato: il codice non è eseguibile — correggi e salva.
  • Perché keplin.widgets.get("...") dà errore? L'id non esiste in questa schermata. Verificalo in cima all'inspector, con il widget selezionato; e ricordati che ogni dispositivo è un albero proprio (vedi Layout e design per dispositivo).
  • Perché l'onParamsChange non è scattato all'apertura? È voluto: scatta solo sui cambiamenti. Per l'avvio, usa l'onLoad.
  • Perché il modale non restituisce nulla? O la schermata di destinazione non esiste, oppure chi sta usando l'app non ha il permesso di aprirla — in entrambi i casi la promise si risolve senza valore. Verifica il percorso e i permessi.
  • Perché non apre la finestra del report? Hai messo l'open dopo un await. Apri prima.
  • Perché il mio evento sembra non girare? Guarda il Radar: gli errori del codice degli eventi restano lì, con schermata, widget ed evento — e il Radar ti porta direttamente all'editor di quell'evento.