Substituições de Componentes (Swizzling)
Visão Geral
Seção intitulada “Visão Geral”O Rebase permite que você substitua componentes de interface de usuário (UI) padrão por suas próprias implementações personalizadas. Isso implementa um modelo de swizzling de componentes no estilo Docusaurus que suporta dois padrões de personalização:
- Modo Eject (padrão): Seu componente substitui totalmente o componente integrado.
- Modo Wrap (
wrap: true): Seu componente envolve o original. O que ele envolve (normalmente o componente integrado) é passado como a propOriginalComponentpara que você possa renderizá-lo dentro do seu layout/lógica personalizada.
As substituições de componentes podem ser aplicadas globalmente no nível da aplicação (no provedor <Rebase>) ou localmente no nível da coleção (dentro das definições individuais de coleção).
Substituições Globais de Componentes
Seção intitulada “Substituições Globais de Componentes”Para substituir componentes globalmente em toda a sua aplicação, passe um objeto components para o provedor raiz <Rebase>.
import { Rebase } from "@rebasepro/app";import { MyAppBar } from "./components/MyAppBar";
function App() { return ( <Rebase client={rebaseClient} components={{ // Eject Mode: Replace the default AppBar entirely "Shell.AppBar": { Component: MyAppBar },
// Wrap Mode: Wrap the login view to insert branding "Auth.LoginView": { // `OriginalComponent` is injected at runtime when `wrap: true`; the override // slot's type does not model it, hence the annotation. Component: (({ OriginalComponent, ...props }: { OriginalComponent: React.ComponentType<Record<string, unknown>> }) => ( <div className="login-branding-container"> <header className="branding-header">My Custom Brand</header> <OriginalComponent {...props} /> </div> )) as unknown as React.ComponentType<Record<string, unknown>>, wrap: true } }} > {/* your app */} … </Rebase> );}Substituições de Componentes no Nível da Coleção
Seção intitulada “Substituições de Componentes no Nível da Coleção”Para substituir componentes apenas para uma coleção específica, adicione um objeto components sob o bloco admin dela. Isso é útil para personalizar estados vazios, cards ou visualizações de detalhes para modelos específicos.
No scaffold padrão, config/collections/ é carregado tanto pelo painel de administração quanto pelo backend, que lê os mesmos arquivos para derivar o schema e a API. Portanto, aponte para cada componente pelo caminho do módulo em vez de importá-lo. Component aceita as mesmas formas que admin.Field e entityViews[].Builder: um caminho, um import() dinâmico/lazy ou o próprio componente.
// config/collections/products.tsimport { defineCollection } from "@rebasepro/cms-types";
export const productsCollection = defineCollection({ name: "Products", slug: "products", table: "products", properties: { /* ... */ }, admin: { components: { // Eject Mode: Replace the default entity form view "Entity.Form": { Component: "../../frontend/src/ProductCustomForm" },
// Wrap Mode: Wrap the empty state to add quick links "Collection.EmptyState": { Component: "../../frontend/src/ProductsEmptyState", wrap: true } } }});O componente que envolve o original reside junto ao restante do seu código frontend e recebe o que ele envolve como OriginalComponent — o componente integrado, a menos que seu app o substitua também (veja Quando os dois escopos substituem um componente):
// frontend/src/ProductsEmptyState.tsximport type React from "react";
export default function ProductsEmptyState({ OriginalComponent, ...props }: { OriginalComponent: React.ComponentType<Record<string, unknown>>}) { return ( <div className="empty-state-wrapper"> <OriginalComponent {...props} /> <button onClick={() => importDemoProducts()}> Load Demo Products </button> </div> );}Cada módulo precisa de um export padrão (default export). O plugin Vite para coleções reescreve um caminho em um lazy import, de modo que o componente seja seu próprio chunk e carregue na primeira vez que a substituição for renderizada. Essa reescrita cobre os arquivos dentro do collectionsDir configurado. Um caminho em um arquivo fora dele chega ao admin como uma string simples: o console emitirá um aviso e o componente integrado será renderizado em seu lugar. Fora do collectionsDir, escreva você mesmo o lazy import: Component: () => import("../../frontend/src/ProductCustomForm").
Uma referência direta (Component: ProductCustomForm) também funciona, mas apenas em um arquivo de coleção que nada no servidor carregue, pois importar o componente também importa o React e tudo o que ele traz consigo.
Quando os dois escopos substituem um componente
Seção intitulada “Quando os dois escopos substituem um componente”Desde 0.25 Na 0.24 o wrapper de uma coleção sempre recebe o componente integrado, e a substituição no nível da aplicação é ignorada dentro dessa coleção.
Os dois escopos se compõem. A substituição no nível da aplicação (<Rebase components>) é aplicada primeiro ao componente integrado, e a da coleção (admin.components) é aplicada ao resultado:
| Nível da aplicação | Nível da coleção | O que é renderizado dentro dessa coleção |
|---|---|---|
| qualquer | nenhuma | o que é renderizado em todos os outros lugares: a substituição no nível da aplicação, ou o componente integrado |
| nenhuma | wrap | o wrapper da coleção em volta do componente integrado |
| replace ou wrap | wrap | o wrapper da coleção em volta da substituição no nível da aplicação, passada como OriginalComponent |
| qualquer | replace | o componente da coleção sozinho; ele substitui ambos |
Assim, uma coleção pode adicionar algo em volta de um componente que seu app já personaliza sem perder essa personalização. Para descartar a versão no nível da aplicação em uma coleção, substitua em vez de envolver.
Escopos de Componentes Substituíveis
Seção intitulada “Escopos de Componentes Substituíveis”Componentes com Escopo de Aplicação (AppComponentName)
Seção intitulada “Componentes com Escopo de Aplicação (AppComponentName)”Estes componentes só podem ser substituídos no nível do provedor raiz <Rebase>, pois representam a estrutura do shell.
| Chave do Componente | Descrição |
|---|---|
"Shell.AppBar" |
A barra de cabeçalho no topo da página |
"Shell.Drawer" |
O menu lateral (sidebar) principal colapsável de navegação |
"Shell.DrawerNavigationItem" |
Links individuais dentro da barra lateral |
"Shell.DrawerNavigationGroup" |
Cabeçalhos colapsáveis de grupos de navegação na barra lateral |
"HomePage" |
A página inicial padrão do modo de conteúdo |
"HomePage.CollectionCard" |
Cards de coleções individuais na página inicial |
"Auth.LoginView" |
A sobreposição exibida ao solicitar autenticação |
Componentes com Escopo de Coleção (CollectionComponentName)
Seção intitulada “Componentes com Escopo de Coleção (CollectionComponentName)”Estes componentes podem ser substituídos globalmente (agindo como padrões para todas as coleções) ou em coleções individuais.
| Chave do Componente | Descrição |
|---|---|
"Collection.View" |
Toda a página inicial da coleção |
"Collection.Table" |
A visualização tabular padrão em formato de planilha |
"Collection.Card" |
O wrapper do item na visualização em cards |
"Collection.EmptyState" |
Visualização exibida quando uma coleção está vazia |
"Collection.Actions" |
Botões da barra de ferramentas acima da tabela/cards |
"Collection.FilterField" |
Entrada de filtro personalizada para uma coluna |
"Entity.Form" |
O formulário detalhado para criação/atualização |
"EditView.FormActions" |
Barra de botões de envio/cancelamento do formulário |
"DetailView" |
Visualização de detalhes somente leitura |
"Entity.SidePanel" |
O contêiner do painel lateral para formulário/detalhes |
"EntityPreview" |
Pré-visualização inline de chip de referência/relação |
"Entity.MissingReference" |
Renderizado quando uma entidade referenciada está ausente |
O seu componente de substituição recebe as mesmas props que foram fornecidas ao componente integrado. O
mapa de substituição não nomeia um tipo de props por chave — ComponentOverride<P> define
P como padrão para Record<string, unknown> — portanto, tipifique o parâmetro você mesmo, ou passe um argumento
de tipo, quando desejar a verificação de tipos das props. Alguns dos componentes integrados exportam um
tipo de props que você pode importar e reutilizar: CollectionViewProps (@rebasepro/ui);
CollectionEmptyStateProps, CollectionActionsProps e
FilterFieldBindingProps (@rebasepro/cms-types); EntityFormProps e
EntityFormActionsProps (@rebasepro/cms). O restante não possui um tipo de props exportado —
escreva a estrutura que você realmente lê.
Relacionados
Seção intitulada “Relacionados”- Estendendo o Rebase — os pontos de extensão que não precisam de substituição
- Campos Personalizados — substituindo o editor de uma propriedade em vez de um componente
- Slots — adicionando conteúdo a um componente em vez de substituí-lo