Dans ce tutoriel, nous allons créer le code d'un champ et l'insérer dans l'espace d'administration d'un site web. En prérequis, il faut avoir déjà créé un plugin comme indiqué dans le tutoriel de création de plugin. Avant de commencer, vous devez donc avoir un site ParoiCMS avec un plugin fork-or-knife-plugin
.
Dans le dossier du plugin (fork-or-knife-plugin
), créez un sous-dossier site-schema-lib
. Il contiendra deux fichiers.
Le fichier field-lib.site-schema.json
décrit le champ :
{ "version": "3.1", "languages": ["fr"], "fieldTypes": [ { "name": "forkOrKnife", "storedOn": "leaf", "storedAs": "varchar", "dataType": "string", "plugin": "fork-or-knife-plugin" } ] }
Nous venons de déclarer un champ dont la clé est forkOrKnife
. Sa valeur est partagée par les éventuelles traductions du document ("storedOn": "leaf"
) et il est stocké sous forme de chaîne de caractères. Il utilise notre plugin (c'est un effet de la propriété "plugin": "fork-or-knife-plugin"
), et cela implique de lui donner un comportement spécial. Les sections suivantes de ce document auront pour objectif d'implémenter ce comportement.
À lire aussi, la documentation sur les champs.
Le deuxième fichier est nommé field-lib.site-schema.l10n.fr.json
et contient le libellé du champ en français à afficher dans l'espace d'administration :
{ "fieldTypes": { "forkOrKnife": { "label": "Fourchette ou couteau ?" } } }
Pour que notre morceau de site-schema soit pris en compte, il faut référencer le dossier site-schema-lib
afin de le rendre disponible sur ParoiCMS :
backend/src/plugin.ts
siteInit
, ajoutez :service.registerSiteSchemaLibrary(join(packageDir, "site-schema-lib"));
package.json
du pluginÉditez le fichier package.json
du plugin (celui situé dans le dossier fork-or-knife-plugin
). Dans la section des "scripts"
, ajoutez :
"build:bo": "(cd bo-front && tsc && vite build)", "build:bo:watch": "(cd bo-front && tsc && vite build --watch)"
Modifiez également la commande "build"
dans cette même section afin d'y ajouter && npm run build:bo
. Votre commande "build"
ressemblera à ceci :
"build": "npm run build:backend && npm run build:bo"
Dans la section "devDependencies"
, ajoutez les dépendances suivantes :
"@paroicms/public-bo-lib": "0.12.0", "sass": "~1.77.8", "solid-js": "~1.8.20", "vite": "~5.4.0", "vite-plugin-solid": "~2.10.2"
NB : N'hésitez pas à utiliser les versions les plus récentes de ces dépendances.
Nous venons d'ajouter les dépendances et les commandes de build d'une application SolidJS située dans le plugin. Il nous reste à créer cette application, ce qui est l'objet de la section suivante.
Dans le dossier du plugin, créez un sous-dossier bo-front
. Les fichiers que nous allons y placer sont ceux d'une application SolidJS classique.
Dans le dossier bo-front
, créez un fichier tsconfig.json
:
{ "compilerOptions": { "strict": true, "target": "ESNext", "module": "ESNext", "moduleResolution": "Bundler", "allowSyntheticDefaultImports": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "types": [ "vite/client" ], "noEmit": true, "isolatedModules": true, }, }
Toujours dans le dossier bo-front
, créez un fichier vite.config.ts
:
import { resolve } from "node:path"; import { defineConfig } from "vite"; import solidPlugin from "vite-plugin-solid"; export default defineConfig({ plugins: [ solidPlugin(), ], build: { target: "esnext", outDir: "dist", lib: { entry: resolve(__dirname, "src/main.tsx"), fileName: () => "bo-plugin.mjs", formats: ["es"], }, rollupOptions: { output: { format: "es", inlineDynamicImports: false, }, }, }, });
Créez un sous-dossier bo-front/src/
qui contiendra les trois fichiers suivants :
// main.tsx import "./styles.scss" import type { BoPlugin, BoPluginInitOptions, BoPluginInstance, BoPluginService, } from "@paroicms/public-bo-lib"; import { createEffect, createRoot, createSignal } from "solid-js"; import ForkOrKnifeField from "./ForkOrKnifeField"; const plugin: BoPlugin<string> = { create, init, }; export default plugin; function init({ pluginBaseUrl }: BoPluginInitOptions) { const cssUrl = `${pluginBaseUrl}/style.css`; const link = document.createElement("link"); link.rel = "stylesheet"; link.href = cssUrl; document.head.appendChild(link); } function create(service: BoPluginService<string>): BoPluginInstance<string> { if (service.fieldType.dataType !== "string") { throw new Error(`data type '${service.fieldType.dataType}' is incompatible, should be 'string'`); } return createRoot((dispose) => { const [value, setValue] = createSignal(service.value); const [language, setLanguage] = createSignal(service.language); createEffect(() => { service.onChange(value()); }); return { element: ( <ForkOrKnifeField language={language} value={value} setValue={setValue} /> ) as HTMLElement, setLanguage, setValue: (list) => { setValue(list); }, getValue: value, dispose, }; }); }
C'est le point d'entrée de l'application SolidJS. Ce fichier exporte deux fonctions décrites par le type BoPlugin<string>
dans le package @paroicms/public-bo-lib
:
init
: Fonction appelée une seule fois, juste après le chargement du plugin par le back-office.create
: Fonction appelée pour chaque champ utilisant le plugin. Elle doit retourner un objet contenant les propriétés suivantes :element
: Fragment du DOM que le back-office insérera en tant que champ dans l'écran d'édition du document.setLanguage
: Setter appelé si le back-office change de langue.setValue
: Setter appelé si la valeur est modifiée en dehors du champ.getValue
: Getter retournant la valeur du champ.dispose
: Fonction appelée lorsque le champ doit être détruit.Il faut également appeler service.onChange
chaque fois que la valeur du champ change. Cet appel n'est pas optionnel : le back-office utilise ce mécanisme pour récupérer la valeur modifiée.
// ForkOrKnifeField.tsx import type { Accessor, Setter } from "solid-js"; export interface ForkOrKnifeFieldProps { language: Accessor<string>; value: Accessor<string | undefined>; setValue: Setter<string | undefined>; } export default function ForkOrKnifeField(props: ForkOrKnifeFieldProps) { const { value, setValue } = props; return ( <form class="FkField"> <label> <input type="radio" name="utensil" value="fork" checked={value() === "fork"} onChange={() => setValue("fork")} /> ⑂ </label> <label> <input type="radio" name="utensil" value="knife" checked={value() === "knife"} onChange={() => setValue("knife")} /> 🔪 </label> </form> ); }
// styles.scss .FkField { display: flex; gap: 10px; label { align-items: center; border: 2px solid #ccc; border-radius: 10px; cursor: pointer; display: flex; font-size: 2rem; height: 100px; justify-content: center; transition: background-color 0.3s, border-color 0.3s; width: 100px; } input[type="radio"] { display: none; } label:has(input[type="radio"]:checked) { background-color: #007bff; border-color: #007bff; color: white; } }
Voir aussi : le site officiel de SolidJS.
Une chose à savoir : il n'est pas obligé d'écrire cette application en SolidJS. L'application d'un champ est intégrée par ParoiCMS en tant que module JavaScript standard, et peu importe le framework avec lequel elle est faite.
À ce niveau, vous êtes prêt à compiler notre application SolidJS. Dans un terminal, placez-vous dans le dossier du plugin, puis exécutez les commandes suivantes :
# installer les nouvelles dépendances npm i # compiler npm run build:bo
Si la compilation s'est bien passée, alors un dossier bo-front/dist
a été créé et les fichiers bundlés pour le CSS et pour le javascript sont déposés dedans.
Pour que l'application SolidJS soit prise en compte, il faut référencer le dossier bo-front/dist
afin de la rendre disponible depuis le back-office :
backend/src/plugin.ts
.siteInit
, ajoutez :service.setBoAssetsDirectory(join(packageDir, "bo-front", "dist"));
Puisque nous avons modifié le code du backend, il faut recompiler. Dans le dossier du plugin, exécutez la commande :
npm run build
Le plugin est prêt.
Il est temps de sortir du dossier du plugin. Dans le fichier site-schema.json
du site web, au niveau de la description du type de document pour la page d'accueil, c'est-à-dire sous la ligne "typeName": "home"
, insérez :
"fields": [ "forkOrKnife" ],
À ce niveau, nous devrions être capables d'utiliser notre nouveau composant dans l'espace d'administration :
npm run dev
).Il nous reste à l'utiliser dans la partie publique du site.
Éditez le fichier theme/templates/home.liquid. La première chose à faire est d'inspecter la payload afin de vérifier si notre champ s'y trouve. Pour cela, ajoutez quelque part :
{{ doc | info }}
La payload est imposante, mais quelque part dedans nous pouvons voir :
Voici un exemple de code Liquid qui utilise notre nouveau champ :
<p>Je mange avec {% if doc.field.forkOrKnife == "fork" %} une fourchette {% elsif doc.field.forkOrKnife == "knife" %} un couteau {% else %} les doigts {% endif %} </p>
… Cela affichera quelque chose comme :
Je mange avec une fourchette
Dans ce tutoriel, nous avons déclaré un nouveau type de champ. Nous avons implémenté une application JavaScript pour éditer ce champ depuis l'espace d'administration, et nous avons utilisé le résultat stocké dans le champ pour le rendu de la page web correspondante.