Rendre les modules d’extension utiles
Présentation
Section intitulée « Présentation »Construire un module d’extension pour StudioCMS est un moyen puissant d’étendre les fonctionnalités de StudioCMS. Il offre une solution simple et flexible pour ajouter de nouvelles fonctionnalités à votre projet StudioCMS. Voici un exemple simple de création et de fonctionnement d’un module d’extension pour StudioCMS.
Mise en route
Section intitulée « Mise en route »Pour commencer, vous devez créer un nouveau module d’extension pour StudioCMS. Voici un exemple de structure de fichier pour un module d’extension pour StudioCMS :
- package.json
Répertoiresrc
- index.ts
Répertoireroutes
- […slug].astro
Répertoiredashboard-grid-items
- MyPluginGridItem.astro
Création du module d’extension
Section intitulée « Création du module d’extension »Dans le fichier principal src/index.ts
, vous définirez le module d’extension pour StudioCMS. Voici un exemple de définition d’un module d’extension pour StudioCMS incluant une intégration Astro pour créer un blog simple :
import { function definePlugin(options: StudioCMSPlugin): StudioCMSPlugin
Defines a plugin for StudioCMS.
definePlugin } from 'studiocms/plugins';import { (alias) interface AstroIntegrationimport AstroIntegration
AstroIntegration } from 'astro';import { const addVirtualImports: HookUtility<"astro:config:setup", [{ name: string; imports: Imports; __enableCorePowerDoNotUseOrYouWillBeFired?: boolean;}], void>
Creates a Vite virtual module and updates the Astro config.
Virtual imports are useful for passing things like config options, or data computed within the integration.
addVirtualImports, const createResolver: (_base: string) => { resolve: (...path: Array<string>) => string;}
Allows resolving paths relatively to the integration folder easily. Call it like this:
createResolver } from 'astro-integration-kit';
// Définir les options du module d’extension et de l’intégrationinterface interface Options
Options { Options.route: string
route: string;}
export function function studioCMSPageInjector(options: Options): StudioCMSPlugin
studioCMSPageInjector(options: Options
options: interface Options
Options) { // Résoudre le chemin d’accès au fichier actuel const { const resolve: (...path: Array<string>) => string
resolve } = function createResolver(_base: string): { resolve: (...path: Array<string>) => string;}
Allows resolving paths relatively to the integration folder easily. Call it like this:
createResolver(import.
The type of import.meta
.
If you need to declare that a given property exists on import.meta
,
this type may be augmented via interface merging.
meta.ImportMeta.url: string
The absolute file:
URL of the module.
url);
// Définir l’intégration Astro function function (local function) monIntegration(options: Options): AstroIntegration
monIntegration(options: Options
options: interface Options
Options): (alias) interface AstroIntegrationimport AstroIntegration
AstroIntegration { const const route: string
route = `/${options: Options
options?.Options.route: string
route || 'mon-module-extension'}`;
return { AstroIntegration.name: string
The name of the integration.
name: 'mon-integration-astro', AstroIntegration.hooks: { 'astro:db:setup'?: (options: { extendDb: (options: { configEntrypoint?: URL | string; seedEntrypoint?: URL | string; }) => void; }) => void | Promise<void>; ... 12 more ...; 'studiocms:plugins'?: PluginHook<...>;} & Partial<...>
The different hooks available to extend.
hooks: { "astro:config:setup": (params: { config: AstroConfig; command: "dev" | "build" | "preview" | "sync"; isRestart: boolean; updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig; ... 8 more ...; logger: AstroIntegrationLogger;}
params) => { const { const injectRoute: (injectRoute: InjectedRoute) => void
injectRoute } = params: { config: AstroConfig; command: "dev" | "build" | "preview" | "sync"; isRestart: boolean; updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig; ... 8 more ...; logger: AstroIntegrationLogger;}
params;
// Injecter la route pour le module d’extension const injectRoute: (injectRoute: InjectedRoute) => void
injectRoute({ entrypoint: string | URL
entrypoint: const resolve: (...path: Array<string>) => string
resolve('./routes/[...slug].astro'), pattern: string
pattern: `/${const route: string
route}/[...slug]`, prerender?: boolean
prerender: false, })
function addVirtualImports(params: { config: AstroConfig; command: "dev" | "build" | "preview" | "sync"; isRestart: boolean; updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig; ... 8 more ...; logger: AstroIntegrationLogger;}, args_0: { ...;}): void
Creates a Vite virtual module and updates the Astro config.
Virtual imports are useful for passing things like config options, or data computed within the integration.
addVirtualImports(params: { config: AstroConfig; command: "dev" | "build" | "preview" | "sync"; isRestart: boolean; updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig; ... 8 more ...; logger: AstroIntegrationLogger;}
params, { name: string
name: 'mon-integration-astro', imports: Imports
imports: { 'myplugin:config': ` export const options = ${var JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
JSON.JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
stringify({ route: string
route })}; export default options; `, } }) } } } }
// Définir le module d’extension pour StudioCMS return function definePlugin(options: StudioCMSPlugin): StudioCMSPlugin
Defines a plugin for StudioCMS.
definePlugin({ StudioCMSPlugin.identifier: string
identifier: 'mon-module-extension', StudioCMSPlugin.name: string
name: "Mon module d'extension", StudioCMSPlugin.studiocmsMinimumVersion: string
studiocmsMinimumVersion: '0.1.0-beta.18', StudioCMSPlugin.hooks: { 'studiocms:astro:config'?: PluginHook<...>; 'studiocms:config:setup'?: PluginHook<...>;} & Partial<...>
hooks: { 'studiocms:astro:config': ({ addIntegrations: (args_0: AstroIntegration | AstroIntegration[]) => void
addIntegrations }) => { addIntegrations: (args_0: AstroIntegration | AstroIntegration[]) => void
addIntegrations(function (local function) monIntegration(options: Options): AstroIntegration
monIntegration(options: Options
options)); // Facultatif, mais recommandé }, 'studiocms:config:setup': ({ setDashboard: (args_0: { settingsPage?: { fields: ({ name: string; label: string; input: "checkbox"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultChecked?: boolean | undefined; size?: "sm" | "md" | "lg" | undefined; } | { name: string; label: string; input: "input"; type?: "number" | "text" | "password" | "email" | "tel" | "url" | "search" | undefined; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { name: string; label: string; input: "textarea"; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { options: { value: string; label: string; disabled?: boolean | undefined; }[]; name: string; label: string; input: "radio"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultValue?: string | undefined; direction?: "horizontal" | "vertical" ...
setDashboard, setFrontend: (args_0: { frontendNavigationLinks?: { label: string; href: string; }[] | undefined;}) => void
setFrontend, setRendering: (args_0: { pageTypes?: { label: string; identifier: string; fields?: ({ name: string; label: string; input: "checkbox"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultChecked?: boolean | undefined; size?: "sm" | "md" | "lg" | undefined; } | { name: string; label: string; input: "input"; type?: "number" | "text" | "password" | "email" | "tel" | "url" | "search" | undefined; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { name: string; label: string; input: "textarea"; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { options: { value: string; label: string; disabled?: boolean | undefined; }[]; name: string; label: string; input: "radio"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultValue?: string | undefined; direction ...
setRendering }) => { setDashboard: (args_0: { settingsPage?: { fields: ({ name: string; label: string; input: "checkbox"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultChecked?: boolean | undefined; size?: "sm" | "md" | "lg" | undefined; } | { name: string; label: string; input: "input"; type?: "number" | "text" | "password" | "email" | "tel" | "url" | "search" | undefined; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { name: string; label: string; input: "textarea"; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { options: { value: string; label: string; disabled?: boolean | undefined; }[]; name: string; label: string; input: "radio"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultValue?: string | undefined; direction?: "horizontal" | "vertical" ...
setDashboard({ // Définir les éléments de la grille pour le tableau de bord // Voici les éléments qui seront affichés dans le tableau de bord de StudioCMS // Vous pouvez définir autant d’éléments que vous le souhaitez // Dans cet exemple, nous définissons un seul élément, qui a une étendue de 2, qui nécessite l’autorisation `editor` et qui injecte un composant Astro qui remplace l’élément personnalisé HTML simple. dashboardGridItems?: GridItemInput[] | undefined
dashboardGridItems: [ { GridItemInput.name: string
The name of the grid item.
name: 'exemple', GridItemInput.span: 1 | 2 | 3
The span of the grid item, which can be 1, 2, or 3.
span: 2, GridItemInput.variant: "default" | "filled"
The variant of the grid item, which can be 'default' or 'filled'.
variant: 'default', GridItemInput.requiresPermission?: "editor" | "owner" | "admin" | "visitor"
The required permission level to view the grid item.
Optional. Can be 'owner', 'admin', 'editor', or 'visitor'.
requiresPermission: 'editor', GridItemInput.header?: { title: string; icon?: HeroIconName;}
The header of the grid item.
Optional.
header: { title: string
The title of the header.
title: 'Exemple', icon?: "map" | "bolt" | "code-bracket-solid" | "code-bracket-square-solid" | "exclamation-circle" | "exclamation-circle-solid" | "exclamation-triangle" | "exclamation-triangle-solid" | ... 1279 more ... | "x-mark-solid"
The icon of the header.
Optional.
icon: 'bolt' }, GridItemInput.body?: { html: string; components?: Record<string, string>; sanitizeOpts?: SanitizeOptions;}
The body of the grid item.
Optional.
body: { // Utilisez toujours du HTML brut sans `-` ni caractères spéciaux dans les balises, ils seront remplacés par le composant Astro et ce HTML ne sera jamais affiché html: string
The HTML content of the body.
html: '<examplegriditem></examplegriditem>', components?: Record<string, string>
The components within the body.
Optional.
components: { // Injecter le composant Astro pour remplacer l’élément personnalisé HTML brut examplegriditem: string
examplegriditem: const resolve: (...path: Array<string>) => string
resolve('./dashboard-grid-items/MyPluginGridItem.astro') } } } ], });
setFrontend: (args_0: { frontendNavigationLinks?: { label: string; href: string; }[] | undefined;}) => void
setFrontend({ // Définir les liens de navigation frontend pour le module d’extension (facultatif) // Ceci est utile si vous utilisez les assistants de navigation intégrés de StudioCMS dans votre mise en page, // comme lors de l’utilisation du module d’extension `@studiocms/blog`. frontendNavigationLinks?: { label: string; href: string;}[] | undefined
frontendNavigationLinks: [{ label: string
label: 'Le titre ici', href: string
href: options: Options
options?.Options.route: string
route || 'mon-module-extension' }], });
setRendering: (args_0: { pageTypes?: { label: string; identifier: string; fields?: ({ name: string; label: string; input: "checkbox"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultChecked?: boolean | undefined; size?: "sm" | "md" | "lg" | undefined; } | { name: string; label: string; input: "input"; type?: "number" | "text" | "password" | "email" | "tel" | "url" | "search" | undefined; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { name: string; label: string; input: "textarea"; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { options: { value: string; label: string; disabled?: boolean | undefined; }[]; name: string; label: string; input: "radio"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultValue?: string | undefined; direction ...
setRendering({ // Lors de la création de pagesTypes, vous pouvez également définir un `pageContentComponent` si votre module d’extension nécessite un éditeur de contenu personnalisé. // pageTypes: [{ identifier: 'mon-module-extension', label: "Article de blog (Mon module d’extension)", pageContentComponent: resolve('./components/MyContentEditor.astro') }], // Dans cet exemple, nous pouvons utiliser l’éditeur de contenu par défaut (Markdown). pageTypes?: { label: string; identifier: string; fields?: ({ name: string; label: string; input: "checkbox"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultChecked?: boolean | undefined; size?: "sm" | "md" | "lg" | undefined; } | { name: string; label: string; input: "input"; type?: "number" | "text" | "password" | "email" | "tel" | "url" | "search" | undefined; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { name: string; label: string; input: "textarea"; required?: boolean | undefined; readOnly?: boolean | undefined; placeholder?: string | undefined; defaultValue?: string | undefined; } | { options: { value: string; label: string; disabled?: boolean | undefined; }[]; name: string; label: string; input: "radio"; required?: boolean | undefined; readOnly?: boolean | undefined; color?: "primary" | "success" | "warning" | "danger" | "info" | "mono" | undefined; defaultValue?: string | undefined; direction?: "horizontal" | "vertical" | undefined; } | { options: { value: string; label: string; disabled?: boolean | undefined; } ...
pageTypes: [{ identifier: string
identifier: 'mon-module-extension', label: string
label: "Article de blog (Mon module d'extension)" }], }) } } });}
L’exemple ci-dessus définit un module d’extension pour StudioCMS incluant une intégration Astro et permettant de créer un exemple de blog simple. Le module d’extension inclut une route injectée dans le projet StudioCMS et un élément de grille affiché dans le tableau de bord de StudioCMS.
Exemple de route
Section intitulée « Exemple de route »Dans le fichier src/routes/[...slug].astro
, vous définirez la route du module d’extension. Voici un exemple de définition d’une route pour le module d’extension. Nous la diviserons en deux parties : la première est le frontmatter (entre les barrières ---
), et la seconde est le modèle HTML placé sous la seconde barrière ---
.
import { const StudioCMSRenderer: any
StudioCMSRenderer } from 'studiocms:renderer';import const sdk: { addPageToFolderTree: (tree: FolderNode[], folderId: string, newPage: FolderNode) => FolderNode[]; ... 29 more ...; notificationSettings: { site: { get: () => Promise<{ id: string; emailVerification: boolean; requireAdminVerification: boolean; requireEditorVerification: boolean; oAuthBypassVerification: boolean; }>; update: (settings: { ...; }) => Promise<{ id: string; emailVerification: boolean; requireAdminVerification: boolean; requireEditorVerification: boolean; oAuthBypassVerification: boolean; }>; }; };}
sdk from 'studiocms:sdk';import const config: { route: string;}
config from 'myplugin:config';
const const makeRoute: (slug: string) => string
makeRoute = (slug: string
slug: string) => { return `/${const config: { route: string;}
config.route: string
route}/${slug: string
slug}`;}
// 'mon-module-extension' est ici utilisé comme identifiant pour// le type de page de la définition du module d’extensionconst const pages: CombinedPageData[]
pages = await const sdk: { addPageToFolderTree: (tree: FolderNode[], folderId: string, newPage: FolderNode) => FolderNode[]; ... 29 more ...; notificationSettings: { site: { get: () => Promise<{ id: string; emailVerification: boolean; requireAdminVerification: boolean; requireEditorVerification: boolean; oAuthBypassVerification: boolean; }>; update: (settings: { ...; }) => Promise<{ id: string; emailVerification: boolean; requireAdminVerification: boolean; requireEditorVerification: boolean; oAuthBypassVerification: boolean; }>; }; };}
sdk.type GET: { database: { users: () => Promise<CombinedUserData[]>; pages: { (includeDrafts?: boolean, hideDefaultIndex?: boolean, tree?: FolderNode[], metaOnly?: false, paginate?: PaginateInput): Promise<CombinedPageData[]>; (includeDrafts?: boolean, hideDefaultIndex?: boolean, tree?: FolderNode[], metaOnly?: true, paginate?: PaginateInput): Promise<MetaOnlyPageData[]>; }; folderPages: { (id: string, includeDrafts?: boolean, hideDefaultIndex?: boolean, tree?: FolderNode[], metaOnly?: false, paginate?: PaginateInput): Promise<CombinedPageData[]>; (id: string, includeDrafts?: boolean, hideDefaultIndex?: boolean, tree?: FolderNode[], metaOnly?: true, paginate?: PaginateInput): Promise<MetaOnlyPageData[]>; }; config: () => Promise<{ ...; } | undefined>; folders: () => Promise<{ ...; }[]>; }; databaseEntry: { users: { byId: (id: string) => Promise<CombinedUserData | undefined>; byUsername: (username: string) => Promise<CombinedUserData | undefined>; byEmail: (email: string) => Promise<CombinedUserData | undefined>; }; pages: { byId: { (id: string, tree?: FolderNode[]): Promise<CombinedPageData | undefined>; (id: string, tree?: FolderNode[], metaOnly?: boolean): Promise<MetaOnlyPageData | undefined>; }; bySlug: { (slug: string, tree?: FolderNode[]): Promise<CombinedPageData | undefined>; ( ...
GET.packagePages: (packageName: string, tree?: FolderNode[]) => Promise<CombinedPageData[]> (+1 overload)
packagePages('mon-module-extension');
const { const slug: string | undefined
slug } = const Astro: AstroGlobal<Record<string, any>, AstroComponentFactory, Record<string, string | undefined>>
Astro.AstroGlobal<Record<string, any>, AstroComponentFactory, Record<string, string | undefined>>.params: Record<string, string | undefined>
Parameters passed to a dynamic page generated using getStaticPaths
Example usage:
---export async function getStaticPaths() { return [ { params: { id: '1' } }, ];}
const { id } = Astro.params;---<h1>{id}</h1>
params;
const const page: CombinedPageData | undefined
page = const pages: CombinedPageData[]
pages.Array<CombinedPageData>.find(predicate: (value: CombinedPageData, index: number, obj: CombinedPageData[]) => unknown, thisArg?: any): CombinedPageData | undefined (+1 overload)
Returns the value of the first element in the array where predicate is true, and undefined
otherwise.
find((page: CombinedPageData
page) => page: CombinedPageData
page.slug: string
slug === const slug: string | undefined
slug || '');
{ slug && page ? ( <div> <h1>{page.title}</h1> <StudioCMSRenderer content={page.defaultContent?.content || ''} /> </div> ) : ( <div> <h1>Mon module d’extension</h1> <ul> {pages.length > 0 && pages.map((page) => ( <li> <a href={makeRoute(page.slug)}>{page.title}</a> </li> ))} </ul> </div> )}
L’exemple ci-dessus définit une route dynamique^ pour le module d’extension qui affiche une liste d’articles de blog lorsqu’aucun slug n’est fourni et affiche le contenu d’un article de blog lorsqu’un slug est fourni.
Exemple d’élément de grille
Section intitulée « Exemple d’élément de grille »Dans le fichier src/dashboard-grid-items/MyPluginGridItem.astro
, vous définirez l’élément de grille du module d’extension. Voici un exemple de définition d’un élément de grille pour le module d’extension :
---import { StudioCMSRoutes } from 'studiocms:lib';import sdk from 'studiocms:sdk';
// 'mon-module-extension' est ici utilisé comme identifiant pour// le type de page à partir de la définition du module d’extensionconst pages = await sdk.GET.packagePages('mon-module-extension');
// Obtenir les 5 pages les plus récemment mises à jour au cours des 30 derniers joursconst recentlyUpdatedPages = pages .filter((page) => { const now = new Date(); const thirtyDaysAgo = new Date(now.setDate(now.getDate() - 30)); return new Date(page.updatedAt) > thirtyDaysAgo; }) .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) .slice(0, 5);---
<div> <h2>Pages récemment mises à jour</h2> <ul> {recentlyUpdatedPages.length > 0 && recentlyUpdatedPages.map((page) => ( <li> <a href={StudioCMSRoutes.mainLinks.contentManagementEdit + `?edit=${page.id}`}>{page.title}</a> </li> ))} </ul></div>
L’exemple ci-dessus définit un élément de grille pour le module d’extension qui affiche les 5 pages les plus récemment mises à jour au cours des 30 derniers jours. Cet élément inclut une liste de liens vers la page de modification de la gestion de contenu pour chaque page.
Intégration avec les assistants FrontendNavigationLinks
Section intitulée « Intégration avec les assistants FrontendNavigationLinks »Si vous souhaitez utiliser les assistants de navigation intégrés de StudioCMS dans votre projet, de la même manière que le fait le module d’extension @studiocms/blog
, vous pouvez créer un composant Navigation.astro
personnalisé :
---import { StudioCMSRoutes } from 'studiocms:lib';import studioCMS_SDK from 'studiocms:sdk/cache';import { frontendNavigation } from 'studiocms:plugin-helpers';
// Définir les props pour le composant Navigationinterface Props { topLevelLinkCount?: number;};
// Obtenir le nombre de liens de niveau supérieur à partir des propsconst { topLevelLinkCount = 3 } = Astro.props;
// Obtenir la configuration du site et la liste des pagesconst config = (await studioCMS_SDK.GET.siteConfig()).data;
// Récupérer le titre du site à partir de la configurationconst { title } = config || { title: 'StudioCMS' };
// Obtenir l’URL du site principalconst { mainLinks: { baseSiteURL },} = StudioCMSRoutes;
// Définir les props de lien pour la navigationtype LinkProps = { text: string; href: string;};
// Définir les liens pour la navigationconst links: LinkProps[] = await frontendNavigation();---{/* S’il n’y a pas d’éléments déroulants */}{ ( links.length < topLevelLinkCount || links.length === topLevelLinkCount ) && ( <div class="navigation"> <div class="title"><a href={baseSiteURL}>{title}</a></div> <div class="mini-nav"> <button>Menu</button> <div class="mini-nav-content"> { links.map(({ text, href }) => ( <a {href}>{text}</a> )) } </div> </div> { links.map(({ text, href }) => ( <a class="links" {href}>{text}</a> )) } </div>) }
{/* S’il y a des éléments déroulants */}{ links.length > topLevelLinkCount && ( <div class="navigation"> <div class="title"><a href={baseSiteURL}>{title}</a></div>
<div class="mini-nav"> <button>Menu</button> <div class="mini-nav-content"> { links.map(({ text, href }) => ( <a {href}>{text}</a> )) } </div> </div> { links.slice(0, topLevelLinkCount).map(({ text, href }) => ( <a class="links" {href}>{text}</a> )) } <div class="dropdown"> <button>Plus ▼</button> <div class="dropdown-content"> { links.slice(topLevelLinkCount).map(({ text, href }) => ( <a {href}>{text}</a> )) } </div> </div> </div>) }
L’exemple ci-dessus définit un composant personnalisé Navigation.astro
qui utilise les assistants de navigation intégrés de StudioCMS pour créer un menu de navigation pour le projet. Ce composant inclut des liens vers l’URL du site principal, la page d’index et toutes les autres pages configurées pour s’afficher dans la navigation.
Il vous suffit d’ajouter quelques styles et vous disposez d’un menu de navigation entièrement fonctionnel qui fonctionne avec les assistants de navigation intégrés de StudioCMS.