Toolbar
Customize the PPTX editor toolbar with named controls, command slots, application actions, groups, and responsive overflow.
Customize the toolbar through named parts, slots, and groups. Each control keeps its command, enabled state, and refusal reason. For every identifier and rule, see the toolbar reference.
Tooltips appear immediately during keyboard navigation. Pointer tooltips wait briefly on the first control. Moving to nearby controls skips the delay and entry animation.
Customize a toolbar control
Pass className, icon, hidden, or asChild to a named part.
<PptxEditor.Toolbar>
<PptxEditor.Toolbar.Bold icon={<BoldIcon />} className="acme-bold" />
<PptxEditor.Toolbar.Strike hidden />
</PptxEditor.Toolbar><PptxEditorToolbar>
<PptxEditorToolbar.Bold className="acme-bold" />
<PptxEditorToolbar.Strike hidden />
</PptxEditorToolbar>hidden on a named part is shorthand for <Toolbar.Slot slotId="text.strike" hidden />. A control shows its state as data-active and data-disabled attributes for your CSS. A slot passes only className to its control. Any other attribute on it, such as style, id, or data-*, prints a development warning. For the part names, see Named parts.
Replace one control
Toolbar.Slot replaces one built-in control with your own content. Your content keeps the control's place and collapses with its group. No control renders, so className and icon on the slot (class in Vue) print a development warning. Put them on your content.
<PptxEditor.Toolbar>
<PptxEditor.Toolbar.Slot slotId="font.family">
<MyFontPicker />
</PptxEditor.Toolbar.Slot>
</PptxEditor.Toolbar><PptxEditorToolbar>
<PptxEditorToolbar.Slot slot-id="font.family">
<MyFontPicker />
</PptxEditorToolbar.Slot>
</PptxEditorToolbar>To keep the control's behavior on your own element, use asChild (as-child in Vue). Forward its attributes and events:
<PptxEditor.Toolbar.Bold asChild>
<button type="button" className="acme-button">
B
</button>
</PptxEditor.Toolbar.Bold><PptxEditorToolbar.Bold as-child>
<button type="button" class="acme-button">B</button>
</PptxEditorToolbar.Bold>Choose the toolbar arrangement
| Configuration | How to use it |
|---|---|
| One control | Pass className, icon, or hidden to its named part. |
| A different element | Use asChild on a button control. Forward its attributes and events. |
| One registered slot | Use Toolbar.Slot with slotId. |
| A built-in group | Use Toolbar.Group with its registered id. Add children or set hidden. |
| An application group | Choose another id, then set its label, position, and overflow behavior. |
| An application button | Use Toolbar.Action. |
| A complete arrangement | Set preset={false} and compose all visible controls. |
| Narrow widths | Keep overflow on, or set overflow={false} to let the bar wrap. A preset={false} bar always wraps. |
| Every group at all times | Set contextual={false}. By default, the bar shows only the groups for the selection. |
| Your own labels | Pass the t prop. Your parts read it with useChromeTranslate() or useToolbarLabel(). See Labels. |
For the groups that each selection shows, see Registered groups and slots.
Group your own controls
Put a Toolbar.Action in your own Toolbar.Group. The action keeps the slide selection. When the toolbar runs out of width, a whole group moves into the More menu. The group's label becomes its heading there.
<PptxEditor.Toolbar>
<PptxEditor.Toolbar.Group id="review" label="Review" priority={15} after="text">
<PptxEditor.Toolbar.Action label="Send for review" onSelect={send} />
</PptxEditor.Toolbar.Group>
<PptxEditor.Toolbar.Group id="find" hidden />
</PptxEditor.Toolbar><PptxEditorToolbar>
<PptxEditorToolbar.Group id="review" label="Review" :priority="15" after="text">
<PptxEditorToolbar.Action label="Send for review" @select="send" />
</PptxEditorToolbar.Group>
<PptxEditorToolbar.Group id="find" hidden />
</PptxEditorToolbar>Give your group a label, or a labelKey from your translation catalog. The id of a built-in group, such as find, changes that group instead. A lower priority leaves the bar first. hidden on a group removes the whole group. For every prop, see Group options and Action options.
Use the editor's icons
Icon draws the editor's Material Symbols as an inline SVG, so your controls match the toolbar. Both adapters export it.
<PptxEditor.Toolbar.Action label="Present" icon={<Icon name="slideshow" />} onSelect={present} />| Prop | Use |
|---|---|
name | A Material Symbol name, such as slideshow or close. An unknown name prints a development warning. |
paths | Your own SVG path data in place of name, for a 0 -960 960 960 view box. |
className | The class of the svg element. The default, pptx-icon, gives the toolbar size, 18px, inside the editor. Give "" or your own class to size the icon yourself. |
Compose the font picker
Toolbar.FontFamily is a compound. It holds the picker state in a part-level context. With no children, it renders the default trigger and the grouped list with a search field. With children, compose its parts:
Toolbar.FontFamily.Triggeris the button that opens the list. Its children replace the current family name.Toolbar.FontFamily.Contentis the list while it is open. Its children replace the search field and the default groups.Toolbar.FontFamily.Itemis one option. Itsvalueis a family name, or a theme font reference:+mj-ltfor the headings font,+mn-ltfor the body font. A theme item shows the theme's family and its role, and the text keeps the reference. The item carries the check mark while the text uses that font. Its children replace the name.
const { FontFamily } = PptxEditor.Toolbar;
<PptxEditor.Toolbar>
<FontFamily>
<FontFamily.Trigger className="acme-font-trigger" />
<FontFamily.Content>
<FontFamily.Item value="+mj-lt" />
<FontFamily.Item value="+mn-lt" />
<FontFamily.Item value="Arial">Arial (brand)</FontFamily.Item>
</FontFamily.Content>
</FontFamily>
</PptxEditor.Toolbar>;In Vue, use const FontFamily = PptxEditorToolbar.FontFamily and the same parts. Each part takes className. An item whose value names no font renders nothing. A part outside Toolbar.FontFamily renders nothing and prints a development warning.
Build the list from useFontFamily
useFontFamily() gives the picker's behavior with no UI. It returns:
value, the family of the selected text, ornullfor a mixed selection or no selection.theme, the theme font that the text uses (majororminor), ornull.options, every family the editor offers.groups, the default picker's groups for the current slide: the theme fonts (theme), the other families the deck uses (document), and all families (all). Each group has anid, alabelKeyto translate, androws. Each row hasfamily,value(a theme font reference on a theme row),roleKey, anddisabled.isCurrent(row), which is true for the row of the font that the text uses.setValue(family), which applies a family or a theme font reference. A refusal does nothing.isEnabledanddisabledReason, asuseEditorCommandgives them.
This list shows the theme fonts and the deck's fonts inside the default trigger and popover:
import { PptxEditor, useFontFamily, useTranslation } from '@pptx-editor.dev/react';
const { FontFamily } = PptxEditor.Toolbar;
function DeckFonts() {
const { groups } = useFontFamily();
const { t } = useTranslation();
return groups
.filter((group) => group.id !== 'all')
.map((group) => (
<div key={group.id} role="group" aria-label={t(group.labelKey)}>
{group.rows.map((row) => (
<FontFamily.Item key={row.value} value={row.value} />
))}
</div>
));
}
export function DeckFontToolbar() {
return (
<PptxEditor.Toolbar>
<FontFamily>
<FontFamily.Trigger />
<FontFamily.Content>
<DeckFonts />
</FontFamily.Content>
</FontFamily>
</PptxEditor.Toolbar>
);
}<script setup lang="ts">
import { computed } from 'vue';
import { PptxEditorToolbar, useFontFamily, useTranslation } from '@pptx-editor.dev/vue';
const FontFamily = PptxEditorToolbar.FontFamily;
const { groups } = useFontFamily();
const { t } = useTranslation();
const deckGroups = computed(() => groups.value.filter((group) => group.id !== 'all'));
</script>
<template>
<PptxEditorToolbar>
<FontFamily>
<FontFamily.Trigger />
<FontFamily.Content>
<div
v-for="group in deckGroups"
:key="group.id"
role="group"
:aria-label="t(group.labelKey)"
>
<FontFamily.Item v-for="row in group.rows" :key="row.value" :value="row.value" />
</div>
</FontFamily.Content>
</FontFamily>
</PptxEditorToolbar>
</template>To build a picker from nothing, replace the whole control with Toolbar.Slot. Render your own list from groups or options, isCurrent, and setValue. See Replace one control. To apply a theme font from code, see Theme fonts.
Place the shape format controls
At 1440 px, the default bar has no room for Quick Styles, Shape Effects, Text Effects, Rotate, Format Shape, and Selection Pane. It leaves them out. The ribbon's Shape Format tab shows them. Place them with their named parts or with Toolbar.Slot. The Vue toolbar draws these controls too.
<PptxEditor.Toolbar>
<PptxEditor.Toolbar.Group id="shape">
<PptxEditor.Toolbar.ShapeStyles />
<PptxEditor.Toolbar.ShapeEffects />
<PptxEditor.Toolbar.TextEffects />
</PptxEditor.Toolbar.Group>
<PptxEditor.Toolbar.Group id="arrange">
<PptxEditor.Toolbar.Rotate />
</PptxEditor.Toolbar.Group>
</PptxEditor.Toolbar>Toolbar.ShapeStyles(shape.styles) applies a theme style (p:style) to the selected shapes.Toolbar.ShapeEffects(shape.effects) sets and removes a shadow, reflection, glow, soft edge, bevel, or 3-D rotation. The inner shadow, reflection, glow, soft edge, bevel, and 3-D rotation paint only with the drawing module of@pptx-editor.dev/pro(EigenPal Pro License). Without the module, the menu offers the outer shadows and No shadow, andToolbar.ImageStyles(image.styles) offers only the frame styles. Removing an effect needs no module. When the selected shapes or pictures have a reflection, glow, soft edge, bevel, or turned camera, the menu also offers No reflection, No glow, No soft edges, No bevel, or No rotation. No shadow also removes an inner shadow. The commands still write every effect, and save keeps it. The controls and theuseEditorValueCommandoptions refresh after the module installs and when the selection or its effects change. A custom control reads the choices withchromeOfferedControl(slot, control, editor). Read them again on each editor change, and aftersubscribeDrawingRenderersfrom@pptx-editor.dev/core/layoutcalls back.Toolbar.TextEffects(shape.textEffects) sets or removes the text warp of the selected shapes from its Transform gallery. A value istransform.and a warp name, such astransform.textArchUp, ortransform.none. The gallery sets the warp's default adjust values. To set other adjust values, run thesetTextWarpcommand. See Warp text.Toolbar.Rotate(arrange.rotate) turns the selection 90° or flips it.Toolbar.FormatShape(shape.properties) andToolbar.SelectionPane(arrange.selectionPane) callrequestPane(editor, pane). A mounted side panel turns to its Format or Selection tab. While no side panel is mounted and nolistenPaneRequestslistener is registered, the controls are disabled with thedisabled.noSidePanelreason. See Open the side panel from the toolbar.
Apply a typed value
useEditorValueCommand(slotId) binds a value slot with its type from ToolbarValueMap. A typo in a value does not compile.
import { useEditorValueCommand } from '@pptx-editor.dev/react';
export function RotateRightButton() {
const rotate = useEditorValueCommand('arrange.rotate');
return (
<button
type="button"
aria-disabled={!rotate.isEnabled || undefined}
onMouseDown={(event) => event.preventDefault()}
onClick={() => {
const result = rotate.execute('right90');
if (!result.ok) console.info(result.reason);
}}
>
Rotate right
</button>
);
}In Vue, the members are refs, such as isEnabled.value. useEditorValueCommand('shape.effects').execute('shadow.outerBottomRight') adds a shadow.
| Member | Use |
|---|---|
execute(value) | Runs the command and returns the engine's CommandResult. A refused value gives ok: false and the reason, such as disabled.invalidValue or disabled.noEffectChange. |
value | The value that the slot shows. null when the selection is mixed or the slot shows no value (the highlight, shape fill, line color, line spacing, and Arrange slots). |
options | The slot's fixed choices from the registry, such as the Rotate, Align, line width, and Shape Effects rows. A slot with a free value, such as font.size, shape.fill, or shape.styles, has none. |
isEnabled, disabledReason | As useEditorCommand gives them. |
Numeric slots give numbers: font.size and shape.lineWidth in value and options, and list.lineSpacing in options. shape.effects shows one choice for each effect kind, separated by spaces (shadow.outerTop reflection.none glow.none softEdge.none bevel.none rotation3d.none). Test one choice with chromeOptionChecked(value, choice) from @pptx-editor.dev/core/editor.
Build a control from a command
Build a control from useEditorCommand when Toolbar.Action does not fit. Keep the selection, keep the control focusable when it is disabled, and give the reason.
import { translateKeyOrText } from '@pptx-editor.dev/core/editor';
import { useEditorCommand, useTranslation } from '@pptx-editor.dev/react';
export function PresentButton() {
const { t } = useTranslation();
const present = useEditorCommand('view.playFromStart');
const reason = present.disabledReason;
return (
<button
type="button"
aria-disabled={!present.isEnabled || undefined}
title={reason ? translateKeyOrText(t, reason) : undefined}
onMouseDown={(event) => event.preventDefault()}
onClick={() => {
if (present.isEnabled) present.execute();
}}
>
Present
</button>
);
}<script setup lang="ts">
import { computed } from 'vue';
import { translateKeyOrText } from '@pptx-editor.dev/core/editor';
import { useEditorCommand, useTranslation } from '@pptx-editor.dev/vue';
const { t } = useTranslation();
const present = useEditorCommand('view.playFromStart');
const reason = computed(() => {
const value = present.disabledReason.value;
return value ? translateKeyOrText(t, value) : undefined;
});
function run() {
if (present.isEnabled.value) present.execute();
}
</script>
<template>
<button
type="button"
:aria-disabled="!present.isEnabled.value || undefined"
:title="reason"
@mousedown.prevent
@click="run"
>
Present
</button>
</template>Render PresentButton inside PptxEditor.Root, and mount PptxEditor.Slideshow in the viewport so the show has a place to play. In Vue, render it inside PptxEditorRoot and mount PptxEditorSlideshow. For the state rules, see Command state.
Build a layout picker
LayoutGallery shows the deck's slide layouts as a Layout or New slide gallery. It has one group for each slide master. Each tile is the engine's own render of a new slide on that layout. Its tiles are menu rows, so you can put the gallery in any menu or popup of your own.
import { LayoutGallery, useEditorCommand, useSlideLayouts } from '@pptx-editor.dev/react';
export function MyLayoutMenu() {
const layout = useEditorCommand('slide.layout');
return (
<div role="menu" aria-label="Layout">
<LayoutGallery
value={String(layout.value ?? '')}
disabled={!layout.isEnabled}
disabledReason={layout.disabledReason}
onChange={(id) => layout.execute(id)}
/>
</div>
);
}
export function MyNewSectionMenu() {
const add = useEditorCommand('slide.add');
const sections = useSlideLayouts().filter((l) => l.type === 'secHead');
return (
<div role="menu" aria-label="New section">
<LayoutGallery layouts={sections} onChange={(id) => add.execute(id)} />
</div>
);
}The Layout menu marks the current layout. The New section menu lists only the section header layouts. Render both inside PptxEditor.Root.
| Prop | Description |
|---|---|
onChange | Called with the picked layout's id. In Vue, listen to change. |
value | The layout to mark as the current one. Without it, the tiles are plain menu rows. |
layouts | The layouts to show. The default is every layout of the deck. |
disabled, disabledReason | Show the tiles but take no pick, and the reason for their tooltips. The default is disabled while the deck is open for viewing. |
className | A class name added to the gallery's root element. |
useSlideLayouts() returns the deck's layouts, in gallery order. Each one has id, name, type (the p:sldLayout/@type value, such as title or secHead), masterId, and masterName. The array keeps its identity while the layouts do not change. Run the pick through useEditorCommand. slide.layout changes the layout of the selected slides, and its value is the layout of the current slide. slide.add adds a slide with the layout. For the Vue props, see React and Vue reference.
Limits
- Hiding a control does not remove its operation. Menus, shortcuts, and the ribbon still run it.
- Keep the caret when a toolbar button receives a mouse press. Call
preventDefault()on mouse down for buttons, but skip inputs, selects, and text areas. - A picker has no single element, so
asChildon a picker prints a development warning and does nothing. - The Vue toolbar draws the same controls as React's. See React and Vue reference.