Aller au contenu

Champs personnalisés

Rebase génère automatiquement des champs de formulaire basés sur les types de propriétés. Pour un comportement personnalisé, vous pouvez créer vos propres champs.

Un champ personnalisé est un composant React qui reçoit des FieldProps :

import type { FieldProps } from "@rebasepro/cms";
import type { StringProperty, NumberProperty } from "@rebasepro/types";
function ColorPickerField({ value, setValue, error, showError }: FieldProps<StringProperty>) {
return (
<div>
<input
type="color"
value={value ?? "#000000"}
onChange={(e) => setValue(e.target.value)}
/>
{showError && error && <span className="text-red-500">{error}</span>}
</div>
);
}
Propriété Type Description
value T Valeur actuelle du champ
setValue (value: T) => void Mettre à jour la valeur du champ
error string Message d’erreur de validation
showError boolean Indique si l’erreur doit être affichée
isSubmitting boolean Le formulaire est en cours d’enregistrement
property Property La configuration de la propriété
context FormContext Contexte complet du formulaire avec toutes les valeurs de l’entité
disabled boolean Le champ est en lecture seule
tableMode boolean Rendu à l’intérieur de la feuille de calcul (mode compact)

Enregistrer sur une seule propriété :

properties: {
brand_color: {
type: "string",
name: "Brand Color",
admin: {
Field: ColorPickerField
}
}
}

Quand le fichier de collection est aussi lu par le serveur

Section intitulée « Quand le fichier de collection est aussi lu par le serveur »

Dans le scaffold par défaut, config/collections/ est chargé à la fois par le panneau d’administration et par le backend — le backend lit les mêmes fichiers pour en déduire le schéma et l’API. Une référence directe au composant n’est sûre que si rien côté serveur ne charge ce fichier, car importer ColorPickerField importe aussi React, votre CSS et tout ce que le composant entraîne dans le graphe de modules du serveur.

Désignez plutôt le composant par un import différé. Il est typé de la même manière et le backend ne l’appelle jamais :

// config/collections/products.ts
properties: {
brand_color: {
type: "string",
name: "Brand Color",
admin: {
Field: () => import("../../frontend/src/ColorPickerField"),
Preview: () => import("../../frontend/src/ColorPreview")
}
}
}

Le module doit avoir un export par défaut — le thunk résout default, et un export uniquement nommé n’affiche rien. Le panneau l’enveloppe dans React.lazy au premier rendu : le composant forme donc son propre chunk au lieu d’alourdir le bundle initial.

Enregistrer un type de champ réutilisable :

const colorPropertyConfig: PropertyConfig = {
key: "color_picker",
name: "Color Picker",
property: {
type: "string",
admin: {
Field: ColorPickerField
}
}
};
// Register globally — keyed by the config's `key`
<Rebase propertyConfigs={{ color_picker: colorPropertyConfig }}>…</Rebase>

Utilisez-le ensuite dans n’importe quelle collection :

properties: {
color: {
type: "string",
name: "Color",
propertyConfig: "color_picker"
}
}

Les champs personnalisés peuvent accéder à toutes les valeurs de l’entité :

function PriceWithTaxField({ value, setValue, context }: FieldProps<NumberProperty>) {
const taxRate = Number(context.values.tax_rate ?? 0.1);
const priceWithTax = value ? value * (1 + taxRate) : 0;
return (
<div>
<input
type="number"
value={value ?? 0}
onChange={(e) => setValue(Number(e.target.value))}
/>
<p>Avec taxe : ${priceWithTax.toFixed(2)}</p>
</div>
);
}

Lors du rendu dans la vue feuille de calcul, les champs doivent être compacts. Vérifiez tableMode :

function MyField({ value, setValue, minimalistView }: FieldProps<StringProperty>) {
if (minimalistView) {
return <span onClick={() => { /* open editor */ }}>{value}</span>;
}
return (
<div>
<label>Éditeur complet</label>
<textarea value={value ?? ""} onChange={(e) => setValue(e.target.value)} />
</div>
);
}

Pour un rendu personnalisé dans le tableau (mode non-édition), utilisez le composant Preview :

function ColorPreview({ value }: { value: string }) {
return (
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<div style={{
width: 24, height: 24,
borderRadius: 4,
backgroundColor: value
}} />
<span>{value}</span>
</div>
);
}
// Enregistrer le composant
properties: {
color: {
type: "string",
name: "Color",
admin: {
Field: ColorPickerField,
Preview: ColorPreview
}
}
}