/* Copyright 2026 Marimo. All rights reserved. */ import React, { type JSX, type PropsWithChildren } from "react"; import { z } from "zod"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "../../components/ui/tabs"; import { renderHTML } from "../core/RenderHTML"; import type { IPlugin, IPluginProps } from "../types"; import { Labeled } from "./common/labeled"; interface Data { /** * The labels for each tab; raw HTML. */ tabs: string[]; label: string | null; } // Selected tab index type T = string; export class TabsPlugin implements IPlugin { tagName = "marimo-tabs"; validator = z.object({ tabs: z.array(z.string()), label: z.string().nullable(), }); render(props: IPluginProps): JSX.Element { return ( {props.children} ); } } interface TabComponentProps extends Data { value: T; setValue: (value: T) => void; } const TabComponent = ({ tabs, label, value, setValue, children, }: PropsWithChildren): JSX.Element => { // We use the index since labels are raw HTML and can't be used as keys // Tabs default to the first tab if the value is not set const [internalValue, setInternalValue] = React.useState(value || "0"); const handleChange = (newValue: T) => { setInternalValue(newValue); setValue(newValue); }; // Reset the internal value if the value is changed externally // and not empty if (value !== internalValue && !!value) { setInternalValue(value); } return ( {tabs.map((tab, index) => ( {renderHTML({ html: tab })} ))} {React.Children.map(children, (child, index) => { return {child}; })} ); };