Dodock met à disposition deux primitives réutilisables côté client pour construire des interfaces de configuration soignées : frappe.ui.SettingsDialog pour les boîtes de dialogue à onglets et ControlSwitch pour les interrupteurs à bascule.
frappe.ui.SettingsDialog génère une boîte de dialogue modale composée de deux zones :
Elle hérite de frappe.ui.Dialog, ce qui lui confère gratuitement la gestion du cycle de vie modal (fond semi-transparent, ouverture/fermeture, touche Échap, pile des dialogues ouverts).
new frappe.ui.SettingsDialog({
title: __("Paramètres"),
default_tab: "preferences",
tabs: [
{
group: __("Configuration"),
items: [
{
id: "preferences",
label: __("Préférences"),
icon: "sliders-vertical",
title: __("Préférences"),
description: __("Gérez vos préférences"),
fields: [
{
fieldname: "theme",
fieldtype: "Select",
label: __("Thème"),
options: "Clair\nSombre",
},
],
actions: [
{
label: __("Enregistrer"),
primary: true,
click(panel) {
const values = panel.get_values(); // null si un champ requis est vide
if (!values) return;
// traiter values.theme …
},
},
],
},
],
},
],
}).show();
| Clé (racine) | Type | Description |
|---|---|---|
title | string | Titre affiché en haut de la boîte de dialogue |
default_tab | string | id de l'onglet actif à l'ouverture |
tabs | Array | Liste des groupes d'onglets (voir ci-dessous) |
Groupe d'onglets (tabs[].group) : libellé de section dans la sidebar.
Élément d'onglet (tabs[].items[]) :
| Clé | Type | Description |
|---|---|---|
id | string | Identifiant unique de l'onglet |
label | string | Libellé affiché dans la sidebar |
icon | string | Nom d'icône Frappe (ex. "sliders-vertical") |
title | string | Titre affiché en en-tête du panneau |
description | string | Sous-titre affiché sous le titre |
fields | Array | Champs FieldGroup (même format que frappe.ui.Dialog) |
actions | Array | Boutons d'action (voir ci-dessous) |
render(panel) | function | Rendu personnalisé dans panel.body |
Action (actions[]) :
| Clé | Type | Description |
|---|---|---|
label | string | Libellé du bouton |
primary | boolean | Style bouton principal si true |
click(panel) | function | Appelé au clic ; reçoit le contrôleur SettingsDialogPanel |
SettingsDialogPanel)Chaque onglet est piloté par un contrôleur SettingsDialogPanel transmis aux fonctions render et actions[].click. Il expose :
| Méthode | Description |
|---|---|
panel.get_values() | Retourne les valeurs du formulaire, ou null si un champ requis est vide |
panel.get_value(fieldname) | Retourne la valeur d'un champ |
panel.set_values(values) | Injecte des valeurs dans le formulaire |
panel.set_view(spec) | Remplace en une fois l'en-tête, les actions et le formulaire |
panel.refresh() | Réinitialise le panneau à la vue définie dans la config de l'onglet |
panel.set_header(title, description) | Met à jour uniquement l'en-tête |
panel.add_fields(fields) | Ajoute des champs au FieldGroup existant |
panel.body | Élément jQuery du corps du panneau (disponible dans render) |
panel.set_view(spec) permet d'implémenter une navigation à plusieurs niveaux (liste → formulaire → confirmation) sans coordination externe. Chaque appel remplace l'ensemble de la vue (en-tête + actions + formulaire) et les actions de la nouvelle vue lisent toujours le formulaire qui leur est colocalisé.
render(panel) {
// Vue « liste »
panel.set_view({
title: __("Choisir un profil"),
description: "",
actions: [
{
label: __("Nouveau profil"),
primary: true,
click(p) {
p.set_view({
title: __("Nouveau profil"),
fields: [{ fieldname: "nom", fieldtype: "Data", label: __("Nom"), reqd: 1 }],
actions: [
{
label: __("Enregistrer"),
primary: true,
click(p2) {
const v = p2.get_values();
if (!v) return;
// sauvegarder v.nom …
p2.refresh(); // retour à la vue initiale
},
},
],
});
},
},
],
render(p) {
p.body.append("<p>Aucun profil enregistré.</p>");
},
});
},
fieldtype: "Switch")Le contrôle Switch est un interrupteur à bascule (toggle) visuel qui remplace un simple bouton à cocher. Il peut être utilisé partout où un contrôle est instancié : FieldGroup, Dialog, formulaire de script client.
ControlCheck — conserve un vrai <input type="checkbox"> et toute la sémantique 0/1 (get_values() retourne 1 ou 0).reqd fonctionnent identiquement à ControlCheck.role="switch" sur la piste, libellé masqué visuellement via la classe sr-only (clip complet : clip, clip-path, overflow).<label> — cliquer n'importe où sur la ligne bascule l'état.:has(input:checked) pour les états cochés, survolés, ciblés et désactivés.const d = new frappe.ui.Dialog({
title: __("Options"),
fields: [
{
fieldname: "notifications_actives",
fieldtype: "Switch",
label: __("Activer les notifications"),
},
{
fieldname: "mode_sombre",
fieldtype: "Switch",
label: __("Mode sombre"),
default: 1,
},
],
primary_action_label: __("Enregistrer"),
primary_action(values) {
// values.notifications_actives → 1 ou 0
// values.mode_sombre → 1 ou 0
d.hide();
},
});
d.show();
new frappe.ui.SettingsDialog({
title: __("Préférences"),
default_tab: "affichage",
tabs: [
{
group: __("Interface"),
items: [
{
id: "affichage",
label: __("Affichage"),
icon: "eye",
title: __("Affichage"),
fields: [
{
fieldname: "compact",
fieldtype: "Switch",
label: __("Vue compacte"),
},
],
actions: [
{
label: __("Appliquer"),
primary: true,
click(panel) {
const v = panel.get_values();
if (!v) return;
frappe.show_alert(v.compact ? __("Vue compacte activée") : __("Vue standard"));
},
},
],
},
],
},
],
}).show();
| Critère | Check | Switch |
|---|---|---|
| Valeur retournée | 1 / 0 | 1 / 0 |
| Rendu | Case à cocher native | Piste coulissante |
| Accessibilité | type="checkbox" | type="checkbox" + role="switch" |
| Cible de clic | Case uniquement | Toute la ligne |
| Interchangeable | Oui | Oui (même API) |