Spreadsheet 2 - Composable, declarative Spreadsheet component.
Beautifully designed Spreadsheet component for React. Composable. Customizable. Declarative
Current version:
@rowsncolumns/spreadsheet
7.1.267
@rowsncolumns/spreadsheet-state
20.0.122
Built for Developers
Spreadsheet is rendered in HTML Canvas, giving you ~60fps rendering performance and ability to display millions of rows and columns without peformance impact. With escape hatches, you can access the internals and customize it to your liking.
Compose the perfect Spreadsheet
Pick and choose the components you need to build your Spreadsheet.
You can also build own custom functionality on top of Spreadsheet 2, with the many callbacks from CanvasGrid.
import {
SpreadsheetProvider, CanvasGrid, Toolbar, ButtonUndo,
ButtonRedo, RangeSelector, FormulaBarLabel, FormulaBarInput,
BottomBar, SheetSwitcher, SheetTabs, SheetStatus
} from "@rowsncolumns/spreadsheet"
const App = () => {
const sheet = { id: 1, rowCount: 1000, columnCount: 1000 }
return (
<>
<Toolbar>
<ButtonUndo onClick={} />
<ButtonRedo onClick={} />
{/*..import all other controls*/}
</Toolbar>
<FormulaBar>
<RangeSelector />
<FormulaBarInput />
</FormulaBar>
<CanvasGrid
sheetId={sheet.id}
rowCount={sheet.rowCount}
columnCount={sheet.columnCount}
getCellData={(sheetId: number, rowIndex: number, columnIndex: number): CellData => {
return {
userEnteredValue: {
formulaValue: '=SUM(4,4)'
},
formattedValue: "8"
}
}}
/>
<BottomBar>
<SheetSwitcher />
<SheetTabs />
<SheetStatus />
</BottomBar>
</>
)
}
{/* Wrap your app in SpreadsheetProvider */}
export const Spreadsheet = () => (
<SpreadsheetProvider>
<App />
</SpreadsheetProvider>
)
Bring your own Data model and State management
Or use `useSpreadsheetState` hook
Spreadsheet components are all uncontrolled and stateless. It renders based on the props that you pass-in and invokes callbacks to user actions.
Spreadsheet 2 is agnostic of your data persistence model. You can choose any database for storage or real-time collaboration.
import type { CellData } from "@rowsncolumns/spreadsheet"
import {
useSpreadsheetState,
Sheet,
SheetData,
RowData
} from "@rowsncolumns/spreadsheet-state"
type MyCellData = CellData & {
customProperty: 'hello'
}
type SheetData<T extends CellData> = Record<number, RowData<T>[]>
type RowData<T> = {
values: T[]
}
const App = () => {
const [ sheets, onChangeSheets ] = useState<Sheet[]>([])
const [ sheetData, onChangeSheetData ] = useState<SheetData<MyCellData>>({ })
return (
<CanvasGrid<MyCellData>
getCellData={(sheetId, rowIndex, columnIndex) => {
return sheetData[sheetId]?.[rowIndex]?.values?.[columnIndex]
}}
onChange={(value: string, sheetId: number, rowIndex: number, columnIndex: number) => {
// Persist and generate undo/redo patches
// If you are using useSpreadsheetState hook,its all built-in
onChangeSheetData(prevData => ...)
}}
/>
)
}
{/* Wrap your app in SpreadsheetProvider */}
export const Spreadsheet = () => (
<SpreadsheetProvider>
<App />
</SpreadsheetProvider>
)
useSpreadsheetState hook
This is an optional (Production grade) hook if you want to get started with the Spreadsheet with complete state management.
- Uses immer for state management.
- Full undo/redo capability.
- Calculation framework with optional web worker support.
- Real time collaboration built-in.
import {
defaultSpreadsheetTheme,
Sheet,
SheetData,
} from "@rowsncolumns/spreadsheet-state"
import {
CellData,
NamedRange,
EmbeddedObject,
EmbeddedChart,
TableView,
SpreadsheetTheme,
CanvasGrid,
SpreadsheetProvider
} from "@rowsncolumns/spreadsheet"
const App = () => {
const [sheets, onChangeSheets] = useState<Sheet[]>([]);
const [sheetData, onChangeSheetData] = useState<SheetData<CellData>>({});
const [scale, onChangeScale] = useState(1);
const [colorMode, onChangeColorMode] = useState<ColorMode>();
const [charts, onChangeCharts] = useState<EmbeddedChart[]>([]);
const [embeds, onChangeEmbeds] = useState<EmbeddedObject[]>([]);
const [tables, onChangeTables] = useState<TableView[]>([]);
const [namedRanges, onChangeNamedRanges] = useState<NamedRange[]>();
const [theme, onChangeTheme] = useState<SpreadsheetTheme>(defaultSpreadsheetTheme);
const locale = "en-GB";
const currency = "USD";
const {
activeCell,
activeSheetId,
selections,
rowCount,
columnCount,
frozenColumnCount,
frozenRowCount,
rowMetadata,
columnMetadata,
merges,
protectedRanges,
bandedRanges,
conditionalFormats,
isDarkMode,
spreadsheetColors,
canRedo,
canUndo,
undo,
redo,
...// other Spreadsheet methods
} = useSpreadsheetState({
sheets,
sheetData,
tables,
functions,
namedRanges,
theme,
colorMode,
locale,
onChangeSheets,
onChangeSheetData,
onChangeEmbeds,
onChangeCharts,
onChangeTables,
onChangeNamedRanges,
onChangeTheme,
})
return (
<>
<Toolbar>
<ButtonUndo onClick={onUndo} disabled={!canUndo} />
<ButtonRedo onClick={onRedo} disabled={!canRedo} />
{/*..import all other controls*/}
</Toolbar>
<CanvasGrid
{...spreadsheetColors}
enableTextOverflow
stickyEditor={true}
scale={scale}
conditionalFormats={conditionalFormats}
sheetId={activeSheetId}
rowCount={rowCount}
columnCount={columnCount}
frozenColumnCount={frozenColumnCount}
frozenRowCount={frozenRowCount}
rowMetadata={rowMetadata}
columnMetadata={columnMetadata}
activeCell={activeCell}
selections={selections}
theme={theme}
merges={merges}
charts={charts}
embeds={embeds}
tables={tables}
{/* Other methods from the hook */}
/>
</>
)
}
{/* Wrap your app in SpreadsheetProvider */}
export const Spreadsheet = () => (
<SpreadsheetProvider>
<App />
</SpreadsheetProvider>
)