Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
streaksheet
Advanced tools
(Under development)
TODO some of the content here is for internal use. We should move that to a separate file and make this file be for users.
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.
To run the example:
yarn example-watch
Then open http://localhost:1234 in your browser. The page automatically refreshes after making a change.
TODO Incomplete
These methods exist on refs to StreakSheet components.
enterEditMode(sectionKey, columnKey, rowKey, queuedValue?).
If you pass a value for the optional 3rd parameter, then the value will be passed to the Cell as the initialEditQueuedValue
prop.
NOTE for users: if you pass a React synthetic event as 3rd parameter, call .persist() on it first. (This will be unnecessary in React 17+.)
When a cell is in edit mode, StreakSheet ignores arrow key inputs, so that moving the cursor around inside a text input inside the cell doesn't move the cursor in the spreadsheet. However, StreakSheet continues to react to escape, enter, shift-enter, tab, and shift-tab key presses. If you want to handle those events yourself when an element inside of a cell is focused, then set up your own event listener to handle the keypress and call the stopPropagation()
method on the event, which will stop StreakSheet from reacting to it.
These are props that should be passed to the StreakSheet component.
TODO Incomplete list here
Optional. This prop allows you to prevent some cells from being able to be switched into edit mode by calling the cancel method on the event object.
Note: rowKey may be null if the user is attempting to enter edit mode on a section row header.
Optional. When a key is pressed that StreakSheet doesn't specifically handle while a cell is selected but no cells are in edit mode, this function is called. If the cell is meant to be editable and support text input, then you may pass a function which calls event.domEvent.persist()
and then enterEditMode(sectionKey, columnKey, rowKey, event.domEvent)
on the StreakSheet ref to put the cell into edit mode and set its initialEditQueuedValue
prop, so you can process the keydown event inside the cell when it first renders in edit mode.
You must pass a React component that takes the following props:
columnKey: string;
isEditing: boolean;
setEditing(isEditing: boolean): void;
initialEditQueuedValue: unknown | undefined;
rowData: T;
rowKey: string;
The initialEditQueuedValue
prop is present when the cell is switched into edit mode with enterEditMode()
, and includes the queuedValue
parameter value. The initialEditQueuedValue
prop should only be read when a component first switches into edit mode.
Separate from props, the useSheetData
hook provides a convenient interface that one uses to asynchronously supply the sheet with row data.
TODO
The return value of useSheetData has the following methods:
getRowData(sectionKey: string, rowKey: string): T | undefined
Returns the rowData for a given sectionKey
, rowKey
pair or undefined
if a row could not be found.
addRowData(sectionKey: string, rowKey: string, rowData: T, afterRowKey?: string): boolean
afterRowKey
indicates a row after which the user would like to place the new row. Ignore this parameter to insert the new row at the beginning of the section.
Returns a boolean for whether the addition was successful. The addition would not succeed, for example, if the provided sectionKey
referred to a section not currently found in the spreadsheet.
deleteRowData(sectionKey: string, rowKey: string): boolean;
Returns a boolean for whether the deletion was successful. A deletion would not succeed if a row to delete could not be found for the sectionKey
, rowKey
pair.
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
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',
})
}}
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,
// Make border between cells 1px while keeping border on leftmost cell.
borderLeft: columnIndex === 0 ? '1px solid black' : 'none',
})
}}
cell
cellEditMode
columnHeaderCellContainer
columnHeadersContainer
columnReorderIndicator
columnReorderOverlay
columnResizeHandle
columnResizeIndicator
copiedRegionBackground
copiedRegionForeground
grid
highlightedRowBackground
highlightedRowForeground
primarySelectedCellBackground
primarySelectedCellForeground
sectionHeaderCellContainer
sectionHeadersContainer
selectionBackground
selectionForeground
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.
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();
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.
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.
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.
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.
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 => {
// pasteTargets is an array of objects with source and target keys.
//
// [
// {
// source: { sectionKey: 'a', 'rowKey: 'b', columnKey: 'c' },
// target: { sectionKey: 'd', 'rowKey: 'e', columnKey: 'f' },
// },
// ...
// ]
};
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'} },
]
FAQs
Unknown package
The npm package streaksheet receives a total of 7 weekly downloads. As such, streaksheet popularity was classified as not popular.
We found that streaksheet demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.