# Spreadsheet + Ribbon bar

The whole Excel surface as one component, <SvSheet workbook={wb} />: a six-tab ribbon (Home, Insert, Formulas, Data, Review, View), the Name Box and fx bar, sheet tabs and the Sum / Average / Count status bar. Format cells, merge them, comment on them, validate what goes in, colour them by rule, filter the region, protect the sheet; every button is the same call as its shortcut, so Ctrl+B and the Bold button cannot drift. A two-sheet P&L with the formats travelling in the document.

Requires `@svgrid/enterprise` (and `@svgrid/grid`). Category: Spreadsheet. Live demo: https://svgrid.com/demos/27-spreadsheet-ribbon/

Install: `npm i @svgrid/grid @svgrid/enterprise` (Svelte 5).

## About this example

The whole Excel surface on a Svelte 5 grid as one component, <SvSheet workbook={wb} />: a six-tab ribbon (Home, Insert, Formulas, Data, Review, View), the Name Box and fx bar, sheet tabs and the Sum, Average and Count status bar, all from @svgrid/enterprise. The Budget sheet is a small P&L with formulas down every column and formats (a header band, thousands, a percent row) that travel with the document; Headcount is a second sheet. Bold, fills, number formats, Merge & Center, comments, data validation, conditional formatting, AutoFilter and Protect Sheet are all on the ribbon, and each button runs the same function as its keyboard shortcut. Enterprise, in @svgrid/enterprise.

The whole Excel surface as one component:

```text
<SvSheet workbook={wb} />
```

That is the demo. The ribbon, the Name Box and fx bar, the A..I column headers over the 1..N gutter, the sheet tabs and the Sum / Average / Count status bar all come from @svgrid/enterprise rather than from this file. This demo used to carry about 1,400 lines of its own ribbon.

Ribbon buttons and keyboard shortcuts are the SAME calls. Click a bold cell and the Bold button lights up; press Ctrl+B and it goes out. Both run one function in sheet/shortcuts.ts, so they cannot drift.

Try: select B2:D4 and watch the status bar. Press Ctrl+Shift+4 or hit $ in the Number group. Ctrl+Down runs to the bottom of a block. The Formulas tab has Show Formulas (Excel's Ctrl+`).

The chrome is painted with the grid's own theme tokens, so the theme picker at the top left re-skins it along with everything else. Pick "Excel" to see it wearing Excel's palette.

## Imports, features and API used

- Imports: `@svgrid/enterprise`

## Frequently asked questions

### How much of this is the demo's own code?

Almost none. The file builds a workbook with createWorkbook, hands it to <SvSheet> with a formats map, and that is it; the ribbon, the formula bar, the tabs, the status bar and every dialog come from the component. The demo used to carry about 1,400 lines of its own ribbon.

### Do the ribbon and the shortcuts stay in step?

Yes, by construction: a ribbon button and its shortcut run one function in the shell's shortcuts module. Click a bold cell and the Bold button lights; press Ctrl+B and it goes out.

### How do the formats get there?

Through the formats prop, keyed by address (A1, or Headcount!A1 for the other sheet), the way a saved workbook carries them. They are per sheet, and getState() returns them with the rest of the document so a sheet can be saved and restored.

## Related documentation

- [The spreadsheet shell](https://svgrid.com/docs/help/cells/spreadsheet-shell.md)
- [Migrating from Handsontable](https://svgrid.com/docs/help/migrating-from-handsontable.md)
- [Recipes / Cookbook](https://svgrid.com/docs/help/recipes.md)
- [Spreadsheet: getting started](https://svgrid.com/docs/help/sheet/start.md)

## Related articles

- [A Fill Handle (Drag to Fill) in SvGrid](https://svgrid.com/blog/fill-handle-drag-to-fill/) - Build a working spreadsheet-style fill handle on top of SvGrid's cell selection and editing - pointer tracking, range highlighting, series fill, and undo/redo integration all covered.
- [Pivot Tables in Svelte - Summarize Data Without a Spreadsheet](https://svgrid.com/blog/pivot-tables/) - Run a cross-tab pivot directly inside your Svelte app using @svgrid/enterprise createPivotModel - no Excel, no server-side aggregation, no stale exports.
- [Spreadsheet-Style Cell Range Selection in SvGrid](https://svgrid.com/blog/cell-range-selection/) - How to enable drag-to-select cell ranges, read live selection state, and build a status-bar footer that sums and averages the selected values.

## Source code (27-spreadsheet-ribbon.svelte)

```svelte
<script lang="ts">
  /**
   * 27. Spreadsheet shell
   * ----------------------
   * The whole Excel surface as one component:
   *
   *   <SvSheet workbook={wb} />
   *
   * That is the demo. The ribbon, the Name Box and fx bar, the A..I column
   * headers over the 1..N gutter, the sheet tabs and the Sum / Average /
   * Count status bar all come from @svgrid/enterprise rather than from this
   * file. This demo used to carry about 1,400 lines of its own ribbon.
   *
   * Ribbon buttons and keyboard shortcuts are the SAME calls. Click a bold
   * cell and the Bold button lights up; press Ctrl+B and it goes out. Both
   * run one function in sheet/shortcuts.ts, so they cannot drift.
   *
   * Try: select B2:D4 and watch the status bar. Press Ctrl+Shift+4 or hit $
   * in the Number group. Ctrl+Down runs to the bottom of a block. The
   * Formulas tab has Show Formulas (Excel's Ctrl+`).
   *
   * The chrome is painted with the grid's own theme tokens, so the theme
   * picker at the top left re-skins it along with everything else. Pick
   * "Excel" to see it wearing Excel's palette.
   */
  import { SvSheet, createWorkbook, type CellFormatEntry } from '@svgrid/enterprise'

  // Formats travel with the document, the way they do in a saved workbook,
  // rather than being something you re-apply every time it opens.
  const BAND = { bold: true, fill: '#e2e8f0', color: '#0f172a' } as const
  const TOTAL = { bold: true, numFmt: '#,##0' } as const
  const MONEY = { numFmt: '#,##0' } as const
  const PERCENT = { numFmt: '0.0%' } as const

  // A small P&L: the kind of thing you would actually open Excel for. Row 1
  // is a header band the way you would type it, not a grid header - in a
  // spreadsheet the first row is just the first row.
  const wb = createWorkbook([
    {
      name: 'Budget',
      cells: [
        ['Line', 'Jan', 'Feb', 'Mar', 'Q1'],
        ['Subscriptions', '18200', '18900', '19500', '=SUM(B2:D2)'],
        ['Services', '4200', '3800', '5100', '=SUM(B3:D3)'],
        ['Support', '1200', '1250', '1310', '=SUM(B4:D4)'],
        ['Revenue', '=SUM(B2:B4)', '=SUM(C2:C4)', '=SUM(D2:D4)', '=SUM(E2:E4)'],
        [],
        ['Payroll', '-9800', '-9800', '-10400', '=SUM(B7:D7)'],
        ['Cloud', '-940', '-1010', '-1180', '=SUM(B8:D8)'],
        ['Marketing', '-3200', '-2750', '-4100', '=SUM(B9:D9)'],
        ['Costs', '=SUM(B7:B9)', '=SUM(C7:C9)', '=SUM(D7:D9)', '=SUM(E7:E9)'],
        [],
        ['Net', '=B5+B10', '=C5+C10', '=D5+D10', '=E5+E10'],
        ['Margin', '=B12/B5', '=C12/C5', '=D12/D5', '=E12/E5'],
      ],
    },
    {
      name: 'Headcount',
      cells: [
        ['Team', 'People', 'Cost each', 'Total'],
        ['Engineering', '6', '11000', '=B2*C2'],
        ['Design', '2', '9500', '=B3*C3'],
        ['Sales', '3', '8200', '=B4*C4'],
        ['', '=SUM(B2:B4)', '', '=SUM(D2:D4)'],
      ],
    },
  ])

  // A bare address formats the sheet that is active at mount (Budget); a
  // sheet-qualified one reaches the other sheet, the way a formula would.
  // Formats are per sheet, so a band on Budget's row 5 says nothing about
  // Headcount's row 5.
  const on = (sheet: string, cols: string, row: number, entry: CellFormatEntry) =>
    Object.fromEntries([...cols].map((c) => [`${sheet}${c}${row}`, entry]))
  const band = (row: number) => on('', 'ABCDE', row, BAND)
  const total = (row: number) => on('', 'ABCDE', row, TOTAL)
  const money = (row: number) => on('', 'BCDE', row, MONEY)
  const percent = (row: number) => on('', 'BCDE', row, PERCENT)
  const headcount = {
    ...on('Headcount!', 'ABCD', 1, BAND),
    ...on('Headcount!', 'CD', 2, MONEY), ...on('Headcount!', 'CD', 3, MONEY), ...on('Headcount!', 'CD', 4, MONEY),
    ...on('Headcount!', 'ABCD', 5, TOTAL),
  }
</script>

<SvSheet
  workbook={wb}
  height="100%"
  rows={24}
  columns={9}
  columnWidths={{ A: 150 }}
  formats={{
    ...band(1),
    ...money(2), ...money(3), ...money(4),
    ...total(5),
    ...money(7), ...money(8), ...money(9),
    ...total(10),
    ...total(12),
    ...percent(13),
    ...headcount,
  }}
/>
```

[View this example on GitHub](https://github.com/sv-grid/sv-grid/blob/main/examples/src/demos/27-spreadsheet-ribbon.svelte)
