react-konva-grid
Version:
Declarative React Canvas Grid primitive for Data table, Pivot table, Excel Worksheets
531 lines (526 loc) • 23 kB
HTML
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>react-konva-grid</title>
<meta name="description" content="Documentation for react-konva-grid">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="assets/css/main.css">
</head>
<body>
<header>
<div class="tsd-page-toolbar">
<div class="container">
<div class="table-wrap">
<div class="table-cell" id="tsd-search" data-index="assets/js/search.json" data-base=".">
<div class="field">
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
<input id="tsd-search-field" type="text" />
</div>
<ul class="results">
<li class="state loading">Preparing search index...</li>
<li class="state failure">The search index is not available</li>
</ul>
<a href="index.html" class="title">react-konva-grid</a>
</div>
<div class="table-cell" id="tsd-widgets">
<div id="tsd-filter">
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
<div class="tsd-filter-group">
<div class="tsd-select" id="tsd-filter-visibility">
<span class="tsd-select-label">All</span>
<ul class="tsd-select-list">
<li data-value="public">Public</li>
<li data-value="protected">Public/Protected</li>
<li data-value="private" class="selected">All</li>
</ul>
</div>
<input type="checkbox" id="tsd-filter-inherited" checked />
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
<input type="checkbox" id="tsd-filter-externals" checked />
<label class="tsd-widget" for="tsd-filter-externals">Externals</label>
<input type="checkbox" id="tsd-filter-only-exported" />
<label class="tsd-widget" for="tsd-filter-only-exported">Only exported</label>
</div>
</div>
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
</div>
</div>
</div>
</div>
<div class="tsd-page-title">
<div class="container">
<ul class="tsd-breadcrumb">
<li>
<a href="globals.html">Globals</a>
</li>
</ul>
<h1>react-konva-grid</h1>
</div>
</div>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<div class="tsd-panel tsd-typography">
<a href="#declarative-canvas-grid-with-react-konva" id="declarative-canvas-grid-with-react-konva" style="color: inherit; text-decoration: none;">
<h2>Declarative Canvas Grid with React Konva</h2>
</a>
<p align="center">
<br />
<img src='logo.png' width='400' />
<br />
</p>
<p>Canvas table grid to render large set of tabular data. Uses virtualization similar to <code>react-window</code> and <a href="https://github.com/konvajs/react-konva/">React-Konva</a> for primitives such as Rect, Text, Shape etc</p>
<p><a href="https://lbesson.mit-license.org/"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT license"></a> <a href="https://travis-ci.org/rmdort/konva-grid"><img src="https://travis-ci.org/rmdort/konva-grid.svg?branch=master" alt="Build Status"></a></p>
<p><a href="https://rmdort.github.io/konva-grid">Demo</a> | <a href="#Usage">Usage</a> | <a href="https://github.com/rmdort/konva-grid/wiki/Extending-Konva-Grid-using-hooks">Wiki</a></p>
<p><kbd><img src="screencapture.gif" alt="Screen capture"></kbd></p>
<a href="#features" id="features" style="color: inherit; text-decoration: none;">
<h2>Features</h2>
</a>
<ul>
<li>:electron: React powered declarative library</li>
<li>:100: Virtualized: Only visible cells are rendered</li>
<li>:bulb: Peformant: Canvas implementation with no DOM nodes</li>
<li>:scroll: Supports scrolling using native scrollbars</li>
<li>:computer: Supports both Fixed and Variable sized grids</li>
<li>:fire: Freeze rows and columns</li>
<li>:white_square_button: Merge rows and columns</li>
<li>:hand: Resizable headers</li>
<li>:deciduous_tree: Create Tree tables</li>
<li>:musical_keyboard: Keyboard accessible</li>
<li>:page_with_curl: Pagination sync/async</li>
<li>:hammer_and_wrench: Fully typed API written in TypeScript</li>
<li>:rainbow: Full Theming and Context Support</li>
<li>:muscle: Highly customizable using <a href="https://github.com/konvajs/react-konva/">react-konva</a></li>
</ul>
<a href="#why-another-canvas-grid-library" id="why-another-canvas-grid-library" style="color: inherit; text-decoration: none;">
<h3>Why another canvas grid library</h3>
</a>
<p>Born out of frustration, having to deal with complicated imperative canvas libraries, I wanted to create something easy to understand and declarative in nature. This Grid primitive is built on top of <a href="https://github.com/konvajs/react-konva/">React Konva</a> making it easy to customize and extend. Take a look at the storybook to learn more.</p>
<a href="#installation" id="installation" style="color: inherit; text-decoration: none;">
<h2>Installation</h2>
</a>
<a href="#npm" id="npm" style="color: inherit; text-decoration: none;">
<h4>npm</h4>
</a>
<pre><code>yarn <span class="hljs-keyword">add</span><span class="bash"> react-konva-grid</span></code></pre>
<a href="#yarn" id="yarn" style="color: inherit; text-decoration: none;">
<h4>yarn</h4>
</a>
<pre><code>npm install react-konva-<span class="hljs-built_in">grid</span> --<span class="hljs-built_in">save</span></code></pre>
<a href="#compatiblity" id="compatiblity" style="color: inherit; text-decoration: none;">
<h2>Compatiblity</h2>
</a>
<p>Konva grid will work in any browser that supports <a href="https://github.com/facebook/react/">react</a>, <a href="https://konvajs.org/">konva</a> and canvas element.</p>
<a href="#integrationsexamples" id="integrationsexamples" style="color: inherit; text-decoration: none;">
<h2>Integrations/Examples</h2>
</a>
<p>Konva Grid is a pure renderer, that will work with many third-party table plugins</p>
<a href="#1-react-table" id="1-react-table" style="color: inherit; text-decoration: none;">
<h4>1. React-table</h4>
</a>
<p><a href="https://github.com/rmdort/konva-grid/tree/master/examples/react-table">https://github.com/rmdort/konva-grid/tree/master/examples/react-table</a></p>
<p>Uses <a href="https://github.com/tannerlinsley/react-table">react-table</a> to create grouped headings and rows, and display on Konva Grid</p>
<a href="#2-excel-worksheet" id="2-excel-worksheet" style="color: inherit; text-decoration: none;">
<h4>2. Excel worksheet</h4>
</a>
<p><a href="https://github.com/rmdort/konva-grid/tree/master/examples/excel-worksheet">https://github.com/rmdort/konva-grid/tree/master/examples/excel-worksheet</a></p>
<a href="#3-zustand---more-control-over-cell-level-re-rendering" id="3-zustand---more-control-over-cell-level-re-rendering" style="color: inherit; text-decoration: none;">
<h4>3. Zustand - More control over cell level re-rendering</h4>
</a>
<p><a href="https://github.com/rmdort/konva-grid/tree/master/examples/zustand">https://github.com/rmdort/konva-grid/tree/master/examples/zustand</a></p>
<p><em>More examples coming soon.</em></p>
<a href="#usage" id="usage" style="color: inherit; text-decoration: none;">
<h2>Usage</h2>
</a>
<pre><code class="language-js"><span class="hljs-keyword">import</span> { Grid, Cell } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-konva-grid'</span>
<span class="hljs-keyword">import</span> { Group, Text, Rect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-konva'</span>
<span class="hljs-keyword">const</span> App = <span class="hljs-function"><span class="hljs-params">()</span> =></span> {
<span class="hljs-keyword">const</span> data = {
[[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>]]: <span class="hljs-string">'Hello world'</span>
}
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">Grid</span>
<span class="hljs-attr">rowCount</span>=<span class="hljs-string">{100}</span>
<span class="hljs-attr">columnCount</span>=<span class="hljs-string">{100}</span>
<span class="hljs-attr">width</span>=<span class="hljs-string">{800}</span>
<span class="hljs-attr">height</span>=<span class="hljs-string">{800}</span>
<span class="hljs-attr">rowHeight</span>=<span class="hljs-string">{(rowIndex)</span> =></span> 20}
columnWidth={(columnIndex) => 100}
itemRenderer={(props) => (
<span class="hljs-tag"><<span class="hljs-name">Cell</span>
{<span class="hljs-attr">...props</span>}
<span class="hljs-attr">value</span>=<span class="hljs-string">{</span>
<span class="hljs-attr">data</span>[[<span class="hljs-attr">props.rowIndex</span>, <span class="hljs-attr">props.columnIndex</span>]]
}
/></span>
)}
/></span>
)
}</code></pre>
<a href="#props" id="props" style="color: inherit; text-decoration: none;">
<h2>Props</h2>
</a>
<p>This is the list of props that are meant to be used to customise the <code>konva-grid</code> behavior.</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Required</th>
<th>Type</th>
<th>Description</th>
<th>Default</th>
</tr>
</thead>
<tbody><tr>
<td>width</td>
<td>true</td>
<td>number</td>
<td>Width of the grid container</td>
<td>800</td>
</tr>
<tr>
<td>height</td>
<td>true</td>
<td>number</td>
<td>Height of the grid container</td>
<td>800</td>
</tr>
<tr>
<td>columnCount</td>
<td>true</td>
<td>number</td>
<td>No of columns in the grid</td>
<td>200</td>
</tr>
<tr>
<td>rowCount</td>
<td>true</td>
<td>number</td>
<td>No of rows in the grid</td>
<td>200</td>
</tr>
<tr>
<td>rowHeight</td>
<td>true</td>
<td>function</td>
<td>Function that returns height of the row based on rowIndex</td>
<td>(rowIndex) => 20</td>
</tr>
<tr>
<td>columnWidth</td>
<td>true</td>
<td>function</td>
<td>Function that returns width of the column based on columnIndex</td>
<td>(columnIndex) => 100</td>
</tr>
<tr>
<td>itemRenderer</td>
<td>true</td>
<td>Function</td>
<td>React component to render the cell</td>
<td>null</td>
</tr>
<tr>
<td>selectionRenderer</td>
<td>true</td>
<td>Function</td>
<td>React component to render selected cell</td>
<td>null</td>
</tr>
<tr>
<td>scrollbarSize</td>
<td>false</td>
<td>number</td>
<td>Size of the scrollbar</td>
<td>17</td>
</tr>
<tr>
<td>showScrollbar</td>
<td>false</td>
<td>boolean</td>
<td>Always show scrollbar</td>
<td>true</td>
</tr>
<tr>
<td>selectionBackgroundColor</td>
<td>false</td>
<td>string</td>
<td>Background color of selected cells</td>
<td>rgba(66, 133, 244, 0.3)</td>
</tr>
<tr>
<td>selectionBorderColor</td>
<td>false</td>
<td>string</td>
<td>Border color of bounding box of selected cells</td>
<td>rgba(66, 133, 244, 1)</td>
</tr>
<tr>
<td>selectionStrokeWidth</td>
<td>false</td>
<td>number</td>
<td>Border width of selection</td>
<td>1</td>
</tr>
<tr>
<td>activeCellStrokeWidth</td>
<td>false</td>
<td>number</td>
<td>Border width of activeCell</td>
<td>2</td>
</tr>
<tr>
<td>activeCell</td>
<td>false</td>
<td>{ rowIndex, columnIndex }</td>
<td>Recently active cell that user has clicked</td>
<td>null</td>
</tr>
<tr>
<td>selections</td>
<td>false</td>
<td>Array</td>
<td>Array of selected cell areas</td>
<td>[]</td>
</tr>
<tr>
<td>mergedCells</td>
<td>false</td>
<td>Array</td>
<td>Array of merged cell areas</td>
<td>[]</td>
</tr>
<tr>
<td>snap</td>
<td>false</td>
<td>boolean</td>
<td>Snaps to the next row or column as you scroll</td>
<td>false</td>
</tr>
<tr>
<td>frozenRows</td>
<td>false</td>
<td>number</td>
<td>No of frozen rows</td>
<td>0</td>
</tr>
<tr>
<td>frozenColumns</td>
<td>false</td>
<td>number</td>
<td>No of frozen columns</td>
<td>0</td>
</tr>
<tr>
<td>showFrozenShadow</td>
<td>false</td>
<td>boolean</td>
<td>Show shadow in frozen columns/rows</td>
<td>true</td>
</tr>
<tr>
<td>shadowSettings</td>
<td>false</td>
<td>object</td>
<td>Customize shadow of frozen columns/rows</td>
<td>true</td>
</tr>
<tr>
<td>onBeforeRenderRow</td>
<td>false</td>
<td>Function</td>
<td>Called right before a row is rendered, useful for <code>react-table</code></td>
<td>null</td>
</tr>
<tr>
<td>stageProps</td>
<td>false</td>
<td>Object</td>
<td>Konva stage props</td>
<td>null</td>
</tr>
<tr>
<td>children</td>
<td>false</td>
<td>Function</td>
<td>Inject React Konva shapes using children</td>
<td>null</td>
</tr>
<tr>
<td>wrapper</td>
<td>false</td>
<td>Function</td>
<td>Inject custom context using a wrapper</td>
<td>(children) => children</td>
</tr>
</tbody></table>
<a href="#methods" id="methods" style="color: inherit; text-decoration: none;">
<h2>Methods</h2>
</a>
<a href="#scrollto-scrollleft-scrolltop-" id="scrollto-scrollleft-scrolltop-" style="color: inherit; text-decoration: none;">
<h4><code>scrollTo({ scrollLeft, scrollTop }</code></h4>
</a>
<p>Scrolls the grid to a specified <code>x,y</code> position relative to the container</p>
<a href="#resetafterindices-rowindex-columnindex-" id="resetafterindices-rowindex-columnindex-" style="color: inherit; text-decoration: none;">
<h4><code>resetAfterIndices({ rowIndex, columnIndex })</code></h4>
</a>
<p>Imperatively trigger re-render of the grid after specified <code>rowIndex</code> or <code>columnIndex</code></p>
<a href="#getscrollposition" id="getscrollposition" style="color: inherit; text-decoration: none;">
<h4><code>getScrollPosition()</code></h4>
</a>
<p>Get the current scroll position of the grid. </p>
<pre><code><span class="hljs-keyword">const</span> gridRef = useRef()
<span class="hljs-keyword">const</span> { scrollLeft, scrollTop } = gridRef.current.getScrollPosition()</code></pre>
<a href="#ismergedcell-rowindex-columnindex-" id="ismergedcell-rowindex-columnindex-" style="color: inherit; text-decoration: none;">
<h4><code>isMergedCell({ rowIndex, columnIndex })</code></h4>
</a>
<p>Check if a cell at a coordinate is a merged cell</p>
<a href="#getcellbounds-rowindex-columnindex-" id="getcellbounds-rowindex-columnindex-" style="color: inherit; text-decoration: none;">
<h4><code>getCellBounds({ rowIndex, columnIndex })</code></h4>
</a>
<p>Returns a selection <code>IArea</code> for a particular cell. Useful to get selection area of a merged cell</p>
<a href="#getcellcoordsfromoffsetsx--y" id="getcellcoordsfromoffsetsx--y" style="color: inherit; text-decoration: none;">
<h4><code>getCellCoordsFromOffsets(x , y)</code></h4>
</a>
<p>Returns exact <code>rowIndex</code> and <code>columnIndex</code> from a <code>x</code> and <code>y</code> cordinate. Useful if you want to get cell coords based on mouse position</p>
<a href="#getcelloffsetfromcoords-rowindex-columnindex-" id="getcelloffsetfromcoords-rowindex-columnindex-" style="color: inherit; text-decoration: none;">
<h4><code>getCellOffsetFromCoords({ rowIndex, columnIndex })</code></h4>
</a>
<p>Returns offset position <code>{ x, y, width, height }</code> of a cell</p>
<a href="#stage" id="stage" style="color: inherit; text-decoration: none;">
<h4><code>stage</code></h4>
</a>
<p>Access Konva <code>stage</code> instance</p>
<pre><code class="language-js"><span class="hljs-keyword">const</span> gridRef = useRef()
<Grid
ref={gridRef}
>
<span class="hljs-keyword">const</span> stage = gridRef.current.stage</code></pre>
<a href="#passing-contexts" id="passing-contexts" style="color: inherit; text-decoration: none;">
<h2>Passing Contexts</h2>
</a>
<p>React Konva uses <code>react-reconciler</code> to create a custom React renderer. Which means Top Level Context is not available inside the canvas. We provide a simple <code>wrapper</code> prop to pass Context to the Grid</p>
<pre><code class="language-js"><span class="hljs-keyword">const</span> ThemeContext = React.createContext({})
<span class="hljs-keyword">const</span> theme = { <span class="hljs-attr">color</span>: <span class="hljs-string">'yellow'</span> }
<Grid
wrapper={(children) => {
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">ThemeContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{theme}</span>></span>
{children}
<span class="hljs-tag"></<span class="hljs-name">ThemContext.Provider</span>></span></span>
)
}}
/></code></pre>
<p>This will let you use ThemeContext is any of the React Konva components. To access theme inside <code>Cell</code>, you could do</p>
<pre><code class="language-js"><span class="hljs-keyword">const</span> Cell = <span class="hljs-function">(<span class="hljs-params">{ x, y, width, height }</span>) =></span> {
<span class="hljs-keyword">const</span> theme = useContext(ThemeContext)
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">Rect</span>
<span class="hljs-attr">fill</span>=<span class="hljs-string">{theme.color}</span>
<span class="hljs-attr">x</span>=<span class="hljs-string">{x}</span>
<span class="hljs-attr">y</span>=<span class="hljs-string">{y}</span>
<span class="hljs-attr">width</span>=<span class="hljs-string">{width}</span>
<span class="hljs-attr">height</span>=<span class="hljs-string">{height}</span>
></span>
)
}</span></code></pre>
<a href="#storybook" id="storybook" style="color: inherit; text-decoration: none;">
<h2>Storybook</h2>
</a>
<p>Examples can be found in <code>stories</code> directory. To run storybook, enter the following commands</p>
<pre><code class="language-bash">yarn
yarn run storybook</code></pre>
<a href="#contribution" id="contribution" style="color: inherit; text-decoration: none;">
<h3>Contribution</h3>
</a>
<p>Feel free to fork and submit pull requests</p>
<pre><code>git clone https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/rmdort/</span>konva-grid.git
cd konva-grid
yarn
<span class="hljs-regexp">//</span> Run storybook
yarn storybook </code></pre>
</div>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<nav class="tsd-navigation primary">
<ul>
<li class="globals ">
<a href="globals.html"><em>Globals</em></a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_cell_.html">"<wbr>Cell"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_grid_.html">"<wbr>Grid"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_helpers_.html">"helpers"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_hooks_useautosizer_.html">"hooks/use<wbr>Auto<wbr>Sizer"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_hooks_usecopypaste_.html">"hooks/use<wbr>Copy<wbr>Paste"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_hooks_useeditable_.html">"hooks/use<wbr>Editable"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_hooks_usepagination_.html">"hooks/use<wbr>Pagination"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_hooks_useselection_.html">"hooks/use<wbr>Selection"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_hooks_usetooltip_.html">"hooks/use<wbr>Tooltip"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_index_.html">"index"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_types_.html">"types"</a>
</li>
<li class=" tsd-kind-module">
<a href="modules/_utils_.html">"utils"</a>
</li>
</ul>
</nav>
<nav class="tsd-navigation secondary menu-sticky">
<ul class="before-current">
</ul>
</nav>
</div>
</div>
</div>
<footer class="with-border-bottom">
<div class="container">
<h2>Legend</h2>
<div class="tsd-legend-group">
<ul class="tsd-legend">
<li class="tsd-kind-object-literal"><span class="tsd-kind-icon">Object literal</span></li>
<li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li>
<li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li>
<li class="tsd-kind-function tsd-has-type-parameter"><span class="tsd-kind-icon">Function with type parameter</span></li>
<li class="tsd-kind-type-alias"><span class="tsd-kind-icon">Type alias</span></li>
<li class="tsd-kind-type-alias tsd-has-type-parameter"><span class="tsd-kind-icon">Type alias with type parameter</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-enum"><span class="tsd-kind-icon">Enumeration</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li>
</ul>
</div>
</div>
</footer>
<div class="container tsd-generator">
<p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p>
</div>
<div class="overlay"></div>
<script src="assets/js/main.js"></script>
<script>if (location.protocol == 'file:') document.write('<script src="assets/js/search.js"><' + '/script>');</script>
</body>
</html>