Skip to content

Ribbon toolbar ​

Pass toolbar to Spreadsheet.mount() and the ribbon is mounted in the same call, above the formula bar:

ts
import '@libraz/formulon-cell/styles.css'
import { Spreadsheet, type RibbonTab } from '@libraz/formulon-cell'

const instance = await Spreadsheet.mount(host, {
  ui: { profile: 'excel365' },
  toolbar: {
    activeTab: (localStorage.getItem('ribbon-tab') as RibbonTab | null) ?? 'home',
    onTabChange: (tab) => localStorage.setItem('ribbon-tab', tab),
  },
})

instance.toolbar?.setActiveTab('data')

toolbar: true uses the defaults. An options object is a MountToolbarOptions, the same type Spreadsheet.mountToolbar() takes. The mounted ribbon is available as instance.toolbar, a ToolbarInstance; it is null when no ribbon is mounted. When toolbar is omitted, the ui profile decides whether a ribbon appears; see Options.

Like the rest of formulon-cell, the ribbon is beta. Its tabs follow a desktop spreadsheet, but individual commands, dropdowns, and keyboard behavior are not guaranteed to match one. Evaluate it as an integration surface for your application rather than a finished end-user toolbar.

Inline or separate ribbon ​

Inline: toolbar mount optionSeparate: Spreadsheet.mountToolbar()
PlacementFirst child of the spreadsheet host, above the formula bar.Any element the host owns, such as a title bar or an application header.
Handleinstance.toolbarThe returned ToolbarInstance.
Runtime changesinstance.setToolbar(next) disposes the ribbon and mounts it again; false removes it.Call dispose() and mount again.
Dropdown menusOn while no interaction policy is active; setPolicy() rebuilds the ribbon to match.Off until you pass dynamicDropdowns.
Accent colorFollows the grid's --fc-accent.Uses the toolbar defaults; set --fc-tb-* on a wrapper.
DisposalReleased by instance.dispose().Call toolbar.dispose() before disposing the instance.

Use the separate form when the ribbon belongs somewhere else in the layout:

ts
import { Spreadsheet } from '@libraz/formulon-cell'

const instance = await Spreadsheet.mount(sheetHost, { ui: { profile: 'excel365' }, toolbar: false })
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
  dynamicDropdowns: true,
  onCommand: (id, applied) => {
    if (!applied) handleHostCommand(id)
  },
})

function close() {
  toolbar.dispose()
  instance.dispose()
}

mountToolbar(host, instance, options) throws when host or instance is missing. The second argument may also be a getter, () => SpreadsheetInstance | null, so the ribbon can render before an asynchronous mount finishes. The getter is read on every dispatch; call toolbar.rerender() once it starts returning the instance so the ribbon subscribes to that instance's state.

The ribbon shell is a flex item. The framework components render a display: contents wrapper so the ribbon takes part in the parent's flex column; give a vanilla toolbarHost the same treatment, or make it a flex column itself.

React and Vue components ​

React exports SpreadsheetToolbar from @libraz/formulon-cell-react. Vue publishes it as a single-file component under the @libraz/formulon-cell-vue/toolbar.vue subpath, so it compiles with the application's own Vue pipeline. Both wrap Spreadsheet.mountToolbar() and take the instance from onReady or the ready event.

ts
import { Spreadsheet } from '@libraz/formulon-cell'

const instance = await Spreadsheet.mount(sheetHost, { ui: { profile: 'excel365' }, toolbar: false })
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
  activeTab: 'home',
  dynamicDropdowns: { applyProtectAction: (action) => openProtectDialog(action) },
  hooks: { review: { spelling: () => openSpellChecker() } },
  onTabChange: (tab) => saveActiveTab(tab),
})
tsx
import { useState } from 'react'
import {
  Spreadsheet,
  SpreadsheetToolbar,
  type RibbonTab,
  type SpreadsheetInstance,
  type ToolbarInstance,
} from '@libraz/formulon-cell-react'

// Module constants keep the references stable; a new object re-mounts the ribbon.
const ui = { profile: 'excel365' } as const
const dropdownActions = {
  applyProtectAction: (action: string) => openProtectDialog(action),
}

export function Editor() {
  const [instance, setInstance] = useState<SpreadsheetInstance | null>(null)
  const [tab, setTab] = useState<RibbonTab>('home')
  const [toolbar, setToolbar] = useState<ToolbarInstance | null>(null)

  return (
    <div className="editor">
      <SpreadsheetToolbar
        instance={instance}
        activeTab={tab}
        onTabChange={setTab}
        locale="en"
        dropdownActions={dropdownActions}
        onSpellingReview={() => openSpellChecker()}
        onToolbarReady={setToolbar}
      />
      <Spreadsheet ui={ui} toolbar={false} onReady={setInstance} />
    </div>
  )
}
vue
<script setup lang="ts">
import { shallowRef, ref } from 'vue'
import {
  Spreadsheet,
  type RibbonTab,
  type SpreadsheetInstance,
} from '@libraz/formulon-cell-vue'
import SpreadsheetToolbar from '@libraz/formulon-cell-vue/toolbar.vue'

const ui = { profile: 'excel365' } as const
const dropdownActions = {
  applyProtectAction: (action: string) => openProtectDialog(action),
}
const instance = shallowRef<SpreadsheetInstance | null>(null)
const tab = ref<RibbonTab>('home')
</script>

<template>
  <div class="editor">
    <SpreadsheetToolbar
      :instance="instance"
      :active-tab="tab"
      locale="en"
      :dropdown-actions="dropdownActions"
      :on-spelling-review="openSpellChecker"
      @tab-change="tab = $event"
    />
    <Spreadsheet :ui="ui" :toolbar="false" @ready="instance = $event" />
  </div>
</template>

instance, activeTab, and locale are required; React also requires onTabChange, and Vue reports tab changes as the tab-change event. instance may be null until the spreadsheet is ready. The optional props are:

PropUse
ribbonTabsThe tab set; see Tabs and display.
dropdownActionsReplace individual dropdown handlers; see Dropdown handlers.
onToolbarReadyReceives the ToolbarInstance, and null when the ribbon is torn down or fails to mount.
onErrorCalled when the ribbon fails to mount. Vue also emits error.
onSpellingReview, onAccessibilityCheck, onTranslate, onRunScript, onAddIn, onDrawPen, onDrawEraserHost callbacks; see Host callbacks.

The components set the ribbon language from locale and mount again when instance, locale, dropdownActions, or ribbonTabs changes. A changed activeTab is forwarded to setActiveTab() without a re-mount. Pass dropdownActions and ribbonTabs as stable references (a module constant, useMemo, or a value defined once in setup).

MountToolbarOptions ​

Every field is optional, so {} is a valid options object.

Platform and labels ​

FieldMeaning
platformdefault, mac, or auto. When omitted, the toolbar inherits data-fc-platform from its host, the nearest .fc-host, or the instance host, and follows later platform changes on the instance.
langLocale for built-in ribbon labels. Defaults to the instance locale and follows setLocale(); an explicit lang stays fixed.
text, menuTextReplace the derived ToolbarText (button titles, group names) and ToolbarMenuText (dropdown labels).

Tabs and initial state ​

FieldDefaultMeaning
activeTab'home'Initial RibbonTab. A tab outside the tab set falls back to the first tab.
ribbonTabssee Tabs and displayThe tab set to render.
ribbonDisplayMode'full'full, singleLine, tabsOnly, or autoHide.
collapsedfalseShorthand for tabsOnly when ribbonDisplayMode is omitted.
formulaBarVisibletrueInitial state of the View tab's formula bar toggle.
theme'paper'The ribbon's own paper, ink, or contrast state, reported through onThemeChange. Change the spreadsheet theme with instance.setTheme().
borderStyle, borderColor'thin', '#000000'Initial line style and color for the borders menu.

Command wiring ​

FieldMeaning
hooksRibbonHooks groups for commands that call into host or feature code: clipboard, sortFilter, insert, page, drawing, review, protection, automation, formula, sheetView. Each group is merged over the built-in group, so a host can replace one handler and keep the rest.
dynamicDropdownsTurn on the built-in dropdown menus: true for the defaults, a partial DynamicDropdownsCtx to replace individual handlers, or a function returning that partial for a context built after mount.
interceptCommand(id, button, event)Runs before a command is dispatched. Return true to mark it handled and skip the built-in dispatch.
commandDelegationSet false when the host handles [data-ribbon-command] clicks itself. Tab and display-option clicks are always handled.
menusRibbonMenus submenu factories, merged over the defaults.
helpersA partial RibbonRenderHelpers to replace one renderer factory, such as the color picker.
createBackstageViewReturns the element shown while the backstage is open.

Runtime hooks ​

focusSheet, refreshCells, refreshZoom, showMessage, and applyRibbonFormat replace steps the dispatcher runs around a command. By default focusSheet focuses the spreadsheet host, applyRibbonFormat records the format change in the instance history, and the other three do nothing. projectFormatToolbar is called after the ribbon updates its active-state buttons, for hosts that mirror that state elsewhere. Most hosts leave all six unset.

Callbacks ​

CallbackFires after
onTabChange(tab)The active tab changes.
onCollapsedChange(collapsed)The ribbon switches between expanded and collapsed display modes.
onDisplayModeChange(mode)The display mode changes.
onBackstageOpenChange(open)setBackstageOpen() changes the backstage state.
onThemeChange(theme)The ribbon's theme state changes.
onFormulaBarChange(visible)The formula bar toggle changes.
onCommand(id, applied)A command was dispatched. applied is false when no built-in handler matched, which lets the host handle its own command IDs as a fallback.

Selecting the File tab only switches the active tab and fires onTabChange('file'); the host renders its own backstage from that callback. toolbar.setBackstageOpen(true) shows the view returned by createBackstageView, and Back or Escape closes it and restores the previously active tab. Other backstage actions are the host's to handle. A host with a file menu can connect the two:

ts
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
  createBackstageView: () => buildFileMenu({ onClose: () => toolbar.setBackstageOpen(false) }),
  onTabChange: (tab) => {
    if (tab === 'file') toolbar.setBackstageOpen(true)
  },
})

Without createBackstageView, an open backstage shows an empty element.

ToolbarInstance ​

MemberUse
hostThe element the ribbon was mounted into.
instanceThe current SpreadsheetInstance, or null while a getter returns null.
applyCommand(id)Dispatch a ribbon command ID through the same path as a click. Returns true when a handler matched. A command the interaction policy denies also returns true, without running.
focusActiveTab()Focus the active tab button, for F6-style landmark cycling. Returns whether focus moved.
setActiveTab(tab), getActiveTab()Change or read the active tab. A tab that is not rendered is ignored.
setDisplayMode(mode), getDisplayMode()Change or read the display mode.
setCollapsed(collapsed), getCollapsed()Switch between tabsOnly and full; getCollapsed() is also true in autoHide.
setBackstageOpen(open), getBackstageOpen()Show or hide the backstage view.
setDisplayMenuOpen(open)Open or close the ribbon display options menu.
setFormulaBarVisible(visible), getFormulaBarVisible()Update the formula bar toggle state.
setTheme(theme), getTheme()Update the ribbon's theme state.
setBorderStyle(style), getBorderStyle(), setBorderColor(color), getBorderColor()Border menu state.
dropdownsApiThe dropdown controller when dynamicDropdowns is on, otherwise null.
rerender()Rebuild the ribbon. Use it after a getter starts returning an instance; selection and format changes update the ribbon without it.
dispose()Remove listeners, restore the host's lang and data-fc-platform, and clear the host element.

Users can also change the display mode by double-clicking the active tab or pressing Ctrl+F1. Arrow keys, Home, and End move between tabs.

Tabs and display ​

RibbonTab is one of file, home, insert, draw, pageLayout, formulas, data, review, view, help, automate, and acrobat, plus the contextual tabs chartDesign, chartFormat, shapeFormat, pictureFormat, and tableDesign. The package exports these tab sets:

ConstantTabs
EXCEL365_STANDARD_RIBBON_TABSFile, Home, Insert, Page Layout, Formulas, Data, Review, View, Help.
OPTIONAL_RIBBON_TABSDraw, Automate, Acrobat.
RIBBON_TABSThe standard set followed by the optional tabs.
EXCEL365_MAC_RIBBON_TABSHome, Insert, Draw, Page Layout, Formulas, Data, Review, View, Automate.
CHART_CONTEXTUAL_RIBBON_TABSChart Design, Format.
SHAPE_CONTEXTUAL_RIBBON_TABSShape Format.
PICTURE_CONTEXTUAL_RIBBON_TABSPicture Format.
TABLE_CONTEXTUAL_RIBBON_TABSTable Design.
CONTEXTUAL_RIBBON_TABSAll of the contextual tabs above.

When ribbonTabs is omitted, the default platform renders EXCEL365_STANDARD_RIBBON_TABS and the Mac platform renders EXCEL365_MAC_RIBBON_TABS; the React and Vue components share this default. The Draw, Automate, and Acrobat tabs need an explicit RIBBON_TABS or [...EXCEL365_STANDARD_RIBBON_TABS, ...OPTIONAL_RIBBON_TABS], and should be added only when the host has wired what they need. An explicit ribbonTabs also replaces the Mac tab set. Tabs render in the order of the array, and keyboard navigation skips tabs that are hidden or disabled.

buildRibbonModel(lang, { tabs, profile, contextualTabs }) returns the same model the ribbon renders: tabs with an id and label, groups with a title, and commands with an id, label, title, and optional disabledReason. profile is a RibbonProfile, either default or excel365Mac. contextualTabs takes a list of contextual tabs, such as CHART_CONTEXTUAL_RIBBON_TABS (see Contextual tabs), and appends them to the model. Use it to build a host-owned command palette or to look up the command IDs to pass to applyCommand():

ts
import { buildRibbonModel, type RibbonProfile } from '@libraz/formulon-cell'

const profile: RibbonProfile = 'excel365Mac'
const commands = buildRibbonModel('en', { profile }).flatMap((tab) =>
  tab.groups.flatMap((group) => group.commands.map((command) => command.id)),
)

Contextual tabs ​

The ribbon adds tabs after the regular ones while an object is selected, on both platforms and whatever ribbonTabs is. Selecting an object replaces the cell selection, so the tabs follow the selection:

SelectionTabsCommands
ChartChart Design, FormatChart Design: Add Chart Element, Change Colors, Switch Row/Column, Select Data, Change Chart Type. Format: Format Selection and Arrange.
ShapeShape FormatShape Fill, Shape Outline (color, Weight, Dashes), and Arrange.
PicturePicture FormatArrange.
Cell inside a tableTable DesignTable Name, Header Row, Total Row, Banded Rows, First Column, Last Column, Banded Columns.

Arrange is one group shared by the chart, shape, and picture tabs: Bring Forward, Send Backward, and Selection Pane act on the selected object. Inserting or pasting an object selects it and opens its first tab; deselecting it returns to the tab that was active before. Color menus show the workbook theme's colors. A host can open the selected chart's settings with instance.openChartSettings(tab?), which takes 'elements' (default), 'styles', or 'filters' and returns false when no chart is selected.

Dropdown menus such as Paste, Fill, Clear, Sort & Filter, and Protect run handlers from a DynamicDropdownsCtx. Override one with a partial object: dynamicDropdowns in mountToolbar() and the inline toolbar option, or dropdownActions on the framework components. Keys you do not pass keep the built-in handler; createDefaultDynamicDropdownsCtx() returns the full default context.

ts
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
  dynamicDropdowns: {
    // 'protect-sheet', 'unprotect-sheet', 'protect-workbook', 'unprotect-workbook', 'allow-edit-ranges'
    applyProtectAction: (action) => openProtectDialog(action),
  },
})

The handler names follow the menus: applyCopyAction, applyRibbonPasteAction, applyFillSeries, applyClearAction, applySortMenuAction, applyProtectAction, applyCalcOptionAction, applyScriptAction, insertPictureFromRibbon, createTableFromSelection, and so on. The DynamicDropdownsCtx type declaration lists them all.

The inline ribbon leaves dropdowns off while an interaction policy is active. A mounted ribbon also disables the commands the active policy denies; see Options.

Host callbacks ​

The framework components route a few ribbon buttons to props:

PropRibbon commandCore hook
onSpellingReviewReview → Spellinghooks.review.spelling
onAccessibilityCheckReview → Check Accessibilityhooks.review.accessibility
onTranslateReview → Translatehooks.review.translate
onDrawPen, onDrawEraserDraw → Pen, Eraser on the default platformhooks.drawing.setInkMode() with 'pen' or 'erase'
onRunScriptThe custom-script item in the Automate script menuapplyScriptAction('custom')
onAddInManage in the add-ins menuapplyAddInAction('manage')

A provided prop replaces the matching core hook. When a prop is omitted, Spelling, Translate, and Check Accessibility keep their built-in handlers, which show a report for the selected cells, and the Script and Add-ins items keep the core script and add-in actions; with vanilla mountToolbar(), pass hooks.review to replace the review handlers. A provided onRunScript or onAddIn handles only its own menu item. The Script and Add-ins buttons open their menu first, and the callback runs when the user picks the matching item. A dropdownActions entry for applyScriptAction or applyAddInAction takes precedence over these props.

Platform differences ​

On the Mac platform the ribbon uses EXCEL365_MAC_RIBBON_TABS, which has no File or Help tab and includes Draw and Automate. Its Home tab is laid out like the desktop Mac ribbon, with icon-and-text Cut, Copy, and Format Painter rows and large Insert, Delete, and Format tiles. Both Home ribbons put Phonetic Settings in the font group. Mac commands use IDs in the mac. namespace, such as mac.data.goalSeek, mac.insert.sparkline, mac.review.stats, and mac.formulas.insertFunction. applyCommand() dispatches them like any other ID, and many of them reuse the default command behind them.

ts
instance.toolbar?.applyCommand('mac.data.goalSeek')

The Mac Draw tab keeps its ink in the mounted session; the drawing hook and the onDrawPen / onDrawEraser props apply to the default platform's Draw tab. Platform and Mac UI lists the Mac dialogs, their command IDs, and their limits.

Styling ​

@libraz/formulon-cell/styles.css imports the ribbon stylesheet along with the grid and themes, so most applications need nothing else. @libraz/formulon-cell/styles/toolbar.css is the ribbon stylesheet alone. The adapters' @libraz/formulon-cell-react/toolbar.css and @libraz/formulon-cell-vue/toolbar.css re-export that file for applications that list their stylesheets per package.

The ribbon reads its own --fc-tb-* tokens. Under .fc-host, which covers the inline ribbon, --fc-tb-accent and --fc-tb-accent-strong follow the grid's --fc-accent and --fc-accent-strong, so one brand override themes both. A separate toolbar keeps the toolbar defaults; set the tokens on its wrapper:

css
.app-toolbar {
  --fc-tb-accent: #0f766e;
  --fc-tb-accent-strong: #115e59;
  --fc-tb-ribbon-bg: #ffffff;
  --fc-tb-ribbon-hover: #f0fdfa;
}

The ink and contrast themes supply their own --fc-tb-* values through data-fc-theme. Theming covers the grid tokens.