UNPKG

react-konva-grid

Version:

Declarative React Canvas Grid primitive for Data table, Pivot table, Excel Worksheets

531 lines (526 loc) 23 kB
<!doctype 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> =&gt;</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">&lt;<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> =&gt;</span> 20} columnWidth={(columnIndex) =&gt; 100} itemRenderer={(props) =&gt; ( <span class="hljs-tag">&lt;<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>]] } /&gt;</span> )} /&gt;</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) =&gt; 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) =&gt; 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) =&gt; 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() &lt;Grid ref={gridRef} &gt; <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> } &lt;Grid wrapper={(children) =&gt; { <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ThemeContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{theme}</span>&gt;</span> {children} <span class="hljs-tag">&lt;/<span class="hljs-name">ThemContext.Provider</span>&gt;</span></span> ) }} /&gt;</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>) =&gt;</span> { <span class="hljs-keyword">const</span> theme = useContext(ThemeContext) <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag">&lt;<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> &gt;</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">&quot;<wbr>Cell&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_grid_.html">&quot;<wbr>Grid&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_helpers_.html">&quot;helpers&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_hooks_useautosizer_.html">&quot;hooks/use<wbr>Auto<wbr>Sizer&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_hooks_usecopypaste_.html">&quot;hooks/use<wbr>Copy<wbr>Paste&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_hooks_useeditable_.html">&quot;hooks/use<wbr>Editable&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_hooks_usepagination_.html">&quot;hooks/use<wbr>Pagination&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_hooks_useselection_.html">&quot;hooks/use<wbr>Selection&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_hooks_usetooltip_.html">&quot;hooks/use<wbr>Tooltip&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_index_.html">&quot;index&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_types_.html">&quot;types&quot;</a> </li> <li class=" tsd-kind-module"> <a href="modules/_utils_.html">&quot;utils&quot;</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>