Composables

Connect Vue controls to PPTX editor state and commands with composables.

Call editor-state composables during setup() inside PptxEditorRoot. The packaged <PptxEditor> provides that root for its default slot. Each composable exports a named return type, Use<Name>Return.

usePptxSource and useFonts prepare input for the root. Call them in the parent component's setup() when needed.

Connect a command button

This button runs a toolbar command and shows why the command cannot run:

<script setup lang="ts">
import { computed } from 'vue';
import { useEditorCommand, useTranslation } from '@pptx-editor.dev/vue';
import { translateKeyOrText } from '@pptx-editor.dev/core/editor';

const bold = useEditorCommand('text.bold');
const { t } = useTranslation();
const title = computed(() => {
  const reason = bold.disabledReason.value;
  return reason ? translateKeyOrText(t, reason) : t('toolbar.bold');
});
</script>

<template>
  <button
    type="button"
    :aria-disabled="!bold.isEnabled.value || undefined"
    :aria-pressed="bold.isActive.value"
    :aria-label="t('toolbar.bold')"
    :title="title"
    @mousedown.prevent
    @click="bold.execute()"
  >
    B
  </button>
</template>

useEditorCommand(slot: MaybeRefOrGetter<ChromeSlotId>): UseEditorCommandReturn takes a slot identifier from the toolbar slot reference, or a ref or getter that returns one. The state follows the slot.

MemberTypeDescription
execute(value?)booleanRuns the slot's command, with a value for value slots. Returns true when the engine ran it, and false on a refusal.
isEnabledComputedRef<boolean>Whether the command can run now.
isActiveComputedRef<boolean>Whether a toggle control shows as pressed.
disabledReasonComputedRef<string | null>A translation key, or the engine's own text, when the command cannot run.
valueComputedRef<string | number | undefined>The value a value slot shows, such as a font size or a family.

The types in these signatures, such as ChromeSlotId, CommandResult, EditorSnapshot, and PptxEditorInstance, are exported from @pptx-editor.dev/vue. You do not need an import from @pptx-editor.dev/core to type your handlers.

Read state

useEditorState returns a read-only shallow ref for one slice of the snapshot. The ref changes only when that slice changes:

<script setup lang="ts">
import { useEditorState } from '@pptx-editor.dev/vue';

const current = useEditorState((s) => s.currentSlide);
const total = useEditorState((s) => s.slides.length);
</script>

<template>
  <span>{{ current + 1 }} / {{ total }}</span>
</template>

Pass a comparator for an object slice. Common fields include status, slides, currentSlide, selection, format, mode, canUndo, canRedo, presentationView, lastRejection, and lastRemoval.

Templates unwrap only top-level refs:

ValueIn a templateIn script
Top-level or destructured ref, such as currentcurrentcurrent.value
Ref inside a returned objectbold.isEnabled.valuebold.isEnabled.value
ComposableReturn typeShape
useEditorState(selector, isEqual?)UseEditorStateReturn<T>Readonly<ShallowRef<T>>. isEqual defaults to Object.is.
usePptxEditor()UsePptxEditorReturnReadonly<ShallowRef<PptxEditorInstance | null>>. null before the editor mounts and outside a root.
useEditorColorMode()UseEditorColorModeReturn{ colorMode: ComputedRef<ResolvedColorMode> }: 'light' or 'dark', after 'system' resolves.
useTranslation()UseTranslationReturn{ t: Translate }. t follows the root's locale. See Language.

usePptxEditor

Use the instance for actions and one-time reads:

<script setup lang="ts">
import { usePptxEditor } from '@pptx-editor.dev/vue';

const props = defineProps<{ upload: (bytes: Uint8Array) => Promise<void> }>();
const editor = usePptxEditor();

function save() {
  const saved = editor.value?.save();
  if (saved) void props.upload(saved);
}
</script>

<template>
  <button :disabled="!editor" @click="save">Save</button>
</template>

A direct snapshot() call does not create a Vue subscription. Use useEditorState for values that you render.

Choose a composable

State members are refs. Function members are plain functions.

Formatting

ComposableReturn typeMembers
useFontFormat()UseFontFormatReturnformat: UseEditorStateReturn<FontFormatRead | null>. isEnabled: ComputedRef<boolean>. apply(update: FontFormatCommand): writes the fields as one undo step and returns whether the engine accepted them.
useParagraphFormat()UseParagraphFormatReturnformat: UseEditorStateReturn<ParagraphFormatRead | null>. isEnabled. apply(update: ParagraphFormatCommand): one undo step.
useBulletFormat()UseBulletFormatReturnformat: UseEditorStateReturn<BulletFormatRead | null | undefined>; null is no list, and undefined means the paragraphs differ. isEnabled. apply(format: BulletFormat | null): null turns the list off.
useEditorValueCommand(slot)UseEditorValueCommandReturn<V, S>A typed value slot. See Apply a value.

Slides and panes

ComposableReturn typeMembers
useSlideLayouts()UseSlideLayoutsReturnUseEditorStateReturn<readonly SlideLayoutSummary[]>: the deck's slide layouts, in gallery order.
useNotesPane()UseNotesPaneReturnopen: ComputedRef<boolean>. setOpen(open) and toggle().
useSlideshow()UseSlideshowReturncontroller: Readonly<ShallowRef<SlideshowController | null>>. start('start' | 'current') and stop().

Dialogs, menus, and notices

ComposableReturn typeMembers
useEditorDialog(key)UseEditorDialogReturnisOpen and isAvailable: ComputedRef<boolean>. open(returnFocusTo?) and close(). key is a PptxEditorDialogKey. See Open a dialog from your control.
usePageSetupDialog(), useParagraphDialog(), useFontDialog(), useBulletsAndNumberingDialog(), useColumnsDialog(), useImageAltTextDialog(), useImagePropertiesDialog()UsePageSetupDialogReturn, UseParagraphDialogReturn, and so on: each is UseDialogReturn<Fields>The draft of an open dialog. values, errors, and isEnabled are refs. setValue(name, value), apply(), and cancel(). Call them inside the dialog. See Dialog parts and draft state.
useBackgroundDialog()UseBackgroundDialogReturnUseDialogReturn<BackgroundDialogFields> with reset().
useContextMenuRequest()UseContextMenuRequestReturnContextMenuRequest | null: the open canvas context menu. Each request mounts the menu anew, so the value is fixed for the menu's life.
useContextMenuTarget()UseContextMenuTargetReturnHTMLElement | null: the element the open context menu opened on.
useOpenFailure({ t? })UseOpenFailureReturnfailure, message, and kept: computed refs. dismiss(failure?). t translates message. See Show a banner or a toast.
useHyperlinkPopup(), useHyperlinkPopupInstance(active?)UseHyperlinkPopupReturn, UseHyperlinkPopupInstanceReturnstate: a computed ref to the link popup's state. The actions of the React hook.

Fonts

ComposableReturn typeMembers
useFonts(source, ...fragments) or useFonts(...origins)UseFontsReturnA MarkedFontResolver with a stable identity, for the fonts prop. Each argument can be a ref or a getter. See Fonts.
useFontFamily()UseFontFamilyReturnvalue, theme, options, groups, isEnabled, and disabledReason: computed refs. isCurrent(row) and setValue(family). Use it for your own font picker. See Build the list from useFontFamily.
usePptxSource(source, options?)UsePptxSourceReturndocument, fonts, error, and isLoading: computed refs. source and options can be refs or getters. See the font loading and download rules in Open a presentation.

Build your own chrome

Call these composables in setup to connect your controls to the editor.

Zoom

useZoom(): UseZoomReturn reads and sets the slide zoom:

<script setup lang="ts">
import { useZoom } from '@pptx-editor.dev/vue';

const { zoom, isFit, fit, zoomIn, zoomOut, canZoomIn, canZoomOut } = useZoom();
</script>

<template>
  <div role="group" aria-label="Zoom">
    <button :disabled="!canZoomOut" aria-label="Zoom out" @click="zoomOut">-</button>
    <span>{{ Math.round(zoom * 100) }}%</span>
    <button :disabled="!canZoomIn" aria-label="Zoom in" @click="zoomIn">+</button>
    <button :aria-pressed="isFit" @click="fit">Fit</button>
  </div>
</template>

zoom, isFit, canZoomIn, and canZoomOut are computed refs. setZoom(zoom), fit(), reset(), zoomIn(), and zoomOut() are functions, and levels is an array. The members are the same as in the React hook.

Events

useEditorEvent(event, handler): void calls handler each time the event occurs, until the component unmounts:

import { useEditorEvent } from '@pptx-editor.dev/vue';

useEditorEvent('rejection', (rejection) => console.info(rejection.reason));

The events are change, selectionChange, rejection, and openFailure, as in the React hook.

Other composables

ComposableReturn typeMembers
useEditorSnapshot(editor)Readonly<Ref<number>>A counter that changes on each editor change. editor can be a ref or a getter. Prefer useEditorState.
useScopeClassName()UseScopeClassNameReturnComputedRef<'' | 'pptx-editor ' | 'pptx-editor dark '>: the scope classes for the root element of your own part. See Style your controls.
useEditorHostDocument()UseEditorHostDocumentReturnReadonly<ShallowRef<Document | null>>: the document that the slide is mounted in.

Find, caret, and undo steps

ComposableReturn typeMembers
useDocumentSearch()UseDocumentSearchReturnThe find session as computed refs (query, matchCase, wholeWord, matches, activeIndex, activeMatch), and the actions of the React hook.
useEditorCaret()UseEditorCaretReturnReadonly<Ref<EditorCaret | null>>: the caret while text is edited.
useHistoryGroup({ kind })UseHistoryGroupReturnref, a function ref for the control, and options() for each command of the gesture.
<script setup lang="ts">
import { useEditorValueCommand, useHistoryGroup } from '@pptx-editor.dev/vue';

const group = useHistoryGroup({ kind: 'range' });
const size = useEditorValueCommand('font.size');
</script>

<template>
  <input
    type="range"
    aria-label="Font size"
    :ref="group.ref"
    @input="size.execute(Number(($event.target as HTMLInputElement).value), group.options())"
  />
</template>

Labels

ComposableReturn typeMembers
useChromeTranslate(overrides?)UseChromeTranslateReturnThe t of the nearest compound, else the root's locale.
useToolbarContext()UseToolbarContextReturn{ t }: a ref to the host's t on the nearest compound.
useToolbarLabel(), useToolbarLabelFor(t)UseToolbarLabelReturn, UseToolbarLabelForReturn(key) => string, as in React.

PptxEditorToolbar, PptxEditorMenu, PptxEditorContextMenu, PptxEditorSlideRail, PptxEditorStatusBar, PptxEditorFindBar, PptxEditorOpenFailure, PptxEditorFontNotice, and PptxEditorEditNotice take the t prop.

Apply a value

useEditorValueCommand reads the value of a value slot and applies a new one, with the slot's type from ToolbarValueMap. This slider sets the font size:

<script setup lang="ts">
import { ref } from 'vue';
import { useEditorValueCommand } from '@pptx-editor.dev/vue';

const size = useEditorValueCommand('font.size');
const error = ref<string | null>(null);

function apply(event: Event) {
  const result = size.execute(Number((event.target as HTMLInputElement).value));
  error.value = result.ok ? null : result.reason;
}
</script>

<template>
  <label :title="size.disabledReason.value ?? undefined">
    {{ size.value.value === null ? 'Mixed' : `${size.value.value} pt` }}
    <input
      type="range"
      min="8"
      max="96"
      :value="size.value.value ?? 18"
      :disabled="!size.isEnabled.value"
      @input="apply"
    />
    <output aria-live="polite">{{ error }}</output>
  </label>
</template>

font.size takes points, or { step: 1 } and { step: -1 } for one step of the size list. null means mixed or unavailable.

MemberTypeDescription
execute(value, options?)CommandResultRuns the command. A refusal gives ok: false and the reason, such as disabled.invalidValue.
valueComputedRef<S | null>The value the slot shows, or null when mixed or unavailable.
optionsComputedRef<readonly V[]>The slot's fixed choices. A slot with a free value has none.
isEnabled, disabledReasonComputedRef<boolean>, ComputedRef<string | null>As useEditorCommand gives them.

The two composables return different results from execute: useEditorCommand returns a boolean, because a toolbar button only needs to know if the command ran, and useEditorValueCommand returns the CommandResult, because a value control shows why the engine refused a value.

To make the values of one slider drag one undo step, pass { historyGroup } from editor.value.beginHistoryGroup(). See Group a slider or a stepper.

Limits

  • Call composables during setup() inside PptxEditorRoot. Outside it, usePptxEditor().value is null.
  • canUndo reports undo availability, not unsaved changes. Track saves yourself.
  • Dialog draft composables read a draft. They do not open the dialog; use useEditorDialog(key) for that.
  • disabledReason can be a translation key. Show it with translateKeyOrText(t, reason) from @pptx-editor.dev/core/editor.

See also