# o-spreadsheet Data Model

This document describes the data structure expected for importing spreadsheet data into o-spreadsheet.

---

## Top-Level Structure

```js
{
    // The version of the data model, used for compatibility checks and upgrading.
    // If omitted, it defaults to the latest version.
    "version": string, // this defintion is for version "18.4.2"

    // Used for multi user capabilities. In single user hardcode "START_REVISION".
    "revisionId": "START_REVISION" | string,

    // see the sheet Object, just below
    // the order of the sheets determines the order of the tabs in the UI.
    "sheets": [ ... ],

    // Styles for the cells, details in data-model/style.md
    "styles": { ... },

    // Formats for the cells, details in data-model/format.md
    "formats": { ... },

    // Boders for the cells, details in data-model/border.md
    "borders": { ... },

    // Should be set to true if the figure IDS are unique across all sheets, else false.
    "uniqueFigureIds": boolean,

    // Settings of the model, epecially the locale settings.
    "settings": {
      "locale": {
          "name": "English (US)",
          "code": "en_US",
          "thousandsSeparator": ",",
          "decimalSeparator": ".",
          "dateFormat": "m/d/yyyy",
          "timeFormat": "hh:mm:ss a",
          "formulaArgSeparator": ",",
          "weekStart": 7
        }
    },

    // Pivot tables, details in data-model/pivot.md
    "pivots": { ... },
    // Because there could be more than one type of pivot table (e.g. when integrating with odoo)
    // the pivotNextId is used to generate unique IDs for new pivot tables across all types.
    "pivotNextId": number,

    // details of custom table styles, see data-model/table-style.md
    "customTableStyles": { ... }
}
```

---

## Sheet Object

The SheetPlugin manages the structure and metadata of each worksheet. Each entry in the `sheets` array is a sheet object with the following properties:

```js
{
  // Unique identifier for the sheet
  "id": string,
  // Display name of the sheet
  "name": string,
  // Number of columns
  "colNumber": number,
  // Number of rows
  "rowNumber": number,
  // Optional: row metadata
  "rows": {
    number: {
      // Row index as key
      "size": number
      // Optional: custom row height in pixels
      "hidden": boolean
      // Optional: whether the row is hidden
    }
  },
  // Optional: column metadata (same as rows)
  "cols": {...},
  // Array of merged cell ranges (e.g., ["A1:B2"])
  "merges": [string, ...],
  // Cell values and formulas, keyed by address or range (e.g., {"A1": "value"}).
  // Exports use a compressed form, see data-model/cells.md
  "cells": {...},
  // Cell/range to style ID mapping
  "styles": {...},
  // Optional: default style IDs for the whole sheet, columns and rows, see "Default styles and formats" below
  "defaultStyle": {...},
  // Cell/range to format ID mapping
  "formats": {...},
  // Optional: default format IDs for the whole sheet, columns and rows (same structure as defaultStyle)
  "defaultFormat": {...},
  // Cell/range to border style ID mapping
  "borders": {...},
  // Array of conditional formatting rules
  "conditionalFormats": [...],
  // Array of data validation rules
  "dataValidationRules": [...],
  // Array of figures (charts, images, etc.)
  "figures": [...],
  // Array of table definitions
  "tables": [...],
  // Show/hide grid lines
  "areGridLinesVisible": boolean,
  // Show/hide the sheet
  "isVisible": boolean,
  // The definition of the groups of columns and rows
  "headerGroups": {
    "ROW": [ // Row header groupings
      {
        "start": number, // Start row index for the group
        "end": number,   // End row index for the group
        "isFolded": boolean, // (optional) Whether the group is folded
      }
    ],
    "COL": [ ... ] // Column header groupings same structure as ROW
  },
  // Split pane configuration
  "panes": {
    "xSplit": number, // Column index to split at (0 for no split)
    "ySplit": number  // Row index to split at (0 for no split)
  },
  // Tab color (hex code)
  "color": string
}
```

---

## Default styles and formats

`defaultStyle` and `defaultFormat` give a style or format to whole columns, whole rows or the whole sheet,
without listing every cell. Values are IDs from the top-level `styles` and `formats` objects. Column and row
indices are 0-based (`"0"` is column A or row 1). All keys are optional.

```js
"defaultStyle": {
  "sheetDefault": 1,            // every cell of the sheet
  "colDefault": { "1": 2 },     // column B
  "rowDefault": { "2": 3 }      // row 3
},
"defaultFormat": {
  "colDefault": { "2": 1 }      // column C
}
```

The style of a cell is resolved **property by property** (`bold`, `fillColor`, …). For each property, the first
value found wins:

1. the cell's own style (sheet `styles` mapping)
2. the row default
3. the column default
4. the sheet default

The format of a cell is its own format if it has one, otherwise the row default, then the column default, then
the sheet default.

With `styles` = `{ "1": { "fillColor": "#FFF2CC" }, "2": { "bold": true }, "3": { "fillColor": "#000000", "textColor": "#FFFFFF" } }`,
the example above gives:

- `A1`: `{ fillColor: "#FFF2CC" }`
- `B1`: `{ fillColor: "#FFF2CC", bold: true }`
- `B3`: `{ fillColor: "#000000", textColor: "#FFFFFF", bold: true }` (the row wins over the sheet for
  `fillColor`; `bold` still comes from the column)
- `C5`: formatted with format 1

Formatting more than half of a column, a row or the sheet stores it as a default instead of a per-cell style
or format, so exported sheets often have few entries in `styles`/`formats` and most of it in the defaults.

---

## More

- [Borders](data-model/border.md)
- [Cells](data-model/cells.md)
- [Conditional formatting](data-model/cf.md)
- [Charts](data-model/chart.md)
- [Formats](data-model/format.md)
- [Pivots](data-model/pivot.md)
- [Styles](data-model/style.md)
- [Tables](data-model/table.md)
- [Version](data-model/version.md)

---

## Example

See [demo/data.js](../demo/main.js) for a full example of a valid data structure.

---

## Notes

- All keys are case-sensitive.
- Some plugins may use additional or optional fields.
- Plugins ignore unknown fields, but missing required fields may cause errors or incomplete imports.
