Skip to main content

Document Toolbar

The DocumentToolbar renders a default set of document controls for common document actions: undo, redo, download, document name editing, revision history, opening in Switchboard, and closing the current document view.

The toolbar can be customized by enabling or disabling built-in controls, replacing individual controls, adding custom controls to toolbar slots, replacing the toolbar containers, applying custom styles, or replacing the entire toolbar contents with children.

Document Toolbar

The Document Toolbar can be found at the top of any generic document.

Basic usage

import { DocumentToolbar } from "./document-toolbar";

export function MyDocumentPage({ document }) {
return <DocumentToolbar document={document} />;
}

When no document prop is provided, the toolbar falls back to the currently selected document.

<DocumentToolbar />

Built-in controls

The default toolbar includes these controls:

["undo", "redo", "download", "name", "history", "switchboard", "close"]

The controls are arranged into three slots:

{
first: ["undo", "redo", "download"],
second: ["name"],
third: ["history", "switchboard", "close"],
}

Disabling controls

Use disabledControls to remove specific built-in controls from the default toolbar.

<DocumentToolbar
document={document}
disabledControls={["download", "switchboard"]}
/>

Enabling only specific controls

Use enabledControls to render only a subset of the built-in controls.

<DocumentToolbar
document={document}
enabledControls={["download", "switchboard"]}
/>

Combining enabled and disabled controls

A control renders only when it is included in enabledControls and absent from disabledControls. When a control appears in both lists, disabledControls takes precedence.

<DocumentToolbar
document={document}
enabledControls={["download", "switchboard", "close"]}
disabledControls={["download"]}
/>

In this example, only switchboard and close render.

Adding a custom control to a slot

Use customControls to add controls to a specific toolbar slot. Custom controls receive the current document when available.

<DocumentToolbar
document={document}
customControls={{
first: {
component: ({ document }) => (
<button
onClick={() => {
alert(`custom control one in ${document?.header.name}`);
}}
>
custom one
</button>
),
},
}}
/>

Adding a custom control at the end of a slot

By default, custom controls render at the start of their slot. Use position: "end" to render a custom control after the built-in controls in that slot.

<DocumentToolbar
document={document}
customControls={{
second: {
position: "end",
component: ({ document }) => (
<button
onClick={() => {
alert(`custom control two in ${document?.header.name}`);
}}
>
custom two
</button>
),
},
}}
/>

Adding multiple custom controls to a slot

A slot can receive a list of custom controls. Each item needs a key.

<DocumentToolbar
document={document}
customControls={{
first: [
{
key: "custom-one",
component: ({ document }) => (
<button
onClick={() => {
alert(`custom control one in ${document?.header.name}`);
}}
>
custom one
</button>
),
},
{
key: "custom-two",
position: "end",
component: ({ document }) => (
<button
onClick={() => {
alert(`custom control two in ${document?.header.name}`);
}}
>
custom two
</button>
),
},
],
}}
/>

Replacing the toolbar container

Use toolbarContainer to replace the outer toolbar container. The custom toolbar container receives the toolbar children and the resolved toolbarClassName.

<DocumentToolbar
document={document}
toolbarContainer={({ children, className }) => (
<div className={className}>
{children}
</div>
)}
/>

Replacing the controls container

Use controlsContainer to replace the container used for each toolbar slot. The custom controls container receives the slot children and the resolved controlsContainerClassName.

<DocumentToolbar
document={document}
controlsContainer={({ children, className }) => (
<div className={className}>{children}</div>
)}
/>

Replacing a built-in control

Use componentOverrides to replace individual built-in controls while keeping the default toolbar structure. This is useful when you want to reuse the built-in control behavior or styling but change part of the rendering or click behavior.

import { ToolbarDownloadButton } from "./toolbar-button";
import type { PHDocument } from "@powerhousedao/shared/document-model";

<DocumentToolbar
document={document}
componentOverrides={{
download: (props: { document?: PHDocument }) => (
<ToolbarDownloadButton
{...props}
onClick={() => alert(props.document?.header.name)}
>
<span>Download??</span>
</ToolbarDownloadButton>
),
}}
/>

The name control can also be replaced through componentOverrides.

<DocumentToolbar
document={document}
componentOverrides={{
name: ({ document }) => (
<textarea defaultValue={document?.header.name ?? "no name"} />
),
}}
/>

Replacing the entire toolbar contents

Pass children when you want complete control over the toolbar contents. When children is provided, the built-in controls are not rendered.

<DocumentToolbar document={document}>
<div>I've totally overridden the whole thing</div>
</DocumentToolbar>

Props such as enabledControls, disabledControls, customControls, componentOverrides, controlsContainer, and controlsContainerClassName are not used in this mode. The props are typed so that the children mode is mutually exclusive with the default-toolbar customization props.

Custom styles

Use toolbarClassName to add classes to the outer toolbar container. Use controlsContainerClassName to add classes to each controls container. The built-in toolbar components use tailwind-merge, so compatible Tailwind classes passed through these props can override default classes.

<DocumentToolbar
document={document}
toolbarClassName="border-none bg-green-100"
controlsContainerClassName="rounded-lg border border-green-300 p-2"
/>

Custom control component shape

Custom controls should match ToolbarControlComponent.

import type { ToolbarControlComponent } from "./document-toolbar";

const MyControl: ToolbarControlComponent = ({ document }) => {
return <button>{document?.header.name}</button>;
};

Toolbar button props

The built-in toolbar button controls accept ToolbarButtonProps. Each toolbar button accepts:

{
document?: PHDocument;
children?: ReactNode;
className?: string;
onClick?: (document?: PHDocument) => void;
}

Providing children replaces the default button contents. Providing onClick replaces the default button behavior.

<DocumentToolbar document={document}>
<ToolbarDownloadButton document={document}>
Export
</ToolbarDownloadButton>
</DocumentToolbar>

<ToolbarHistoryButton
document={document}
onClick={(document) => {
console.log("Custom history action", document?.header.id);
}}
/>

Exported components

DocumentToolbar
ToolbarButton
ToolbarUndoButton
ToolbarRedoButton
ToolbarDownloadButton
ToolbarSwitchboardButton
ToolbarHistoryButton
ToolbarCloseButton
ToolbarName
ToolbarInput
ToolbarContainer
ToolbarControlsContainer

The individual toolbar controls are exported so they can be reused when building custom toolbar layouts or custom control components.

Exported types

DocumentToolbarProps
DocumentToolbarWithChildrenProps
DocumentToolbarWithCustomControlsProps
DocumentToolbarControlName
ToolbarButtonProps
ToolbarButtonClickHandler
ToolbarControlComponent
ToolbarControlComponents
ContainerComponent
ControlSlot
ControlPosition
CustomToolbarControl
CustomToolbarControlList
CustomToolbarControls

Notes

  • DocumentToolbar falls back to the currently selected document when document is omitted.
  • Built-in undo and redo buttons automatically disable themselves when the corresponding action is unavailable.
  • The document name control supports inline renaming.
  • children mode replaces the toolbar contents completely.
  • disabledControls takes precedence over enabledControls.