Système de plugins
Vue d’ensemble
Section intitulée « Vue d’ensemble »Les plugins sont un concept propre au panneau d’administration. Ils s’exécutent dans le navigateur, au sein de l’admin React, et sont enregistrés là où vous le construisez. Rien dans cette page ne concerne le backend : un plugin ne peut pas ajouter de route, de callback ou de tâche cron. Pour cela, consultez Custom Functions, Entity Callbacks et Cron Jobs.
Les plugins constituent le mécanisme d’extension principal du panneau. Ils peuvent :
- Envelopper l’ensemble de l’application avec un provider (contexte, gestion d’état)
- Ajouter des actions sur la page d’accueil et des widgets
- Injecter des composants de vue de collection (barre d’outils, générateurs de colonnes)
- Ajouter des composants de formulaire (générateurs de champs, panneaux supplémentaires)
- Injecter ou modifier des collections dynamiquement
Interface des plugins
Section intitulée « Interface des plugins »interface RebasePlugin { key: string; // Unique identifier loading?: boolean; // Hold admin content until the plugin is ready
// UI contributions — a flat array, each entry naming its slot. // This replaced the old per-area objects (homePage, collectionView, form). slots?: SlotContribution[];
// HOC providers. `scope: "root"` wraps the whole admin below // RebaseContext; `scope: "form"` wraps each entity form / edit view. providers?: PluginProvider[];
// Behavioural (non-UI) hooks: collection modification and injection, // column reordering, navigation entries. hooks?: PluginHooks;
// Custom field rendering (e.g. data enhancement). fieldBuilder?: FieldBuilderConfig;
// Views added to the navigation automatically. views?: AppView[];
// onMount, onAuthStateChange, onUnmount — see below. lifecycle?: PluginLifecycle;}Chacun de ces éléments est optionnel à l’exception de key. La liste complète des noms d’emplacements (slots) se trouve sur la page Slots.
Cycle de vie
Section intitulée « Cycle de vie »onMount(context)s’exécute une fois, lorsque l’authentification est prête pour la première fois : un utilisateur est connecté, ou la connexion a été ignorée.onAuthStateChange(user)s’exécute à chaque changement d’utilisateur ultérieur : avec le nouvel utilisateur lors d’une connexion, avecnulllors d’une déconnexion. Le plugin reste monté dans les deux cas.onUnmount()s’exécute lorsque<Rebase>est démonté.
Depuis 0.25
lifecycle.onCollectionChange est déprécié. Rien ne l’a jamais appelé, en 0.24
comme avant. Un plugin qui le définit compile toujours, et <Rebase> avertit une seule fois par
plugin. Pour suivre les lignes d’une collection, abonnez-vous depuis onMount et
désabonnez-vous dans onUnmount :
import type { RebasePlugin } from "@rebasepro/cms-types";
let stopListening: (() => void) | undefined;
const openOrdersPlugin: RebasePlugin = { key: "open_orders", lifecycle: { onMount: (context) => { stopListening = context.data.collection("orders").listen( { where: { status: ["==", "open"] } }, ({ data }) => console.log(`${data.length} open orders`) ); }, onUnmount: () => stopListening?.() }};Utilisation des plugins
Section intitulée « Utilisation des plugins »Les plugins se placent sur <Rebase>, aux côtés du client. Tout ce qui se trouve en dessous — la navigation, les vues de collection, les formulaires — les lit à partir de là :
import { Rebase, useRebaseAuthController } from "@rebasepro/app";import { RebaseCMS, RebaseShell } from "@rebasepro/cms";import { useDataEnhancementPlugin } from "@rebasepro/plugin-ai";
export function App() { const authController = useRebaseAuthController({ client }); const dataEnhancementPlugin = useDataEnhancementPlugin();
return ( <Rebase client={client} authController={authController} plugins={[dataEnhancementPlugin]} > <RebaseCMS collections={collections}/> <RebaseShell title="My App"/> </Rebase> );}Les plugins sont généralement construits par un hook, de sorte que le tableau est reconstruit à chaque rendu ; cela ne pose aucun problème, et c’est la raison pour laquelle plugins est une prop plutôt qu’un élément à mémoïser manuellement. Deux plugins avec la même key constituent une erreur — <Rebase> journalise les doublons plutôt que d’en ignorer un silencieusement.
Pour une contribution unique, vous n’avez pas du tout besoin d’un plugin : <Rebase slots> accepte directement les mêmes entrées SlotContribution.
Dans le cadre d’une composition manuelle
Section intitulée « Dans le cadre d’une composition manuelle »La liste des plugins ne doit être transmise manuellement dans le contrôleur de navigation que si vous avez remplacé <RebaseShell> par les couches sous-jacentes :
const navigationStateController = useBuildNavigationStateController({ plugins, collections: () => collections, // These four are required — the controller resolves navigation against them. authController, data, collectionRegistryController, urlController});<RebaseNavigation> effectue exactement cet appel pour vous, en lisant plugins depuis le contrôleur de personnalisation fourni par <Rebase>. Consultez Advanced: manual layout.
Création d’un plugin
Section intitulée « Création d’un plugin »Voici un plugin minimal qui ajoute une action de barre d’outils à chaque collection :
import type { RebasePlugin } from "@rebasepro/cms-types";
function useMyPlugin(): RebasePlugin { return { key: "my_plugin",
// `slots` is a flat array of contributions, each naming its slot. // See the Slots page for the full list of slot names. slots: [ { slot: "collection.actions", Component: MyToolbarAction } ],
// `fieldBuilder` is top-level and takes a `wrap` function that returns // a *component* (or null to leave the field as it is) — it is not // a render function and no longer lives under `form`. fieldBuilder: { wrap: ({ property }) => property.propertyConfig === "my_custom_field" ? MyCustomField : null } };}Wrappers de champ de plusieurs plugins
Section intitulée « Wrappers de champ de plusieurs plugins »Depuis 0.25 En 0.24, le wrap de chaque
plugin recevait le champ intégré, et seul le dernier wrapper renvoyé
était affiché.
Les wrappers de plusieurs plugins se composent, dans l’ordre du tableau plugins.
Le wrap de chaque plugin reçoit, sous params.Field, le champ tel que les plugins
précédents l’ont laissé : le wrapper du premier plugin est donc le plus interne et celui du dernier
le plus externe. Un plugin dont le wrap renvoie null, ou dont la garde enabled
renvoie false pour le champ, le transmet inchangé. Un wrapper qui
affiche Field conserve donc tous les wrappers qui le précèdent :
import type { RebasePlugin } from "@rebasepro/cms-types";import { Typography } from "@rebasepro/ui";
const descriptionPlugin: RebasePlugin = { key: "description_below", fieldBuilder: { // `Field` is the built-in field, or the earlier plugins' wrapper of it. wrap: ({ Field, property }) => property.description ? (props) => ( <> <Field {...props}/> <Typography variant="caption" color="secondary"> {property.description} </Typography> </> ) : null }};Plugins intégrés
Section intitulée « Plugins intégrés »Plugin d’enrichissement des données (Data Enhancement)
Section intitulée « Plugin d’enrichissement des données (Data Enhancement) »Autocomplétion de champs alimentée par l’IA :
import { useDataEnhancementPlugin } from "@rebasepro/plugin-ai";
const enhancementPlugin = useDataEnhancementPlugin();Il ajoute une action Autofill à chaque formulaire, et une commande slash Autocomplete à chaque champ markdown, qui poursuit le texte à partir du curseur. Les deux n’apparaissent que là où le plugin est activé : le service se déclare disponible et getConfigForPath (si vous en passez un) autorise la collection. Un champ dont customProps.editorProps.aiController nomme un contrôleur qui lui est propre utilise celui-là. Depuis 0.25 Un autre plugin peut donner un contrôleur aux champs markdown de la même façon, avec EditorAIControllerProvider de @rebasepro/cms dans un fournisseur limité au formulaire.

Injection de collections
Section intitulée « Injection de collections »Les plugins peuvent ajouter dynamiquement de nouvelles collections :
hooks: { // Receives the resolved collections and returns the full list to use. injectCollections: (collections) => [...collections, auditLogCollection]}Modification de collections
Section intitulée « Modification de collections »Les plugins peuvent modifier des collections existantes :
hooks: { // Receives one collection, returns the modified one. modifyCollection: (collection) => ({ ...collection, properties: { ...collection.properties, last_modified_by: { type: "string", name: "Modified By", admin: { readOnly: true } } } })}modifyCollection est synchrone, et l’administration l’applique lorsqu’elle résout
ses collections, et non à chaque changement du plugin. Lorsque la modification nécessite des données
à récupérer, récupérez-les avant d’afficher <Rebase> et construisez le plugin
à partir de ce qui est revenu :
import { useEffect, useMemo, useState } from "react";import { Rebase, useRebaseAuthController } from "@rebasepro/app";import { RebaseCMS, RebaseShell } from "@rebasepro/cms";import type { RebasePlugin } from "@rebasepro/cms-types";import { CircularProgressCenter } from "@rebasepro/ui";
export function App() { const authController = useRebaseAuthController({ client }); const [labels, setLabels] = useState<Record<string, string>>();
useEffect(() => { fetch("/collection-labels.json") .then((response) => response.json()) .then(setLabels); }, []);
const labelsPlugin = useMemo<RebasePlugin | undefined>(() => labels && { key: "collection_labels", hooks: { modifyCollection: (collection) => ({ ...collection, name: labels[collection.slug] ?? collection.name }) } }, [labels]);
if (!labelsPlugin) return <CircularProgressCenter/>;
return ( <Rebase client={client} authController={authController} plugins={[labelsPlugin]}> <RebaseCMS collections={collections}/> <RebaseShell title="My App"/> </Rebase> );}Depuis 0.25
hooks.modifyCollectionAsync est déprécié. Rien ne l’a jamais appelé, en 0.24
comme avant. Un plugin qui le définit compile toujours, et <Rebase> avertit une seule fois par
plugin. Utilisez modifyCollection comme ci-dessus.
Étapes suivantes
Section intitulée « Étapes suivantes »- Studio Tools — Console SQL, console JS, éditeur RLS
- Custom Fields — Création de champs de formulaire personnalisés