Biblioteca React para gerenciamento de abas baseadas em rotas.
npm install tabistryou
yarn add tabistryPrimeiro, crie uma classe que estenda RouteTab:
import { RouteTab } from "tabistry";
interface UserParams {
id: string;
}
class UserTab extends RouteTab<UserParams> {
type = "user";
renderTab({ onRemove, isSelected }) {
return (
<div className={isSelected ? "selected" : ""}>
Usuário {this.params.id}
<button onClick={onRemove}>x</button>
</div>
);
}
renderScreen() {
return <UserDetails id={this.params.id} />;
}
}import { RouteDescriptor } from "tabistry";
const routes: RouteDescriptor<UserTab>[] = [
{
path: "/users/:id",
tab: UserTab,
},
];import { RouteTabs } from "tabistry";
function App() {
const [tabs, setTabs] = useState<UserTab[]>([]);
const handleAddTab = (tab: UserTab) => {
setTabs((prev) => [...prev, tab]);
};
const handleRemoveTab = (tab: UserTab) => {
setTabs((prev) => prev.filter((t) => t !== tab));
};
return (
<RouteTabs
tabs={tabs}
routes={routes}
onAddTab={handleAddTab}
onRemoveTab={handleRemoveTab}
/>
);
}import { RouteDescriptor } from "tabistry";
// 1. Defina suas tabs
class UserTab extends RouteTab<{ id: string }> {
type = "user";
// ...implementação da tab...
}
class ProductTab extends RouteTab<{ productId: string }> {
type = "product";
// ...implementação da tab...
}
// 2. Configure as rotas com suas respectivas tabs
const routes: RouteDescriptor[] = [
{
path: "/users/:id",
tab: UserTab,
element: <UserLayout />, // Layout opcional
children: [
{
path: "profile",
element: <UserProfile />,
},
{
path: "settings",
element: <UserSettings />,
},
],
},
{
path: "/products/:productId",
tab: ProductTab,
element: <ProductDetails />,
},
];O hook useRouteTabState permite acessar e manipular o estado das tabs de qualquer lugar da aplicação:
import { useRouteTabState } from "tabistry";
function TabList() {
const { tabs, tab, change, remove, isTabActive } = useRouteTabState();
return (
<div className="tab-list">
{tabs.map((t) => (
<div
key={t.type}
className={isTabActive(t) ? "active" : ""}
onClick={() => change(t)}
>
{t.renderTab({
onRemove: () => remove(t),
isSelected: isTabActive(t),
})}
</div>
))}
</div>
);
}import { RouteTabs, useRouteTabState } from "tabistry";
// Componente de Layout
function Layout({ children }) {
const { tabs, tab, change, remove, isTabActive } = useRouteTabState();
return (
<div>
<header>
<nav>
{tabs.map((t) => (
<TabButton
key={t.type}
tab={t}
isActive={isTabActive(t)}
onChange={change}
onRemove={remove}
/>
))}
</nav>
</header>
<main>{children}</main>
</div>
);
}
// Aplicação Principal
function App() {
const [tabs, setTabs] = useState<(UserTab | ProductTab)[]>([]);
const handleAddTab = (tab) => {
setTabs((prev) => [...prev, tab]);
};
const handleRemoveTab = (tab) => {
setTabs((prev) => prev.filter((t) => !t.equals(tab)));
};
return (
<BrowserRouter>
<RouteTabs
tabs={tabs}
routes={routes}
onAddTab={handleAddTab}
onRemoveTab={handleRemoveTab}
>
<Layout />
</RouteTabs>
</BrowserRouter>
);
}O hook retorna um objeto com as seguintes propriedades:
tab- Tab atualmente ativatabs- Array com todas as tabs abertaschange(tab)- Função para mudar para uma tab específicaremove(tab)- Função para remover uma tabisTabActive(tab)- Função que verifica se uma tab está ativa
Classe abstrata base para definição de tabs:
type: string- Identificador único do tipo da tabparams- Parâmetros da rotaquery- Parâmetros da query stringrenderTab(props: RenderTab)- Renderiza o conteúdo da abarenderScreen()- Renderiza o conteúdo principal da abaonFocus?()- Callback quando a aba recebe focoonBlur?()- Callback quando a aba perde focoonAdd?()- Callback quando a aba é adicionadaonRemove?()- Callback quando a aba é removida
tabs- Array de tabs ativasroutes- Configuração das rotasonAddTab- Callback quando uma nova tab é adicionadaonFocusTab- Callback quando uma tab recebe focoonBlurTab- Callback quando uma tab perde focoonRemoveTab- Callback quando uma tab é removida
- Gerenciamento automático de estado das tabs
- Sincronização com rotas do React Router
- Suporte a parâmetros de rota e query string
- Callbacks para eventos do ciclo de vida das tabs
- Verificação de tabs ativas
- Use o
typeda tab como identificador único - Implemente o método
equalsna sua tab para comparação personalizada - Mantenha a lógica de renderização dentro dos métodos
renderTaberenderScreen - Use os callbacks de ciclo de vida (
onFocus,onBlur, etc) para efeitos colaterais