StreakSheet
(Under development)
Usage
Important: StreakSheet requires the browser to support AbortController, so if
you need to support browsers that predate it
(https://caniuse.com/#feat=abortcontroller), you must first load a global
polyfill.
Example
To run the example:
yarn example-watch
Then open http://localhost:1234 in your browser. The page automatically
refreshes after making a change.
Styling
You can provide custom styles for most components via the styles
prop. Each
value of this object in an optional function. The first argument is the default
styles and the second is an object of state values specific to that component.
The return value is a CSSProperties
object.
(base: CSSProperties, state: {}) => CSSProperties;
You can choose to extend the default styles or replace them completely. You
almost always want to do the former.
styles={{
columnReorderOverlay: base => ({
...base,
backgroundColor: 'red',
})
}}
Be careful when overriding styles completely. Many components apply rules like
position: absolute
that will break the component if removed.
The Styles
type indicates exactly what state values each style callback sends.
Some components are unaffected by state, in which case the second argument is
omitted completely.
styles={{
columnResizeHandle: (base, { isDragging }) => ({
...base,
backgroundColor: isDragging ? 'red' : 'blue',
})
}}
This API is inspired by React Select
Selection Styles
For each selection type there are actually two styles to apply: one for the
foreground, another for the background. Typically you want to apply a
backgroundColor
rules to the background style and borders to the foreground.
This prevents elements like images from getting discolored by a partially
transparent background overlaying it.
styles={{
selectionBackground: base => ({
...base,
backgroundColor: 'red',
})
selectionForeground: base => ({
...base,
borderTop: '1px dashed blue',
})
}}
Overriding Borders
When applying custom border styles, it's common to want different styles for
different edges. To make overriding default styles easier, internally we use
borderBottom
, borderLeft
, borderRight
, and borderTop
shorthand, even
when all four rules have the same value.
styles={{
columnHeaderCellContainer: (base, { columnIndex }) => ({
...base,
borderLeft: columnIndex === 0 ? '1px solid black' : 'none',
})
}}
Style Keys
cell
columnHeaderCellContainer
columnHeadersContainer
columnReorderIndicator
columnReorderOverlay
columnResizeHandle
columnResizeIndicator
copiedRegionBackground
copiedRegionForeground
grid
highlightedRowBackground
highlightedRowForeground
primarySelectedCellBackground
primarySelectedCellForeground
sectionHeaderCellContainer
sectionHeadersContainer
selectionBackground
selectionForeground
Keys vs. Indexes
StreakSheet internally often uses row and column indexes to refer to a cell.
For example, the selection logic uses indexes and has no knowledge of sections.
The client never deals with indexes. Instead, section, row, and column keys
uniquely identify each cell. In callbacks like onPaste
and imperative methods
like getSelectedCells
, cells are represented by objects:
{
sectionKey: string;
rowKey: string;
columnKey: string;
}
Internally, a cellIndexesToKeys
function converts raw grid indexes to cell
keys.
Selections
There are multiple ways a cell can appear selected.
It may be sufficient to override the default styles for the various
selections, but you can also get the current selection using the imperative getSelectedCells
method.
const selectedCells = sheetRef.current.getSelectedCells();
Primary Selected Cell
The first cell of each selection region is the "primary" selected cell. It acts
as the anchor when expanding the selection region using the keyboard. When you
click on the grid and there's a single cell selected, this is the primary one.
You can change the primary selected cell by clicking on any cell or using the
arrow keys.
Selection Region
Clicking and dragging or using shift + arrow keys allows you to select multiple
cells.
It's possible to have multiple selection regions by holding command while
dragging.
Highlighted Row
All the cells in the same row as the primary selected cell appear highlighted.
This isn't a selection per se, more a visual decoration.
Copied Region
When some cells are selected and you hit command-c to copy them, the copied
region is indicated until the user pastes or copies a different region.
Copying Cells
StreakSheet supports the ability to copy and paste regions of cells. Clients
listen for a paste event by supplying an onPaste
to the useSheetData
hook.
Upon being notified of a paste, it's up to the client to update the data
accordingly. StreakSheet takes care of mapping the source cells to target
cells.
onPaste: pasteTargets => {
};
The onPaste
callback has a second argument didCut
which indicates if the
user used command-x rather than command-c to copy the selection. Again, it's up
to the client to update the source cells (or not, depending on the desired
behaviour).
When the paste region is larger than the copy region (i.e. the user copies a
range, selects a larger range, then pastes), the copied values are "looped".
That is, a 2x2 region pasted to a 3x3 region will result in this:
┏━━━━━┳━━━━━┓ ┏━━━━━┳━━━━━┳━━━━━┓
┃ A ┃ B ┃ ┃ A ┃ B ┃ A ┃
┣━━━━━╋━━━━━┫ ┣━━━━━╋━━━━━╋━━━━━┫
┃ C ┃ D ┃ ┃ C ┃ D ┃ C ┃
┗━━━━━┻━━━━━┛ ┣━━━━━╋━━━━━╋━━━━━┫
┃ A ┃ B ┃ A ┃
┗━━━━━┻━━━━━┻━━━━━┛
The resulting pasteTargets
will have nine entries. Assuming the copy region
starts at [row 0, column 0] and the selection region at [row 10, column 10], the
list will be:
[
{ source: {..., rowKey: '0', columnKey: '0'}, target: {..., rowKey: '10', columnKey: '10'} },
{ source: {..., rowKey: '0', columnKey: '1'}, target: {..., rowKey: '10', columnKey: '11'} },
{ source: {..., rowKey: '0', columnKey: '0'}, target: {..., rowKey: '10', columnKey: '12'} },
{ source: {..., rowKey: '1', columnKey: '0'}, target: {..., rowKey: '11', columnKey: '10'} },
{ source: {..., rowKey: '1', columnKey: '1'}, target: {..., rowKey: '11', columnKey: '11'} },
{ source: {..., rowKey: '1', columnKey: '0'}, target: {..., rowKey: '11', columnKey: '12'} },
{ source: {..., rowKey: '0', columnKey: '0'}, target: {..., rowKey: '12', columnKey: '10'} },
{ source: {..., rowKey: '0', columnKey: '1'}, target: {..., rowKey: '12', columnKey: '11'} },
{ source: {..., rowKey: '0', columnKey: '0'}, target: {..., rowKey: '12', columnKey: '12'} },
]