-
Notifications
You must be signed in to change notification settings - Fork 4
/
rss.xml
220 lines (220 loc) · 90.9 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ReactGrid's blog RSS feed]]></title><description><![CDATA[Spreadsheet experience for your React app]]></description><link>http://github.com/dylang/node-rss</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 08 Oct 2024 08:55:36 GMT</lastBuildDate><item><title><![CDATA[Financial liquidity planner with ReactGrid and Chart.js]]></title><description><![CDATA[We made ReactGrid to compose your view with arbitrary cell order. In many components and tools in React ecosystem you have to
keep the same…]]></description><link>https://reactgrid.com/financial-liquidity-planner-with-reactgrid-and-chartjs/</link><guid isPermaLink="false">https://reactgrid.com/financial-liquidity-planner-with-reactgrid-and-chartjs/</guid><dc:creator><![CDATA[Patryk Eliasz]]></dc:creator><pubDate>Fri, 05 Mar 2021 00:00:00 GMT</pubDate><content:encoded><p>We made ReactGrid to compose your view with arbitrary cell order. In many components and tools in React ecosystem you have to
keep the same data schema in all rows. Our component breaks out from the frame. You can add it to your project simply by
running: </p><div id="87547e63e1bd67105f8d023b24d82512" file="install.sh"></div><p>Even if you don’t need it in your project right now, you can leave a ⭐ in our <a href="https://github.com/silevis/reactgrid">Github project</a>.</p><p>Today we will show you how to build a liquidity planner - an app that provides a strategy for financial planning in a long term.
Our app will provide entering, aggregating, and evaluating planned cash flows.</p><p><img src="/b97590fffcddc4a4daafe1bdb520115a/finished-app.gif" alt="Finished app"/></p><p>On the Internet, you can find many spreadsheets files that e.g. accountants and financial analysts use -
one of them, as an inspiration, will be moved from spreadsheet to standalone, fully <strong>reactive</strong> React.js app.
We also visualize common parameters that help in decision-making with the Chart.js library.</p><h2>What is ReactGrid made for?</h2><p>ReactGrid was designed to handle complex data displaying and editing in an arbitrary way. We depart from the rule of placing
cells in the same order in every row - therefore you are able to add a spreadsheet-like experience to your React app.</p><p>Liquidity planner is one of the many cases, in which 90% of React data table components usage is insufficient to recreate the expected look and feel.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:68%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAADoklEQVQ4y0VTb0xbVRQ/D5iZos7xYZuLiTizD1MzjfGT8cvU6Db3dW7GSZTMzG0fyGRzhsShxq2M0dEBCUT5AFs0BhbWxIIBtlK0/4D6WpgTHqUt9A+v0Nf2vb7+e30t13PfVnaTX+7vnnvuuffc3zkQFtdgJRkFQgikS3mQcR6yj2rrVD4Daqmo8VxB0Wa6DsQiEM+I2priz3k7bA5uNQBc2L8lnc9+GM+l9hMLgSOnP2Goo5SVAe2MUlRBzMk7skr+mJhJ7V0I+XZGEms7C4WCFrDNfOtxQGVDhSIhu3HjWjQRuxoS+Ebk9SVCjq6nEs9Ylv6BnKpAXBb3ob0lGAm3hlbDV5A3rUvxPdl8DrKFPDMbWQTn8hzQGyp8Af9nUT7aEgqGflpYWmyPxWLt4UhYlykpuxOyCFIxuytJ8lvbOttfmnJN62bnZg08z98okOIrWtoqYcrpg0o2alb5VZ3NZusaGx/vNZlMPXrD9bqjnx5/TsSUF4O+PbIst6JzHSb0dF9/32mz2dxzzzJxHG1bEdvzhDAqKcEGzuWPfSGZll7FVA5wgSWaGoxO3NVejzgz88Dzu6DKX9P1j7/oqywu27NBga9R1EIjH43qxHSqIRyP7tBeSAclvLAGvmU/uCwO8K8E6MtrWY/7B4fd3jnNzhiMI380oeuuDw68XzE5ZaNnnkKcdXP/dq9nkteRn1QIqYahwdvA+byVNOhaUnhZTqe/WY4EaxWivsi62cumYVOr0+nssFqt3XX1n7/xKKNKDF7V0dX5DrfI6V0u1zWWZXtt0463YMh4h2nRt5ZfesIb9Pelc5nz9NDJunpq24LYi3ib2rSApYcCTLtdr4mSqBfi8eZ5L3cIQ1RAQ+M5iucHbg9+i8LcGLs73ulwOLpHx8cuX/q++c3+X29qh32RlW2ClPxiPS5cwFI5XCCkioqCX0OV3ma8Y4RzTRcAsGQgEFp51+Px3MSAPcMjI1fuTZgvWpEveLmzX773cVm4+lQu3WGbm+lFrsuouVpqz5cK+N8E5u//x4xYxqDsXI04hDiIePJR+jVlPjxoBMeU85SbZX+2Tznb7A674QE3/7plcpIKWJnISMBLgtaqD/uTbGz2ZVn6Mjf/bWH6f7sFh498tE93taVhYGCga9hk6g2GQxdx/wlNzFT8cetJuXS50Wm1M4KcBFnJAt7KLAS88F3zJTh15isGXaupsn/ZrQeDoaABa/bEMTTQDV6Kbcb7HzWP1x2P8ChtAAAAAElFTkSuQmCC&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="ReactGrid use cases" title="ReactGrid use cases" src="/static/2a5f38fe55102dba6821cc0e53026baf/00d43/reactgrid-use-cases.png" srcSet="/static/2a5f38fe55102dba6821cc0e53026baf/63868/reactgrid-use-cases.png 250w,/static/2a5f38fe55102dba6821cc0e53026baf/0b533/reactgrid-use-cases.png 500w,/static/2a5f38fe55102dba6821cc0e53026baf/00d43/reactgrid-use-cases.png 1000w,/static/2a5f38fe55102dba6821cc0e53026baf/aa440/reactgrid-use-cases.png 1500w,/static/2a5f38fe55102dba6821cc0e53026baf/7970d/reactgrid-use-cases.png 1908w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">ReactGrid use cases</figcaption>
</figure></p><h2>Planning app with ReactGrid</h2><p>Before we start coding, we will talk about good practices that help to make predictable apps. Obviously, it’s a good idea
to apply them in all projects:</p><ol><li><p><strong>Separate the data from ReactGrid</strong></p><p>The main concept of <strong>reactivity</strong> is updating your view every time your data changes.
Very often we have no influence on the data structure, but we can map it to the structure that is the most convenient for us.
Before the data becomes acceptable by ReactGrid or Chart.js we have to calculate a few variables in a repeatable way.
This data lives only “for a moment” and should be independent from data and ReactGrid internal interfaces like <code class="language-text">Column</code> and <code class="language-text">Row</code>.</p><div id="87547e63e1bd67105f8d023b24d82512" file="separating-data.tsx"></div></li><li><p><strong>Apply changes directly to the data</strong></p><p>ReactGrid contains its own encapsulated state to manage many background functionalities, like virtual scrolling or rendering optimizations.
This state is based on <code class="language-text">rows</code> and <code class="language-text">columns</code> - two necessary ReactGrid props. ReactGrid is read-only until you define
your own changes handling function, but a good practice is to update data at its source.
After that the cycle of <strong>reactivity</strong> concept is complete.</p><p>Be aware of updating data that is directly related with ReactGrid interfaces ⚠️</p><div id="87547e63e1bd67105f8d023b24d82512" file="handling-changes.tsx"></div></li><li><p><strong>Use Typescript wherever it is possible</strong></p><p>Typescript prevents us from possible bugs at runtime. We encourage you to use it, especially with ReactGrid.
For cell templating reasons we introduced a few types of how the cell templating engine interfaces with ReactGrid.
Thanks to this, you can safely transfer data between incompatible cells while interacting with them using the cell editor or,
for example, pasting data from external sources or even other spreadsheets.</p><p>A concrete example will be shown in the next chapter, but for now, take a look at the tiny example of Typescript
<a href="https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions">discriminating unions</a>.
Implemented in ReactGrid, <code class="language-text">CellChange</code>interface <code class="language-text">type</code> field allows you to ensure that the <code class="language-text">checked</code> field on <code class="language-text">newCell</code> really exists.</p><div id="87547e63e1bd67105f8d023b24d82512" file="typescript.ts"></div></li><li><p><strong>Compose your cell styling and behavior</strong></p><p>In most cases, you will use built-in cell templates like <code class="language-text">NumberCell</code> or <code class="language-text">DateCell</code>. ReactGrid allows you to style
a cell and its behavior without introducing a new cell template, for example, “non-editable number cell with blue background”.
Instead, you can compose the functions as follows:</p><div id="87547e63e1bd67105f8d023b24d82512" file="composing.ts"></div></li><li><p><strong>Avoid merging metadata with cells</strong></p><p>Since all of the cells have no idea where they are placed, it’s tempting to extend them with some metadata.
By metadata we mean the data that was added to ReactGrid related interfaces (e.g. <code class="language-text">Row</code>) by extending them with new attributes.</p><div id="87547e63e1bd67105f8d023b24d82512" file="metadata.ts"></div><p>You can also come across a similar situation that has arisen when some cells are related with each other - when building a tree list.
Let’s have a look at the <code class="language-text">ChevronCell</code> interface:</p><div id="87547e63e1bd67105f8d023b24d82512" file="ChevronCell.ts"></div><p>Except for <code class="language-text">parentId</code> (which can actually be a row, column, or another cell) you can only control its look.
There is no place declaring tree structure, dependencies between cells, or other data.
We recommend extracting this metadata. The easiest way is to move this logic into a separate React hook that will contain
those variables/data/logic.</p><p>We will show you how to implement row toggling and working with tree-like structures in the next article.</p></li></ol><h2>What does this planner do?</h2><p>We could debate financial issues for a long time, but there is not enough room for it so let’s look at them in a nutshell. You can skip this chapter if you like.</p><p>You can place a new value only in light green cells (credit line, opening balance) or with white background (cash inflows and outflows).
Grayed-out cells are read-only.</p><p>Two of them (opening balance and credit line) are just numbers. We merged types of cash flow into two.
Each entry is called “group” and has its own title like “Travelling expenses” for outflow and “Sales” for inflow.
Except for the title, all groups have an array of the amount of money spent/earned each month.</p><p>Groups are aggregated vertically (inflows and outflows separately) into a total inflow or outflow per month.
The last column presents all of the totals in a calendar year.</p><p>“Cash in” and “Cash out” make up the “Total” row. This row and the other cells should update their content when sourcing data
has changed or e.g. user typed a new value into the cell.</p><p>Some items remain to be explained (and it’s the hardest thing to understand): </p><ul><li>“Cumulative row” takes cash in the bank, adds “Cash in” and then subtracts “Cash out”.</li><li>User can manipulate the value of a green cell “Cashbox/bank” and is called an “Opening balance”. The rest of the cells in this row are
filled automatically by moving already calculated value from the cumulative to the next month in the “Cashbox/bank”.</li><li>these operations are repeated until all months are filled.</li></ul><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:52.800000000000004%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAADL0lEQVQozy1TSUwTURh+RItQAxLDQTQYDwQESiLqwaipwYNL3BJENMhFTxrjkmjiwUSjRo0mmnDx4hITD8REA2oVEAZhKAGKCy1YKNN2pp1OZ2k7aym1Hfv8x3h4+d/6ve/73vdQIpFALMu2RqPRMMdxzRhj5PP5WqBRfr//iDVuO9FehhBaBa3YZrOVOBwOe21trb2urs7e0NBgr6qqKoW1EqfTuRJtq21Csiw/SaVS31VVJQB4eGlpidA0zW0YBpHN5RoZfxxB/wHs+6YbxkE4jH7/yZctm7nVcGHJIrW4tru7ez1BEPVw7coKRVHcyWQSG7qBRUHEAI6TiQTWjTRmfo60J1x3HLwg9auKgtO60ckkY8ijzqMf0gL64B9GWSNjq2+ot42NjTWh4oPr1gDgoAUia8rvCM+aqqKakiQVoGbdntG2Z71Pm9hYzMWKHGYE9mrvzy+7vdH5YxPBH4d6pwf2HDndWmpZQ5KkA1U+ulcGgEMyAKY01WT4OFZkuQDyMDDKk96Z1oeunkae5/siIo+Dce7mwPxsx2wsctFDB89/8ftONDt3lYMLRcDQgVre4nJZUYgUSJb1jBmISFiSxP+Acm7cx7S/GBG2cnH+MyNoOCQYtwgq3/mLy17xhJcukaFcx979hzfs276peGpyssnytxQOE5aH2WzWDIVCeGpqqhAM05aHeeLryPEbt+83QgI+BRYpbOZz7Za8UZK84p7wdH7qHzz95m3PBcCxDX51b0ZoRfFaxWIIkuF1TV3Xsa5rhVhwFouRQJ72kkfJVzcaRVFwpbgwTslyx7Iuo8DMxMbUAnlODU9fTsdmLwXGe8/OuT+2/GMIYEOiKGKIjalqGlSloAg0llgqo/DhMzOk66Qg8H2yEMULC4HLwRC9o//1Y7vue799OUTuNIJuJzP5zilR09tQdXV1BcglBEHAIOufZKgFlotjXpTSYZq5PkK6r9E0TdBMBHu93rtzc3N3n798VWkF/X/giywbIpxYg7q6uqzQDoFcDNWEn4MzmUwhA2OYK6TTxhZrMwS9D5hjUHKKoigEc0UH9u4qObxvzyqr3/1xFImCUPMXkD5if1eAPaAAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Calculating totals and cumulative" title="Calculating totals and cumulative" src="/static/19a97d36ea0991d8c35a5112492422b2/00d43/calculation-pseudo-algorithm-part-1.png" srcSet="/static/19a97d36ea0991d8c35a5112492422b2/63868/calculation-pseudo-algorithm-part-1.png 250w,/static/19a97d36ea0991d8c35a5112492422b2/0b533/calculation-pseudo-algorithm-part-1.png 500w,/static/19a97d36ea0991d8c35a5112492422b2/00d43/calculation-pseudo-algorithm-part-1.png 1000w,/static/19a97d36ea0991d8c35a5112492422b2/aa440/calculation-pseudo-algorithm-part-1.png 1500w,/static/19a97d36ea0991d8c35a5112492422b2/e8950/calculation-pseudo-algorithm-part-1.png 2000w,/static/19a97d36ea0991d8c35a5112492422b2/742d3/calculation-pseudo-algorithm-part-1.png 2550w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Calculating totals and cumulative</figcaption>
</figure></p><p>The last part is the “Credit line”. Users can update it by typing it in. This variable is constant for all months and is used for
calculating “Credit line overdraft” - in a nutshell - if the absolute value from “Cumulative” exceeds the given credit line,
then the cell should display this result.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:30%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAABtUlEQVQY0yWRzWsTQRjG39CCqf0EST00FO2lUME/QoTk4qnXQs7aQ7140FP+AL1I20PpNSKBgFnjLohNzKVmL0sglcluc8huCOb7Y7NJmzDb6TPxhZeZd5hnnt/7DhEiGAzScDjUXdcVSN7r9QTSH41Gsi6nUqnler3+rNvtik6n47Tb7XXUD2zbXlFVdblSqaxqmvbUMIw1CofDjxGbEOrj8Vj0+31eq9XEZDLxpYHneQyegXw+v4vHRKvVstPp9FIsFiMhhORZkWskEiFd1wPyYAP5COICxKLZbPJqtSrJfA+EMCkmk8ndXC73otFocFA5lmU9jMfjhHtHoLMdx4m9Oz8j6+evRQqFQhSNRgnCAgSiVCrNH/zLmO/CYDKdXmU0bRXUexjLHUbhMMbmdIlE4gD7k5PT031ZG1++0jxkAboCnEWxWORDtHp5kfXZ5R/RMi32Sc3Q+OZmW3aAtNH6a0VR3kK6dWUYtL2+8fz75+OP5W/KezJNk2azmfyUAuYmpre33MMs+cjzm2VTtK1rtqMkaeC6T7z/n2QPBoM3nPNXL9HZj3ptAUCBf9nfh9cZ9cM9WhlOxiDOxpcAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Calculating credit line overdraft" title="Calculating credit line overdraft" src="/static/b8e909f4ba00810432efa358a83e2bbe/00d43/calculation-pseudo-algorithm-part-2.png" srcSet="/static/b8e909f4ba00810432efa358a83e2bbe/63868/calculation-pseudo-algorithm-part-2.png 250w,/static/b8e909f4ba00810432efa358a83e2bbe/0b533/calculation-pseudo-algorithm-part-2.png 500w,/static/b8e909f4ba00810432efa358a83e2bbe/00d43/calculation-pseudo-algorithm-part-2.png 1000w,/static/b8e909f4ba00810432efa358a83e2bbe/aa440/calculation-pseudo-algorithm-part-2.png 1500w,/static/b8e909f4ba00810432efa358a83e2bbe/e8950/calculation-pseudo-algorithm-part-2.png 2000w,/static/b8e909f4ba00810432efa358a83e2bbe/742d3/calculation-pseudo-algorithm-part-2.png 2550w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Calculating credit line overdraft</figcaption>
</figure></p><p>What about the chart? This part should give us an instant knowledge about the company’s state of finance. In this case, we limit ourselves
to display “Cashbox/bank” and “Credit Line Overdraft” as a line chart and cash inflow and outflow as bars.</p><h2>Some examples</h2><p>In the penultimate chapter, we discussed 5. tips for good app implementation with ReactGrid. We will not discuss every line of code, but only code fragments in terms of the above-mentioned tips on how to work with ReactGrid.</p><ol><li><p><strong>Separate the data from ReactGrid</strong></p><p>App component named <code class="language-text">LiquidityPlanner</code> has four React’s <code class="language-text">useState</code> hooks, each of them stores part of raw financial data.
E. g. <code class="language-text">cashInflow</code> is initiated with <code class="language-text">emptyInflows</code> that comes from the <code class="language-text">rawData.ts</code> file. This data has no connection
with ReactGrid’s interfaces, and can be used directly by other components like charts.</p></li></ol><div id="87547e63e1bd67105f8d023b24d82512" file="cash-inflow.ts"></div><ol start="2"><li><p><strong>Applying changes to the data</strong></p><p>ReactGrid runs your changes handler function when you interact with the data displayed by the grid.
Each change is applied by dispatching <code class="language-text">setCashInflow</code>. To set updated inflows we used a technique called currying
(<a href="https://javascript.info/currying-partials">more info</a>), to apply particular <code class="language-text">change</code> on desired groups (<code class="language-text">cashInflow</code>).</p></li></ol><div id="87547e63e1bd67105f8d023b24d82512" file="applying-changes.tsx"></div><p> <figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:68%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAADFElEQVQ4y2VUO2wdRRRd26kiIaG0lBElJZEoIkU0aSigI6RAaaigQIEiKEhEChIlBheRiyDxcSI5iUiiOHws7Lw4MTjv47fz9r87s7O/t7vv2R3deg935jkWhJWOrmZn9uy55947hkFPVMh5WWVGXKZvmdLFX5F5MIjtdiBsRGMJXiaIysNI67CIdTxCIdtwHKt4oPgMWizwcWLQ5nkv52DSa3qeCScN4coQVuTC5h4s7sKRwRHpC2jVe+NQ4QK9MAjnBKlwZdAEmWjvrN3FRu8JWOajLyzskmIWOuBVotX+C1qhIp0RjuUCQROqA5bwmoHH2oed3zEcDhHyCEEYYpT4GLgMZmDDFjPFFil3Yh+UIYEUXvjog7loLztG/mlCJduO/YaFdutVMdw0AqMP7SRAPxrB5A62WRcj6cFNQthpgK43xCC04BcCxpm3zxoAjJ6wDErzXLZfqoONEwdtQN7YWQiHwKcZvl9dwfqTTUR1CjeL0I9tva9+quDkEYxXXjv5KsPfL1Pmc+l++d6vnXX0A9bQQU04S2VW3V3hQMgYpsUgYoGepC7IY1R1hXpSk5cpjM7OU+T1GOFYWn+w7U9uP7gL07UOwkS0u7Hzn0qqDojrDH7KkU4L/RMVqeWQ1LneM8R+0cmnZT+okx+2nO6HwSTBiLsHNqXMUh8B+RLkAsqK71Z/xMfXv8QXa9dwhbD8+Ba++u06Lt9bwqWfv8Hy1i1o/1S6KlKF31XGMuE2Q263cq+AqFL4GUe1V+Pqg2XMX3wdL10+g+OXTuPU1+/jxOdv4vhnpzH36Sm8sXhh1odENr8TmUdt4+g+5O1WdxuDYISwmk3Eur2Nxc0VfLuxgqXNm7jx7CGuPV7F0qObWNz4CTd21mDctzqGm0d6Up4T2sJviLS988s9/DnsKgv0hPhljKIagycC9f4ExaTUyqu9SavX0/J/jX1eKaEUG1P14ZSqm0swz0YgOai10PeZ7kuffJ2Ba1DBWrWezXIRz6vRI7J3Dge/Ie/aIV0UXT5Cj/AsYhgmrvZUXRQvXBDt8/gP+rKI4hX6FJcAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Reactivity cycle" title="Reactivity cycle" src="/static/852cf9838a4fc8fcfcb133d8da0a0e0d/00d43/reactivity-cycle.png" srcSet="/static/852cf9838a4fc8fcfcb133d8da0a0e0d/63868/reactivity-cycle.png 250w,/static/852cf9838a4fc8fcfcb133d8da0a0e0d/0b533/reactivity-cycle.png 500w,/static/852cf9838a4fc8fcfcb133d8da0a0e0d/00d43/reactivity-cycle.png 1000w,/static/852cf9838a4fc8fcfcb133d8da0a0e0d/aa440/reactivity-cycle.png 1500w,/static/852cf9838a4fc8fcfcb133d8da0a0e0d/7970d/reactivity-cycle.png 1908w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Reactivity cycle</figcaption>
</figure></p><p> Implementing changes handling in this way closes the reactivity cycle, therefore our task of processing input data
into outputs is fully repeatable and has no side effects.</p><ol start="3"><li><p><strong>Typescript + ReactGrid = ❤️</strong></p><p>ReactGrid is built with Typescript and fully supports it. We also encourage you to use it in your projects.
A real example from our app shows how we narrowed down the expected change object type exclusively to <code class="language-text">NumberCell</code>,
therefore you are sure that you are able to access only actually existing fields.</p></li></ol><div id="87547e63e1bd67105f8d023b24d82512" file="typescript-and-reactgrid.ts"></div><p> The other part is e. g. extending the set of built-in cell templates with your own. To be able to do it you have to pass the
name of your custom Cell interface into a generic <code class="language-text">CellChange</code> interface.</p><p> Of course, you are not obliged to move your project right now to Typescript, but we highly suggest using static typing.</p><ol start="4"><li><p><strong>Compose your cell styling and behavior</strong></p><p>When you work with ReactGrid it’s highly possible that you will need to achieve the same or similar behavior
or styling on many cells. The solution is quite simple - small, reusable functions.
Familiarity with <a href="/docs/">documentation</a> will definitely be useful.</p></li></ol><div id="87547e63e1bd67105f8d023b24d82512" file="composeable-functions.ts"></div><p> and the usage: a function that uses mentioned functions to fill up the cells array in a single row.</p><div id="87547e63e1bd67105f8d023b24d82512" file="defining-row.ts"></div><h2>Live demo</h2><p>We created a fully working liquidity planner example on
<a href="https://codesandbox.io/embed/reactgrid-liquidity-planner-526ps?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2FLiquidityPlanner.tsx&amp;theme=dark">codesandbox.io</a>.
This sample runs with the ReactGrid MIT, we encourage you to visit a <a href="https://reactgrid.com/">fully functional sample</a>
deployed at our website.
There you can try extra features available only in the PRO version: fill handle, range selection, area copy/cut/paste. You can compare both versions <a href="https://reactgrid.com/feature-comparison">here</a>.</p><iframe src="https://codesandbox.io/embed/reactgrid-liquidity-planner-526ps?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2FLiquidityPlanner.tsx&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" title="reactgrid-liquidity-planner" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe><h2>Summary</h2><p>Liquidity planner is only one of many possible ReactGrid use cases when the standard data table is not enough.
The main purpose of this article was to show you five useful tips that help you start the ReactGrid project using good practices.</p><p>Don’t forget to leave a ⭐ on our <a href="https://github.com/silevis/reactgrid">Github</a>.</p><p>We encourage you to visit our official <a href="https://reactgrid.com">ReactGrid website</a>,
where you will find the documentation and information what the PRO version of our component offers.</p><p>Bye 👋</p></content:encoded></item><item><title><![CDATA[How to build a table with concurrent horizontal and vertical sticky headers in pure CSS]]></title><description><![CDATA[While creating user interfaces, we often encounter the problem that there is more data
in our tables than can be fitted in the visible…]]></description><link>https://reactgrid.com/how-to-build-a-table-with-concurrent-sticky-headers/</link><guid isPermaLink="false">https://reactgrid.com/how-to-build-a-table-with-concurrent-sticky-headers/</guid><dc:creator><![CDATA[Kamil Mysłek]]></dc:creator><pubDate>Mon, 18 Jan 2021 00:00:00 GMT</pubDate><content:encoded><p>While creating user interfaces, we often encounter the problem that there is more data
in our tables than can be fitted in the visible viewport. To achieve
an excellent user experience on components like Gannt charts, data tables and spreadsheets,
we often use the sticky CSS property on the header elements.
This is a simple task when doing it only on one edge of the table.</p><p>But what if we want to display a huge table and therefore need sticky headers on more edges simultaneously?
This is the exact problem we faced while building our ReactGrid and in this article we want to share the solution we have found.</p><p><img src="/b37e6367104ee32f2b08c9c083e8cc9e/example-article.gif" alt="ReactGrid example with horizontal and vertical sticky headers"/></p><p>In this guide, we will show you how to create the layout to achieve
a native scroll behavior with sticky headers like those shown above <strong>without using any JavaScript.</strong></p><p>What are the benefits of the proposed solution?</p><ul><li>native support from modern browsers,</li><li>excellent user experience (intuitiveness, swiftness),</li><li>additional elements in the scrollable view do not affect the UX,</li><li>no JavaScript, only CSS and HTML,</li><li>works perfectly on touch devices,</li><li>avoids using z-index not to affect other elements on the website (needs z-index style values in Firefox with this solution).</li></ul><p>You will learn how to place elements in the DOM and style them to achieve fully functional scrollable sticky panes using flexbox step by step.
It’s working and has been tested on the following browsers (remember, Firefox needs z-index style values).</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:675px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:21.2%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPUlEQVQY02OQnrzvvP3BxqD8Dd0Mt/KS5z7sz0ne09bDUNZRdvT44ryZ5bPXMASteLQ4YNG9ha6L7zOErH7RE7ri6aaqZU8Ypsz71zln4c8bXxM8GT4Lq9rfU9NtYWDwmmXMp5rPvdmjmuGyYbDZPecwmZN9VQy6a1MN15+LUcndvoTBq/uwinPnKWWLktUMxrGtegZJEy2a1/5niExYYZKZv9+DAQg2MIsJ3Ta2UmNgY2CQE2Bi4KnPLGYGiosDsYCvqx0HlC2W2FXDCmULZczeDlIjD8TKChqGfEBaBYi1pcTFhRhYWASBbFmGxJUdmpsuHBL5//8/d8OGSUrrTm4XA7Grd0xVmHditSSQzVWxdYZi676lsv///Odp275QtmH9bLn/n/7z9G5aIlM9b4oiSP2sQysla9b1KQEAYTR9OepbXwcAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Supported browsers" title="Supported browsers" src="/static/d843eb0d80a758098513b36dbf93c6cb/23296/rg_article03_image01.png" srcSet="/static/d843eb0d80a758098513b36dbf93c6cb/63868/rg_article03_image01.png 250w,/static/d843eb0d80a758098513b36dbf93c6cb/0b533/rg_article03_image01.png 500w,/static/d843eb0d80a758098513b36dbf93c6cb/23296/rg_article03_image01.png 675w" sizes="(max-width: 675px) 100vw, 675px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Supported browsers</figcaption>
</figure></p><h2>Placing Elements in the DOM using FlexBox</h2><p>The first and most important aspect is the correct order of the elements in the DOM because elements that appear later in the DOM will overlay other elements.</p><p>Let’s imagine that we want to create a layout with sticky headers on each edge. Therefore we need to divide our surface into 9 panes.
Of course, the same procedure also applies to a smaller number of sticky edges.</p><p>We place everything in two DIV tags. The first with <strong>overflow: auto</strong> CSS property to have a scrollable view. The second with the following CSS properties:</p><ul><li>position: relative;</li><li>display: flex;</li><li>flex-wrap: wrap;</li><li>justify-content: flex-start;</li><li>align-items: flex-start.</li></ul><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:404px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:60.4%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACFElEQVQoz21TaW/aQBD1//8XlapW6peqaqRe+dC0anMQCA0YG3PZYHxhfIBv432dXaCJ4q602tGbHe+8N8/Sq85F8Mee4MHW2NWsh7cPX9AzVdwYA9waQ7E7qxF61pjiJ6y7UnCp3eFd/xsGzpR11yp+Lfq1xAOUB+Y7HvhS3QVerqasEWz8Fp6UGYzIQRGnCP2AcUzqmCOWRDuoispqNHi0JuIyY+xfoe/7mE6OOJ7hQRpjFq7hmBZbLEQjTLrUbnFgDYqmhrvbCjpFXSLOE+yLFLs8RVoXlK8oPmG086rAKnRxT3I0YOIOz0uvu58w9VdQNzo6yxHeD74TbZ06nWJoTzGw+TnDyJ2f4iOmkDS/9Ud8lH9gQvUyz1ON1DUVNEUNz3bByYyceUsrVjfYhXEL590uQgtVkmN70phriHxPifkcB2qev/Jy1VWFrb9t4WG2wywwsafHTNM8fvDr+AYV6ZcdCtjxBtdEIy1zcTnK94iy/VFPmiiPz5iYcGDjbimjpPp9xfM7SG96n4WG440hvPVheAXNM4RuMtGX6VQ8HWN/+YQ5M4wJu9YHuJB/Yk60+R2el+5NhRFT+K4nDKE4bR/mSQbXcv6roR7ZSIIYvrc5UuaTIt8wz/OQ0ej7a00kDg0p2jQijqIIhmG0cD8JoW1XsE0L+ikv0e9Wn4fJdTkbu2FNy+Dn+HxyYz93RV6V+AuU7IfKVCdILAAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Table with elements DOM positions" title="Table with elements DOM positions" src="/static/a919b0373fed7019e02946144c6669cb/494f9/rg_article03_image02.png" srcSet="/static/a919b0373fed7019e02946144c6669cb/63868/rg_article03_image02.png 250w,/static/a919b0373fed7019e02946144c6669cb/494f9/rg_article03_image02.png 404w" sizes="(max-width: 404px) 100vw, 404px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Table with elements DOM positions</figcaption>
</figure></p><p>The graphic above illustrates the proper DOM order of the elements. The first element should be the one in the center of the layout, let’s call it “middle-center-pane”.
It’s the only DIV element which has a relative position in its CSS styles. The next elements, in order from the least to the most important, are bottom-center-pane,
middle-right-pane, top-center-pane and midlle-left-pane. These elements have sticky positions in CSS styles. The same situation applies to the elements located in the corners of the view.
We placed them in the order: bottom-right-pane, bottom-left-pane, top-right-pane, top-left-pane.</p><div id="715117ad84f80cde284a91a034ce829b" file="sticky-headers.html"></div><p>Now we know in which order the panes should be placed to achieve the correct overlay behavior. However, we need more. For example, our top-left-pane is the last element in the DOM
and will be rendered as the last one on the screen. What we need to do is to change the visual order of the elements. This can be achieved by using the CSS order property.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:404px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:60.4%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACOUlEQVQoz42SaW7jMAyFff/L9AaDDtBOOwjaptncuInlVd7lLbbjONUbSkbyexQQCvlogfxI4yPYi+QkkHal9ESMbWwjaQrwOtMW1TnSU4mYcm6+umPKcQTHV+JADK2M2hyuiCZD/Tl3gwyDEP10hlNwjNcL+suAYRrRjj28jCNNUlx+JnTjQNoZZ9LKvgErOY4eQ12UEnQM3uayFhW2u520SXxmnzjmAfapi0PmY5vYWOw/4doOWBVhTxVZNy064tld4dfbk2SMqfekoQR1wjbDm2/iyV7CTBg2lKxMITBTB5vUJv9A/hzfxQzvwRf+sBXWyZHabjBdJxhKZMTiYfuoK1t4W7AixCH3tamY8o9FcPfVbVNMPaqKiNsCRV9rroYaSFbkeFovMMmr/nhmOHNqzh24SNFWNUZiqOID2ThdUPUtbCrG4T6qXOhOjbDJ0DUneMzBUYR4oRZsquY783Q1G2rRih0ozjbpVurdtR3hUAxfzQ/4/ozuztAh4Krd38d3zWjFv7WtiZu6P/j+HlOmchbeTjNfcgvFUOuqDQVd8XjYPMIrYw3aLxPasUjtlbagTuHVid67W0zlWlTpZ2hpfmooiWbYCtm0DV6377IZO1pU2qnhBNHVmhEtPKzARholaC49CooL2r9qaPUQ1AasDyZEVswte1UMXKXMsgyHwsdfd0OD4fdJfmUOlsyE67pwKVfxu2kmTfnF2+B5tQAP+fwgtTFhPpJ+urL/PdPPFTV1cfdpD/8BQtJ8hmWI1J8AAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Table with helper arrow" title="Table with helper arrow" src="/static/9ee046d3d78296546c2f0d63346cb9fd/494f9/rg_article03_image03.png" srcSet="/static/9ee046d3d78296546c2f0d63346cb9fd/63868/rg_article03_image03.png 250w,/static/9ee046d3d78296546c2f0d63346cb9fd/494f9/rg_article03_image03.png 404w" sizes="(max-width: 404px) 100vw, 404px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Table with helper arrow</figcaption>
</figure></p><p>Let’s use the illustration above and place an arrow to help us sort the elements in the right order. We need to go from the top left corner to the bottom right corner.
This will make it easier for us to create a table to define the CSS flex order property for our sticky header elements.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:196px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:214.28571428571428%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAArCAYAAAB4pah1AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHCElEQVRIx41WaWxbVRZ+EgMMUJhBICGQ4O9IjJDQaDRAKS07lCINgmEGzQw/oCBAFAoCQhNaRCnDEk2F1LK1SWnSNDRpkzRt08RO2jQbWZp4iZNmJXY2J7YTO453O/H95pxjm/glQsyVPr/P557zvXfPPfe8p/1+5wMXf/fB/bjy/fVJgvrtjnuZg69X5NyDa/M2gXxw3QebsC5vI64i+9U7NuCa3PvI7x6GIr8k21lL44Cbdz+uXjr+Ce7e9wI2fLUVzxTn4N79W/HAt6/ilt2bJfimjx7Fowe24bmSPDx7ZAfu+O9zWL//RWwueBOPH3wDd+79p+IH067L25RkZwpWWwrfUk8eehuMRw68jr9+/w5u2/Pkz4L3f/OK2Fj4z18+jy2F28ExW8v3qI1fv8yrSWr0mKJMd1R/zP+7PBWLPXX4XTx2cBtu2PUQeJ5xde4GXL/zQdye/yyt5CX86ct/4w+fP8MpUOklKxYU54dJZHPBdjxBd326KAf/Kt2JvxW/jxs/fFjmWYjzeOueLTL3QtluvHz8P/hHSS49/WOKdUSQc8jJ5o24QpK8Hpe/dzd+895duOzdv2Bd7kbZmGvJh/14+ZfnpOYZvHHrcu9TPEd+StvbdBSzi/MYcDkw7BnHmM+JiUUXRjwTGKL/0343fjAbBMwH3Q6ZcyzMwL7gpJgJXHLZFWuQltLKrQ3IHoP9A2gw1OtsnRN9guzR2tQCS48p81fxT7m1Xmknes/pHKfsE+hsbdfZuicvCbKHuasbQ3TzbEHSUtrH9QXonx1Dm8OKdocN3dODsLhGhP/o6EXvzAgOdFQJmLOtY9wG08wwepxD4kexijVIS2m76r6luw/g3MhFNI52o9luQdtEr/DzoxdlqftbywXM2ZbxaxozC6dYxRqkpbSK3vO6pQS9fvSZrDpbz9SAIHu4Jp0IkG/2kklr7abYR3/6vzZlenwSXvfcqk1pUFr+hSOYXHCh1zmCvtmfMEilMuydFG6bGYXD60RJT62AOdv6ZtjPgQG3XfwoVrEGaSkt58w+XBjtwan+Zpy+1IK64Q4YR7twhjjbOLd8UwZztvFc7VC7+J5O+SnWIC2lVdoadUvxTLswZNOXiGlqULDazz01o1syaSntmMUolmQymdoAqq/qqpNZbkA7lQkj29bV0QlzurApVqykpbR9rWVYiAQwNj+Ncd8Mpuh4TQc8wjln8yE/756AuZ1s475ZwaTfJX4Uq1iDtJS2vXovagZaWR3llnpU9jWiqv+C8DIC52hPQ6GAOdvEj3wyfvxkrEFaa3MYmF+AayU3v5hDv8cLn2tubQ65i/BYWl5KnVGTCVWVVcKXk8tybbNbBTpbaxuam5uFJ5aWRJC0lMZndJk2xB30whv2S57mQgvCPUEf4ksJnB1oEzBnm8wFvOSb8qNYxRqkpbTXq75Ahe08irrPoLi7BqUW6n1Wo3DGcTpJuwzfCZhn7OyT8aNYxZtGWmvb13I0gchi6FfbVyIcRdC3uLZ9HTXViYWXw8PhcMBsNqdzk7K1jFkE6XzJ1WA0oKamRngsERdB0lIaL1W6TCyMaCKOSDyGcDyK6FIcIbry4JbF4BGKR2QuEA4KYsQpVgR56dqrFZ9RbRlR0FmNQ12nUNRTgyPmWuGFXdUopSrIq/1awJxtPFdiqUOx6axwilWsQVrUviz6VpWIxuD3+nS2jvE+ga4OvQuIhsK6HFLRcw5rM3mQq63PhmNlZbq8NlNnZmTbqk9Vw1ifephoPJbOYa3SSs2pTVlKF2wkEkEwGNQ1jFZq94xsWzgcFqRjRbCUN+WVik/lnXuw8yQKKY9FlJcSa53wArJxFeSe/UrAvCDtx/ljX+YUq1iDtFbKJpE+esFQCG63W/fU2WWTsfEqeDXp2JWyWZ3DMfsYLjQ16fLVQvlrWZXDw0VFMBgMa3O4+iWFZBq/8pIK+gOIBEJrX1KvSR3WS319TzVVzHVIuWHONfaD1OE3AuaH0vZSqwFHrYZMvSrWeI3r8PDF03KLxWgIkQTVYCiA+UWf8EAstYsNI10C6ZdkiyzF4PHNw+2dk1NDsfKEpEVls+os9/f342T1Sd1GZZdNxmYym34+8xS7simrvxxiwQj8c95V3WZAoBvx5ex/K18O26ryUUWvAe5rR3rO4piV3hm2c8IZ3N4+NBwQMGdbCYF7IZ9t5hSrWIO0lPZde6XiZdAHo/JQB+aO7KZuzF3bFZiXXJ651CpgzjaZow/MjN/s4pxosJZ2zGxMpnsfP7ZqbGxUFSdOCF9eliOl6H0iyLZVVlYqo8EoPB0L1sp0G5VJRti7CPeEU5cuq3NYkD3mnW4EPAu6PHJN0zsl38z5o1pK0rlU1AvV0V6DYs7gA0+9UMA8Y6caVNQTU/8pljWoH3b9D/wBQAY0izhFAAAAAElFTkSuQmCC&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Table with sorted order values" title="Table with sorted order values" src="/static/66cc79b846466d7e0c90a26b233dd509/ad4c1/rg_article03_image04.png" srcSet="/static/66cc79b846466d7e0c90a26b233dd509/ad4c1/rg_article03_image04.png 196w" sizes="(max-width: 196px) 100vw, 196px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Table with sorted order values</figcaption>
</figure></p><p>We have created a solid basis to achieve the desired behavior. The panes are positioned in the right order and their overlay behavior is as expected.
Now we need to set additional required CSS values for them. For our sticky header elements we should set <strong>position: sticky</strong> and the proper <strong>top, right, left</strong> and <strong>bottom</strong> CSS values.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:452px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:126.4%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFCUlEQVQ4y2VVSW9cRRB+BxAHLpADB8QOijhwgggiJA4QcWCHCIkTIH4CIDaJCEEITiCQCHFGiYjEFUiCAAXHW2wndozt2d++L/PmzZt5s3hJ3B9VPY5FQkul7tdV011f1/fVKLsO7Fu7/dOnce+XL4ndh/fjga9ewX2HXsZDY6/R+lU8SHbHZ89i14FnyPbhNop97Nib2Pv9O3j8+NvYc/wtXou7vniefRvKzR/uhfLeo+LOz5/D7rH9uOfgi7j74AtyfT8dzHbLR09CeX8PbvrgCXAcX/LIN2/g4SOvg5PgmeLErZ88BeXdX78T306cwqFzP4qxiZM4zHZ+e962o1OncHj8BCgWx6d/xrEZMpq/nvgJR8ZPYuzvE+IonfHx2R+E0mg6KDYG6Bc9pF6MdpRioxgij1voNtso0hztsAlsbsEvmtIXuyE6SSb9/x3NXhvKBWsFldhE0E5guJY0N/LlbPoOdMcks5D1clz264haCTTbgEU+PwnRHfZQrA+wfmUDpUiHcroyDSsLwZlanUia2QlhdUdru4hh5iHqTRtnKNZsB9LHe3rm0+9s1BJKpOXjnHoJSjkyZJAXU6Cqo1qqwLNd1GgOXB9qrYFBtychGXRAp5WjulqGTzG4Kq6D7HcSKOfUi5RhACP1oEUOGoEFNbRQ9036pts9HW4WwaPg8/oirKYvfUbiwctjOqRJc4Kkl2HOXuUDL0FLXdgEO+ym0jjo2potkHujA+VeMdqTRrHs44LM2SWGrMNqh8jaGayGjiSIoFbo8T3Kuq4hdLwdSBxXEGS9rqKTZrhx8GXK2eoMbApspA7qiS2zValAKs312ILWcuUe+36vXRitaZ+LqG4br016tnFtAcqksSRTjjoEJUvgp5Gcw6yJOB/t5YMC7WGBeaeEtNtG0IqRFm101nrIhyNff2MNy4EKZcGtyOp1Ox3oNVVW0NZNWJqxY9cGZ5ET1Hq5inaz9T/I/J7Kmeo0nHaEBkFjzrFJPhLXjDyQ31rLQ5XIL3lIe04vkT4t8yQ/2acTS5gxyiwpJem3JTQ7cKVKwjSRSojS0V6SpeiRGlgpum1ibvEilkrLWK6UkPe7BHeIjSubYE5LYtt5BJeIbVKVTU1HTJWubhO7Ua2hk+USEisj9kPMzcyiRrCHvcF1kJmPym+VKegEqZ7acAcpnH4Ck+DaBMsq6KJBEypBY52epthyqGM10FDblhz3AU6Kq/1XYx7KPJGRieqR0A3LhO258EKSoWPT7MO0LRT9nhT/SqjCIMjTszNY/GcJ5VoFvWEf61c3sSW2JLUUpoLbidHv92GZFhzLRugHCINQrnmPoiWkBnEwDiMsXV6S/hsH81n5pTyB1VDDSqRJmCbBZDO61HG4wrlPsAwZ82t5Eqws+U2wK4kpv/k5OLs/63NQlqhyXHojcGBTNrZNb1Otwvep06gqBpQ5j60tgkRqcmwHU5NT0DQN2G42QogdnircIYr1vmS+LpsqyY+a6rWZVcKU6ZAaFv0aNV4bswvzqGh1tOg3PaIMK2Ztc12i4AYrWLdEXuEWTeF0Y7JkZ00MEERajhF/1GcF6VhUQ0Ma+0jb0sfvR91IKItulXgYijzPpeQcw5JmayZi7/qHt6hLh46PC5PTCN3gxpoIKT3qh2vcvnW6perpQo1saWVHFVrsCDePySJB/VKM6wuiZNXF9PJFsWLWBDVdQbLl7AS3Lvp/2vgXT7HZQCw4XCMAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="CSS styles" title="CSS styles" src="/static/c06f33b7ec7ca33e281b911d640e987e/fcb94/rg_article03_image05.png" srcSet="/static/c06f33b7ec7ca33e281b911d640e987e/63868/rg_article03_image05.png 250w,/static/c06f33b7ec7ca33e281b911d640e987e/fcb94/rg_article03_image05.png 452w" sizes="(max-width: 452px) 100vw, 452px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">CSS styles</figcaption>
</figure></p><p>Now we can set the size of the panes. First, we set the width and the height of the “scrollable-element”. Of course, it needs to be smaller than the content, to display some scrollbars.
Then, the width and the height of the “panes-wrapper” should be the sums of the widths and heights of containing panes.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:900px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:77.6%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAADCklEQVQ4y2VUS4sUVxgtiYuAkCAG9IckuAgBESX5P45BEhITCNk46KDGbEIWMsw4cZeljJOZIWahETQ4D6e761116/3qrqrunuEev+92up3Whkvd+h7nnnPPV619/O0FnPn+kvzk+mWc/u4itCuf4sTCZ/jg6nmc+uYLnPvxK3xENRrFpjnef3jtc5z94Uvuxcmvz0tt0lNrixv35S9ba7i9uYpf/36Ipe0V/PToN9zcWMbtrVXc2/4Dd7Ye4Mb6fcqt4uf137HI+80Vlbu7vaZqb20sy4U/l6AFWYwkSaQfCDRNg7E8ghk4yLMMeZFDUDxJUwgh0A6H8PIIcRyjKAsEUYiI9jH1Z0mKJ70XUjMjFx1bR8+30HUM6KGDXb+n9j41l6MaeVuhGA6Q1AV2KKf7NgzhqFje9pG1lawPh3jceSo1O/ZhUIEVuLCECzvxsS8MWL6DIE9QNH3kdYW8qZA1JfYoZ3MtrWwar0s5GDbY6Dxjhh50z1IFDGwQ4z1fh0ExkUUTBnWpGlNiyOxNYmcIW8WzSVz2CfAxA9qJoGYbJi0n8JSsXacLN/BRtgP0hzUqWvzk905owQ19OLSmOYrL4eEIW73nDOgrqXyqHwcI+6liyJLDIlEgLLtgpsRmPzBgkpKoSGc5Yi/rUUuS/5WaSwznATPsefOALPddwOOHZTNAkuylgZJrEoCIQ6R08SzZiwT6owYVNVX/S2PQA5YcsGTvPcmb3edvXWZAZirKGDtuV+3TQTEnmZnuH3N5ylxJHrf4iyVbsafAWDKfGlQJdoihTQaxi9xw/A4nY+MRoHf8Kt7eoUVjo1wmQC6M6A53nI5yfAY2B6grhjr1zDGcAuqRwwxlzzWVc3Gd45V9oFgn/XwmdXL5pTJFdy3EVUa5wWywZ6Y4iZA9z0SPBjkkwIDG5j/rtWIcD3Ia2lINND/5fVf01IcgilgdwLlkUEg2Z/2APj3+cxBhIP0woI+/RX/coOsT2yTG8HCM0XQdjdGSk2biwRc01PUA46NDlSOHJf3wj/FSvgGvv3Tj7pFd7QAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Elements size helper table" title="Elements size helper table" src="/static/4fae44102aaffb7e77abe2b43a6a6d8d/1cfc2/rg_article03_image06.png" srcSet="/static/4fae44102aaffb7e77abe2b43a6a6d8d/63868/rg_article03_image06.png 250w,/static/4fae44102aaffb7e77abe2b43a6a6d8d/0b533/rg_article03_image06.png 500w,/static/4fae44102aaffb7e77abe2b43a6a6d8d/1cfc2/rg_article03_image06.png 900w" sizes="(max-width: 900px) 100vw, 900px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Elements size helper table</figcaption>
</figure></p><p>If you have reached this step, your result might already be working as expected, but you have probably noticed that the bottom panes are covered by the top panes while scrolling.
The same behavior also happens to the left and right panes. To avoid this overlay we need to add some margins to the panes, so that the bottom and top panes, and also the left and right panes push each other away.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:62%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACkklEQVQozyWSyW4TQRRF+0fICgQSIsB3kA1ShEj4nCAhIIGvYQ0kRAoZHJLYjttt91w9VM+jx/TlVXlRKrffdO95pWwd7Dy8PNoHne7Vt308OniDx5/e4vX3D3j+9R22Pu7g2ZddEcfTz7vye/toD9uHe3hCeeKI2heH70Vdp/wY/sHJ8LI7HV3h9P4KZ/otfo8vcTK4wKUxwF+7j1/Dc5xpNxgEOo6nPZl3bY9wF0zwU73Acf8ct0zr/rExlDG3uqzMQUfecZnBTQKUdYXFeoWozsCzGHlZYL5agOUccZagnjVol3P4ZYzletWJWDVvO6XnjqDZOoZTFSNdg8Fd3FkqNEtHXGWwMh930xGswEU2K6FFNuyQgRcJ4iaHxi14cYikzsGrFMoNyQzSCC735R2UCYyYyaS4SOHkIczQRd6UyGcVjMRDvZyhnDdI2xIT7sjajOJigHJNCi0q0Bwdpu9IS1bqo1w0pKiSClVnCsOzZQNChDE58lOOpC0wZFNMHAMsCcEJjyJA+xGpcG15JzRF2HZ8hqTIJCOhTPCqFy30yJW5WV2gEt+k0GYOyraWeZKhzT2YxEgcJw1wT1PdyKfmBVlkGDMDYR5vmEVCaSFjITGbRI5kHRNDgUsyFHY0kj11TbhZiIGtwSHwxbyGmXowwk2RZEYNvWSzBNFETzZ5GcXkUm49DSEtgxHYMInk5GlgS7vCpldESOm/lhYhLBtkmYU+iqaSi7EFb7Jb0e+8JcvnVh+aZ2JgqFKJWIiwJQALCyotoTfpw4o8yfM+NBGT5aBKwGjYjaNiaI5hcga34FBGlJDmBD8MULeNnCqkr9Zr8WBlY/FMVg9rethLyVjkNrMWM3rMduyBRxwlKW4o7z8+vVoWOZDA7gAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Elements margins helper table" title="Elements margins helper table" src="/static/e4bcf2b1a64014d07ec7be6412a23716/00d43/rg_article03_image07.png" srcSet="/static/e4bcf2b1a64014d07ec7be6412a23716/63868/rg_article03_image07.png 250w,/static/e4bcf2b1a64014d07ec7be6412a23716/0b533/rg_article03_image07.png 500w,/static/e4bcf2b1a64014d07ec7be6412a23716/00d43/rg_article03_image07.png 1000w,/static/e4bcf2b1a64014d07ec7be6412a23716/35a31/rg_article03_image07.png 1028w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Elements margins helper table</figcaption>
</figure></p><p>At the beginning of the article it was mentioned that if you want to be compatible with Mozilla Firefox browser you need to set z-index CSS style to some elements. The table below shows the required z-index values.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:460px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:96.39999999999999%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAADwUlEQVQ4y3VUSY/cVBC2IPyBhBMSyQSQuCO2M+GCGDbxE7jCBRgQyzSEmQCCW0QiyPwAUA4gJBKSGUAgTSBhSJhkepm23d7bdtvd7tXd7u19VL0OTVojSip9z2/5XF9VvaccWT0xOLz6FI6uL4sHTr2AY+vLOLq2jKVTz2Np/Tmex5HcCRxe/c/vO/kMjtNeXjtGe+5fe1bcXmsrh1aehPL6I4IXHv70Zblh6fb4wY9flN93rzyOu958DIdWnoDyxqO4N/e0XD9OP33ok5dkADQv7nmLuF799jNxcnMD7/14VuS2ziG3+RU+2NqY4U8beP/Sl8iRv/3Dabz23ef48PI5vHvxLN658AWfofEZGp8RHxHHK+fXhKLHLqrtGGm7h3aUYNDqoeFHhF0kQYysk4JNCIGw08CCjcS/Izm45WtC+dMpwO/ECJsxdNuA6TnQrAqsqoNSRYUfh8gmI1RbER/AcDxCJ+uhm6VodFsS24OemE6n+EXbEcpm+SqMRhXlyIbZCmC0fJhtwqYPu1OD3vCgxQ6uuyVsqddor4f9mkX7Lag0z1iqmcJOApzf3RLKX7TRTHw0kybU4j7KhRIs3YBdMWGoOny3KjVl4yGcZrigOOv1FyTTT4Xys7YDve7CpCi1wIYaWNBCG3rNwb5XQSVyEXTrKIQGto1dymMdHA2T2w1fopUEIu418X3+N6Fsm7sy2W4SwktqqDYj6R59B+26nA8J+adX7T000jYCIuUzfiueYTsWlEdcKG4LhXPAkt0q5apYlnJ9x0Ph5p7EUr6IyWCEsZjAPSB5sCjZI8mX9v/gpKIYmbMCNFzpat2Zu0bR7VA3cAE50iLJ5zPl2JZI6RAmyf/m702h3PD2UU9bCJMYblCFH4UHPBsNpbR8UMF4OkFvOEB/lCHptCT2sr6M8Ff9+qwPWXIcx1BLJNkw4Vi2dK2swiZkS4mEo7jT6lG8IJm4hHKxdEX2oNpwYHdrMDtUtW4okV0l+dx31+w8OD0q7eVIC2FFrjHuBbqo1D18feOyULj7S1wY30HVq8JybNi2DddzJWbDTIaQ9DuykdlYNt+MKI4kjiZjGeEV86ZQiB3dYR/1pAGbyEzLgmEasIjMcV2MxuP/JWw2mwcJSbLgB6Jcd4Tdi+aSnTRCha4hS1qQTKQzyYZME+OCZG5WjciiiAh0uhm6DpMKo6kqolptXoAOPQJ86E6rheH8JWJgLiUf6NN0NEC33xNcaZaRJAlqRJam6fxwq9+VjwQby2SSwWAgcTKdSMbfrVvjfwC23Cj7DXFAYwAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="z-index values" title="z-index values" src="/static/9515572c4f98ea5bde5ca0b4539c20c1/08a84/rg_article03_image08.png" srcSet="/static/9515572c4f98ea5bde5ca0b4539c20c1/63868/rg_article03_image08.png 250w,/static/9515572c4f98ea5bde5ca0b4539c20c1/08a84/rg_article03_image08.png 460w" sizes="(max-width: 460px) 100vw, 460px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">z-index values</figcaption>
</figure></p><h2>Summary &amp; example application</h2><p>If you set all properties as shown in this article, you will be able to achieve the expected result. We also prepared a quick example which allows you to look at the whole implementation.
Feel free to use our experience and save time while trying to achieve a satisfactory outcome or try our ReactGrid.</p><p>Have fun :)</p><iframe src="https://codesandbox.io/embed/scrollable-sticky-panes-t0x86?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" title="reactgrid-chart.js-audiogram" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe></content:encoded></item><item><title><![CDATA[How to integrate ReactGrid with Chart.js?]]></title><description><![CDATA[ReactGrid is a React.js component for displaying and editing data in a spreadsheet-like way.
This guide shows you how to integrate it with…]]></description><link>https://reactgrid.com/how-to-integrate-reactgrid-with-chart.js/</link><guid isPermaLink="false">https://reactgrid.com/how-to-integrate-reactgrid-with-chart.js/</guid><dc:creator><![CDATA[Patryk Eliasz]]></dc:creator><pubDate>Mon, 11 Jan 2021 00:00:00 GMT</pubDate><content:encoded><p>ReactGrid is a React.js component for displaying and editing data in a spreadsheet-like way.
This guide shows you how to integrate it with the well-known pure Javascript library - Chart.js.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:86.80000000000001%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABYlAAAWJQFJUiTwAAADXklEQVQ4y2VSWUiUURT+K1qJoO2hl3oLigqCMtPKLIkegiAIeml/qCgrU9s3igojMgsJIVpAWtWxBYoypbGMpslCWsxmJh2bscZx9n/+ZZavc+54w+rCx3fuOd/9/vOfe5VgLIwllVsxp3wtsis2YB7jwnqC5PXIGojydX/Vsis2IufiJkwvW43TDdeg+CNBTDhSgCGlWRixLwfD987/H5QfuT8Xo/YvwMh9uSKWGFySJXLKzlnYVnMGSl8khCknVmDMwTyMO7xEYGw/jz+yFBOPFoh4GBkPocMj9uZgaGm2AOemnlmFSceXQymajaL758kwGsJkNjyUJwzGMQ4vJdN80dGg4rkYfWAhllXtQNXrOrR6vqLJYcexx1Uob6zGz7AfLV1tQrujtizzyxPplweVzKUO5olfGEwmLOCvb75zEvYfXyBWGgj1BdHu7MDrVhustld48qoRAZ8fZU03sOHWcSihWAT5lVuQe2kzVl4tRsnDClTbH8PW9RG+aED4uH+48aSlEQ+bn+KZ7QVsn96j+5cHAWrG3euFva0VDW+suP72ERTd0NFFB3w+H4J9AajhKNRQVOzbXR2wtb2D/dMHuDxdULU45Eqn0wJyOb474eh0QUkmEkinMoVkKimQSDKnkCL8u6SRMBvAvAzDgKLGVcR1DYZpIk4dsBl3YiRMaNS9ZmjQTUNozGQCGmupxjlmoeNc0oRJsbLpzglMO7saM86twUxC7t1CzK8pRH59MRZb9iDPUoSCB6VYWLcbCyyEul2E3VhE+Zy6nVhMOq5l1WzHqbfVUOrfPUdlw01cfn4bV6y1sHxrxv3vLah3vkRthxUWx0tCM2q/WQXutjdlYqrd+twg+B7lahwv8L7XASXwy4/gTz8NcMCgEv1s4s9zgZHKsJ7MMGs0ilP9uX6d0uvvhafHC3d3N0KRMNXTSNDF8DziGs/WELPkGeo09JgaEzNmjsSiYn5hOsevhfWK1+uF0+mEy+VCOBz+c5NJuhyNBAl6BXx7uq6LmHO8j8fjAiZdZiwWE8x7xePxoLOzU5hKQ/GEyFCaSEN5iGNmNuJaNBoVNf6Y0tPTA7fbjUAgIEz47ckuVFUVORZLc+Z/DWWHwpA3kUhEHOQlDVnAYt5zTYJNGbIu/0DWfgPkclDn0ZlSIwAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Audiogram app" title="Audiogram app" src="/static/553fd53faf861cf0991ae8cf2778db7f/00d43/full-view.png" srcSet="/static/553fd53faf861cf0991ae8cf2778db7f/63868/full-view.png 250w,/static/553fd53faf861cf0991ae8cf2778db7f/0b533/full-view.png 500w,/static/553fd53faf861cf0991ae8cf2778db7f/00d43/full-view.png 1000w,/static/553fd53faf861cf0991ae8cf2778db7f/aa440/full-view.png 1500w,/static/553fd53faf861cf0991ae8cf2778db7f/e8950/full-view.png 2000w,/static/553fd53faf861cf0991ae8cf2778db7f/03692/full-view.png 2440w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Audiogram app</figcaption>
</figure></p><h2>Why ReactGrid?</h2><p>There are plenty of different data tables available on the Internet, which perform great if you want to display one object per row.
Each of these objects has to have exactly the same static properties, which are mapped to columns in the table.</p><p>ReactGrid was designed to be independent of your data model.
It doesn’t care about your schema. You can render anything in any cell and thus you are able to display things the way you like it.</p><p>Unlike other grid components, ReactGrid also performs great on mobile devices or those with a touch capability and provides the same experience as on a desktop.</p><p>Before we get started let’s list three main tasks:</p><ul><li>displaying the collected data will be achieved with ReactGrid. To be <strong>reactive</strong> we will re-render the view only when the source data has changed.
In this example, raw data comes from an audiometer - a device that is used for making hearing tests.
In a nutshell, audiometer measures multiple hearing difficulties at many frequencies, and the audiogram is a way of
visualizing such disorders.</li><li>visualize the collected data on the line chart using Chart.js and its React wrapper,</li><li>add a possibility to enter a new value and rerender the whole view with an updated state.</li></ul><h2>Let’s code!</h2><p><strong>Initialize the project</strong></p><p>Nothing simpler - just type one of the commands below into your terminal to initiate a React app with Typescript support.
‘Create React App’ will take care of all the necessary stuff.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="project_init"></div><p><strong>Define useful interfaces and types</strong></p><p>First, we need to declare a few interfaces and types that help us to keep everything in the right place and order.
In this particular example, we know all about the data that we want to process.
A good idea is to ‘be as narrow’ as possible.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="interfaces.ts"></div><p><strong>Mark the columns and rows</strong></p><p>Relying on those interfaces now we can introduce <code class="language-text">getColumns</code> function.
In our app, we got a <code class="language-text">Line</code> column, and after that, we got columns which are related to a particular frequency from 0Hz to 16000Hz.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="columns.ts"></div><p>The next stage is mapping all the rows. We make it in a similar way to previous examples.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="rows.ts"></div><p><strong>Define the data</strong></p><p>As we defined our data, it’s time to define our data that we are working on.
<code class="language-text">getData</code> function returns an object whose each key must exist within the <code class="language-text">RowsMap</code> interface.
Each key of this object contains an array of <code class="language-text">Freq</code> objects.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="getData.ts"></div><p><strong>Map the data to ReactGrid</strong></p><p>Now we are ready to generate rows that directly feed into ReactGrid.
Each row contains the same amount of cells, but all of them can be arbitrarily placed in any order.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="getRows.ts"></div><p><strong>The main component - <code class="language-text">Audiogram</code></strong></p><p>It is time to create the main component - <code class="language-text">Audiogram</code> and wrap up already written code.
As you can see we stored our data inside React <code class="language-text">useState</code> hook.
ReactGrid always expects two props - <code class="language-text">columns</code> (they are constant and don’t change over time) and <code class="language-text">rows</code>
(they are calculated every time the <code class="language-text">Audiogram</code> component is rerendered).</p><div id="32c10a2f03059d8153ca300d2f11314f" file="Audiogram.ts"></div><p>All that’s left is to render the component with:</p><div id="32c10a2f03059d8153ca300d2f11314f" file="index.ts"></div><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:32.4%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAx0lEQVQY012RCQqFMAxEe/9Diisqgmjd13xeYBC+EKZt0pdJDU3TWJZlVte1dV1n933bdV2u27b5et93D9bzPNtxHJ4jlmXxIPc8j4WqqixNUxuGwdZ1NT4Kz/P0QhSYQDFGz0s5H8fR69gHXOV57tC+7x34vq93AyTlglwDphlKACWHodC2rRVF4UGnfyCFKBcZS0A5JjdN0wcsy9KSJPFgbD5gciPVG+JMT6A3ZHzlAj+DcXEnZxTQnTM5ZS+3cgUQV6wF/AFIT9BQAbCVFQAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="ReactGrid displaying the data" title="ReactGrid displaying the data" src="/static/002ba2944f82d6fa2b885d887a0db6e8/00d43/only-grid.png" srcSet="/static/002ba2944f82d6fa2b885d887a0db6e8/63868/only-grid.png 250w,/static/002ba2944f82d6fa2b885d887a0db6e8/0b533/only-grid.png 500w,/static/002ba2944f82d6fa2b885d887a0db6e8/00d43/only-grid.png 1000w,/static/002ba2944f82d6fa2b885d887a0db6e8/aa440/only-grid.png 1500w,/static/002ba2944f82d6fa2b885d887a0db6e8/e8950/only-grid.png 2000w,/static/002ba2944f82d6fa2b885d887a0db6e8/80521/only-grid.png 2416w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">ReactGrid displaying the data</figcaption>
</figure></p><h2>Apply changes with the cell editor</h2><p>There are two things left to do:</p><ol><li>Add a header row to mark the data (devices and all the frequencies);</li><li>Add possibility to edit data with ReactGrid’s cell editor;</li></ol><p><strong>Adding the header row</strong></p><p>To add it we have to create a short function called <code class="language-text">getHeaderRow</code>.
As an argument, it gets a column order (as keys of columns) and returns a row object that contains only a cell of the <code class="language-text">header</code> type.
We also added some green background to those cells.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="getHeaderRow.ts"></div><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:38.4%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABZElEQVQoz42PzU7CQBSF+0gmutMFiRENr2B8BTWgoj6AP1SIvoOxEgxBcO2aBJCwJ4RQtAtTofT/j+PcaQtGN05y8905c+bMHWGltIvV2z2ssUpJ+9ioHSLTOMPm8zEyL+dIN/LYaZyy/RFStSy2WU9M1/Pcs1U/4bVePcBdrwohV7lBtiwix0psSij2nvhBof2IUrcCsVOO+FbGdVuCyPSr1gOKTLto3vPzQkvCZUfC67gLwficAnMsVxgziJmc+XEfxj0tOza7y+vCaCzjXfmAOlHhzwPYrgMv8KFbBmzPgWGbES2TF+kTbcp9RNOx8DWdwPFcWLbFAkcjDIdDqKoaDTSPRrJtG77vw3GcPzQMg/emacLzPOi6zveWxQIpbDAY/CvQdV2uz2YzHkQkjRgEQRTY7/chyzIXaZExDMPFFEnwTyaBmqbxQOJiQkVRuCmZLjHTN+hVupCQdLr4+8vExPMNUVo+ZjtqS08AAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="ReactGrid with a header row" title="ReactGrid with a header row" src="/static/980e0a737657ffab21b447ca9d1726f2/00d43/reactgrid-with-header.png" srcSet="/static/980e0a737657ffab21b447ca9d1726f2/63868/reactgrid-with-header.png 250w,/static/980e0a737657ffab21b447ca9d1726f2/0b533/reactgrid-with-header.png 500w,/static/980e0a737657ffab21b447ca9d1726f2/00d43/reactgrid-with-header.png 1000w,/static/980e0a737657ffab21b447ca9d1726f2/aa440/reactgrid-with-header.png 1500w,/static/980e0a737657ffab21b447ca9d1726f2/e8950/reactgrid-with-header.png 2000w,/static/980e0a737657ffab21b447ca9d1726f2/63908/reactgrid-with-header.png 2410w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">ReactGrid with a header row</figcaption>
</figure></p><p><strong>Editing frequency values in cell editor</strong></p><p>At this moment ReactGrid behaves as a read-only.
To change that we updated the <code class="language-text">Audiogram</code> component by adding our handler function called <code class="language-text">handleChanges</code>.
We expect that only <code class="language-text">NumberCell</code> will be changed, therefore we marked the <code class="language-text">changes</code> argument as <code class="language-text">CellChange&lt;NumberCell&gt;[]</code>.
Our task is to change data on the basis ReactGrid has been rendered.</p><p>Cell editor opens when it receives double-click action or the Enter key is pressed.
Then you can type a new value in and then commit the change.
If we <code class="language-text">console.log(changes)</code> we get an array of objects as shown below:</p><div id="32c10a2f03059d8153ca300d2f11314f" file="changes.json"></div><p>To change our raw data we have to find <code class="language-text">rowId</code> where the change takes place.
Then loop over all frequency samples and apply a new value (<code class="language-text">change.newCell.value</code>) to an appropriate cell or just return without changes.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="AudiogramWithLineChart.ts"></div><h2>Data visualization with Chart.js</h2><p>Chart.js library delivers plenty of components to visualize data, but this time we focus on a single one -
<code class="language-text">Line</code> from <code class="language-text">react-chartjs-2</code> that we can use as a React component.</p><p>We have to create two functions:</p><ol><li><p><code class="language-text">getChartData</code> - this function should return an object that contains two fields. The <code class="language-text">labels</code> - which is an array of frequency title label
and then <code class="language-text">datasets</code> field to provide the <code class="language-text">data</code> field which contains an array of values for each frequency.
You can also style your line by setting for example a <code class="language-text">backgroundColor</code> or <code class="language-text">pointRadius</code> for a better experience.</p><div id="32c10a2f03059d8153ca300d2f11314f" file="getChartData.ts"></div></li><li><p><code class="language-text">getChartOptions</code> - here we return an object that is compatible with <code class="language-text">ChartOptions</code> interface.
We want to disable legend, set the title, display, and adjust axes.</p></li></ol><p>That’s all! The job is done, now you can check the result below.</p><iframe src="https://codesandbox.io/embed/reactgrid-chartjs-audiogram-gtlgr?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2FAudiogram.tsx&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" title="reactgrid-chart.js-audiogram" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe><h2>Summary</h2><p>What you learned after completing this guide:</p><ul><li>what is ReactGrid and how to do a fully functional app; </li><li>how you can use it in a reactive way;</li><li>why Typescript is also helpful in a small-scale project to avoid the most common mistakes.</li></ul><p>As you see integrating ReactGrid with other libraries like Chart.js is not so hard.
Of course, you don’t need to start a Typescript project and make all data mappings to compose a predictable solution.</p></content:encoded></item><item><title><![CDATA[What is ReactGrid and why is it unique?]]></title><description><![CDATA[ReactGrid is a React component which enables you to add spreadsheet-like behavior to your app. It was created to satisfy a narrow group of…]]></description><link>https://reactgrid.com/what-is-reactgrid-and-why-is-it-unique/</link><guid isPermaLink="false">https://reactgrid.com/what-is-reactgrid-and-why-is-it-unique/</guid><dc:creator><![CDATA[Patryk Eliasz]]></dc:creator><pubDate>Thu, 29 Oct 2020 00:00:00 GMT</pubDate><content:encoded><p>ReactGrid is a React component which enables you to add spreadsheet-like behavior to your app. It was created to satisfy a narrow group of recipients for whom other products are not an appropriate solution.</p><p>On the one hand there are data tables like Handsontable or ag-Grid. These render records row by row and offer various filtering, sorting and grouping methods. In 90% of the cases this functionality is fully sufficient.</p><p>Then there are Spreadsheet web components like KendoUI Spreadsheet or dhtmlx Spreadsheet which display regular Excel sheets in the browser. They are able to interpret formulas and offer rich editing features for the end-user.</p><p>ReactGrid places itself exactly in the middle between the two. It is not limited to a record-based model where each row has to have the same schema. The component enables you to create tables of any shape that look and feel like Excel-sheets. Additionally it integrates well with the data handling model provided by React.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:60.4%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAACmUlEQVQozz1SO2sVQRRei3RBBbu0AbWz1M5CEBSJBsRI9sIlWU0juRGCICQ2VtpoShUJJthaBMEi/oUEJdEoYjS5j9zH7ux77z5m7hy/syYZOMw3M995zPmOkef5qOd5M7DpKIostsFgYBGRVV1dtIz7Z60TDy5YQ/MXreHFy9bQo0vWqaVr1sj7u9bJt2PWmeVxa2R1Yvr08q2Z4Tc3Ro0kSar1ep329/ep0WhQu92moiiINNGLzyt05eU9uro0Q2OvH9Kdd4/p+qtZuv1hkarrz2hsbYEmPj2lqfXnNP7xCd1cW6gaSikzDEOybbvodrsqjmOltVaoUqVpqlzXVcIRKu2nSgih7J59jB3gDJwgCArHdqgfJ6aBr5l5kVOr1eIH1EWaFwLqPMt0GITaFUIjuEZiLQ5xFEbHOIkTTkwoxiwrRBCKo1j1ej3tez7hjq8oyzImEZPhWGKPcT+lfpIcYY22KWhA2E1j2/5T2RF7BFObzR/6S/sXfXf+0lfsW93fxOeN5g5t9XYJXNpo7NA28Ddg8BlrYLXZ+sk+FUP0g4qbhuTnsTrwenq3XaeW6NBep0l27FHbt6luH5CT+ORlUYnhU+ImeMAaWDHuhqJi0ECbrCiW4r7wl49WkeWU4XuhHxBk4u6SJ9xyAth81ys7DqwCblUh//cQAvCoKMdxNAsDLvyVTiEK5lK7nqvzotDoqWZOIaXG/JaiHGJWnXtuGgg2yQHhmONSIrBEfok7CYKEshINLzFEkUgoEUCCL33fLzEEy3Fm4Sa5wimumlen0yFWixcnYZWZyNlZeVaaOfyGQDy75T2fDydhypBSnsflPPYaxmYOpDmcSwNhDqNQQ5IaKi8NjjXmImeN3xgj8SxaMY/3c/8AxxlHz31kOccAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Adjust your grid view and behaviour to your needs with ReactGrid" title="Adjust your grid view and behaviour to your needs with ReactGrid" src="/static/99d30443a8b291bd852299c55e030400/00d43/ReactGrid-illustration05.png" srcSet="/static/99d30443a8b291bd852299c55e030400/63868/ReactGrid-illustration05.png 250w,/static/99d30443a8b291bd852299c55e030400/0b533/ReactGrid-illustration05.png 500w,/static/99d30443a8b291bd852299c55e030400/00d43/ReactGrid-illustration05.png 1000w,/static/99d30443a8b291bd852299c55e030400/aa440/ReactGrid-illustration05.png 1500w,/static/99d30443a8b291bd852299c55e030400/29114/ReactGrid-illustration05.png 1920w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Adjust your grid view and behaviour to your needs with ReactGrid</figcaption>
</figure></p><h1>When do you need ReactGrid in your app?</h1><p>ReactGrid was created to solve the issues in which we, web developers, weren’t satisfied with the existing spreadsheet or data grid solutions. You must have wanted to present your data in a rather unusual way more than once, for example:</p><ul><li>create a field that aggregates values only from selected places;</li><li>create an unusual combination of action and reaction;</li><li>display unstructured data, e.g. MongoDB documents;</li><li>sort or group data with an unusual structure together;</li><li>react to data changes in a way that is fully controlled by you;</li><li>transfer the solution implemented in the spreadsheet to a closed application.</li></ul><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:50.8%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAACqklEQVQoz01SS2gTYRCebXoRUxXEFyKC4KEIeutFjx6qoCcP9lCsBw89qAVBKeITPKhY8FRQRFtrKxalQo0UqaWNtaZ5NM/G3WRf2c3uptk2j9rWJJsdZ2MP/eHjm/lm/v+f+ecH2FxpTQJekxmCw27CfdI+El/n9YbmwLUlz8XrEu2TIa1LTcSMYwMiwpOpQeZzbNrV5x1m3gY8FJQmCLgF49NcoFnLG418dUkDyVAB/1oQzbDunLkEf0pl+BSedIEnOcu0v7wG3WOPwRP3grlitkmGgqKRqVJlVbKrIvlyTp0qFYutdKBbzetHRD3TQ7EZ4pSkKx+q5fW9Q/4vAG8C43D2Vc+eZ9NDJ7pGHx76nvT1hqRFpJvrKU20g2ICya8tkBYSExW/EGN/q/wamxUworAYUzmMqykMyImxK6OPDgMch21BJRkxSiZmi0sbRtm0MgUDvVzQ/pWO2KmcbOvFvBOz8qsFjGZTOJcO48yivxJOJapxka0tcPENQcvgN843BRPs3E3LslCTVVtKC2itVVAvm/bP1AKyumibWs42VM1WRBl1RbMjGdby8dG6UcijJqkYCYWRT3JoFpZxMOQ5B5Pc/GRudUWWV/R1hSqTljV7Xog5VdgJXahrpbxFMZsqxEVDsKllnKXLtNW8QJ1wcZllxXxW9amJEWdgAGfA3dbXedCXjqgBPobzfLTKZcUavVE9IMTR0Tb1WlBIVAQ9g0Eh/px2NkPXvl1wu3UHXGrZST4DF3cTdbihY6AXMrnsuDNdKaciTbcBmmya9B/EZUd34vSFFKpk++V39/5/4DvHALr3N8zT/d0AI/6vTdW1DUiq6QOU3E/wEJ4STkXkZIvTBpvlj5I/RogTTuqmAZ6Yt/n86xtM1/sHTOfwXab9xVW4MHAL/gFfSEL7ZibZVwAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Three ReactGrid fundamentals" title="Three ReactGrid fundamentals" src="/static/3d5c884c74c7a0c0bd52c1baddb97a3a/00d43/ReactGrid-illustration01.png" srcSet="/static/3d5c884c74c7a0c0bd52c1baddb97a3a/63868/ReactGrid-illustration01.png 250w,/static/3d5c884c74c7a0c0bd52c1baddb97a3a/0b533/ReactGrid-illustration01.png 500w,/static/3d5c884c74c7a0c0bd52c1baddb97a3a/00d43/ReactGrid-illustration01.png 1000w,/static/3d5c884c74c7a0c0bd52c1baddb97a3a/7388e/ReactGrid-illustration01.png 1104w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Three ReactGrid fundamentals</figcaption>
</figure></p><h2>Reactivity with arbitrary cell placement</h2><p>To show the difference, we created a simple graph with two key aspects contained on the intersecting axes:</p><ul><li>vertical — reactivity — a concept taken directly from React.js library. The opposite is imperative, you have full control over actions and their influence on the current view. In most cases you should follow the following pattern:</li></ul><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:68.39999999999999%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAADtUlEQVQ4y4VUC0zbZRC/tk5KWaGP/4CCiNnmNFNM0ICpDqEDJ8sSN90STUg0+Ag6Hdk0TMdjuojEGd3iYw+ZiRkZWUUtzjGJj0AgdnallErti3/pu7RllLXdgtDH//y+DhNjlnjJL3e5L/e7++67+wD+JXw+P6vVajUgYvkb33701tunerYS120Eecw9ZcX5b27Z+hUi8Avz4P+ErygtYYjO0fww2DRq1C6dnxhGg3kq8/mZU2OKspI6ciYvbWuoyX9dWUHsXJ5ojYxo2WrCm3Lgi/d4VK8rLqRkwi/VZ+VWt8NvclqwTz+U1E0b8MJPQ9iv+dq8vaquFFqKKkra6jdo+M8LmAfXC0F8u5CXI2CaSdXilmoe7Ok/BDs/a6Up5H2jg5BKJde7I/4l73wQR+16zsCaOYvLkbR6ZpAksXZdOtl0bux7GNaNwvbulwBe2EzrYfL2VoPkSD2A8pNmWd3pV5jK13aUwxOwtk938VGLn0055zzojQQ4d9jPkQToCvvSBNS3PBvy/mp0W1r3fffhvbn7H5ZLVJvuK+xuZCQdKgYmA/bFQCySCFy/GvPHI3HvYujGODuJhJD7D6gv7Qi6cG4hjJ1DJ1B0aMuKuP2x+NqO2mvi9tpEQUddHBaXEkjFOcNiLHota5v8Do4GkkooCdUcBbVngm4uHJ1Pv6zuThECVH7ajFXHn8PKY00o76pHcEWDuJxawauxaHrxeizzV2qF07nNHAlEZ+hmdavE1M6QRJlwNJJ+daAnDQcqk/LDDUnFkUYsfrcRJZ0qhONj/bt6dYNPj1h1lt+dJtSyU5krs9NZEnfYx7mIZoPuDEsTkApJT7MYsV3B0+Pf4JnfNHj44gntmoPK3YRwD/xi10HiRqJaa9ChM+BG33yQ80QC6In4OdJ4tATYTCAaQt/CHDcb8dm1M0YrOR8PLoS18UR8IL280qJpP1nywAfPAK+tGoA4gVTTa7Ca0OJ2pGxeFm0elrP5WDw3cSmttxhR8+OFaM/HRw8ywgKF6J3amvwXq4rIqAhIu+lGZcdm20inQLDvIQF45gNAxqHXRuZMbzYuT9r+wCnbNF62GlIDxp9x8k8T7tz91F4SdD/dCunRxs25z1bUELuAKVfINty9sUgqlebJ5DJg1pHdIL0B0ptH6IzReSPIEND5w/P6YfLi9v0kWFJWfudGWoq0Q/X4Hcd2iWCTEG71D1BC3ippA8HEP6/pCvkuG1zmJ1evlN1WcatSIOlSiaTvb4Ocmrt4t/oQ/gYZqGATCBFd/wAAAABJRU5ErkJggg==&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Reactivity in ReactGrid" title="Reactivity in ReactGrid" src="/static/32f23df84f4fac4e24084350d6d54a8a/00d43/ReactGrid-illustration02.png" srcSet="/static/32f23df84f4fac4e24084350d6d54a8a/63868/ReactGrid-illustration02.png 250w,/static/32f23df84f4fac4e24084350d6d54a8a/0b533/ReactGrid-illustration02.png 500w,/static/32f23df84f4fac4e24084350d6d54a8a/00d43/ReactGrid-illustration02.png 1000w,/static/32f23df84f4fac4e24084350d6d54a8a/12bff/ReactGrid-illustration02.png 1032w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Reactivity in ReactGrid</figcaption>
</figure></p><ul><li>horizontal — arbitrary cell placement — opportunity to “Thinking in rows and columns”, each row has the same schema. Our component allows you to place any cell anywhere. We have focused on a fully controlled cell schema defined by our cell template engine. Templating is a powerful feature that allows you to define cell behavior based on its current and future data state.</li></ul><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:80.80000000000001%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAADzElEQVQ4y31UDUxTVxT+Xl9LS0tpoS1I2g5BJ3FjZiEYkjm2zMwfsATjTAzqYuYCy7ZsYcZNli2ECTg3o8WRquhmSPYTMWaBCXQJEehCoOlCbGlpobTqVpFGbCBbhu2Avp1bi5mZ8yZf7r3vnvPd755z3gH+Z2i1Wo4AlUqlz87OfiMrK+ugTqdbd/ZCG/6Ymy+cnp3pisxFyoTY0jOLC9ENld8cAnaJgN2nAtjXGkzMr1tucFsaJ7itTZPcs1vfEatTgMzMzBf1er2L4NZotbs+av4UF365cnjIPyrYAy6hb3x48ZrXvnSq/9sDgiAA5cf92HlyCqYv/dhO6+K6cWz8mFDnQemJBeTmr2OC0xhWqbXASVq9/1z+K5a39lz61Trf5ewXul024aj1/BcPCSvMN1B+IiAvb/YaX6odMRYfvm4o+tBpLKlzrWJsqvQ0RUZGJiPF81WbOexVob7nLO7cC3eE7k4Ld2bDHezsk24LUH3uJv/BxVs4aAlWffZjeHH/1zPhN9tCkeq236N7TwfHi1oEGdmmimXpypX4vn35GMfU0FPl3tBUIVszkFIOWfnFPNtsOu7OPlL/c6X5tYbayncv7dx86GqF6dh4SZJDwYn4tBXCWCyGUb8b3pt+LJOvRqflN71cKqYjEcRiScJot3E9KAJ5Yan4ux1AKX3K2+8TIMnIBTgRI1P8uwr27KtCy5lWbmUvlUohl8sBnue3pMpk23lZakWhNqdodE1hwbXvvWK/IHDvHe2QJO3Tkng4RCImRgyO44wSicSUkpKyTaFQbINGoxnLzc2dpJoLkV11IlMAFQwYmZSgJieSiacIGaxEGYhQR7OSVB3IycmZprKaoHqdSNxms9kwPDxcTvDZBgacIyMj161Wq8/j8VjpAkaemp6e/ohCpm7lqWVlZWhqakJNTQ0wMzMjcrvdbC4OBAJnfD6fORgMmr1er8XlctUTIU9+JET+WEK1Wv3f36yhoYEjQgwNDYGUJRT39PTA4XDAZDIx50di+FvMj1vRSVY63FRgiuvs7BS1t7c/SJDT6cTY2BhIJWe32/nBwUGezX19fXxvby9TxwKvpJgppXIpjphr4f7bDvd9B4S4gLgQT9Tg8vIykvF//KAAQ6lUcpS5RFIfgEs4BZY8cn/UvZbWa5JYG4/H9U8kpKYA6jCgZBgIO6jrmIyrDU9fdHyFgfmfXnUs9M/d/zN6O3Q7FIpEInN3Z2e78KRBZDy1K9a+Gg0Gwz1ChBLQVrC+AOd+sMj/EiLa7qu9qxubP9/QcrqVIW9jyQv4B+B+mxpgcAdEAAAAAElFTkSuQmCC&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="ReactGrid compared with another spreadsheet/datagrid components" title="ReactGrid compared with another spreadsheet/datagrid components" src="/static/0e708e7a4d3ff1eb0e88922252f8c691/00d43/ReactGrid-illustration03.png" srcSet="/static/0e708e7a4d3ff1eb0e88922252f8c691/63868/ReactGrid-illustration03.png 250w,/static/0e708e7a4d3ff1eb0e88922252f8c691/0b533/ReactGrid-illustration03.png 500w,/static/0e708e7a4d3ff1eb0e88922252f8c691/00d43/ReactGrid-illustration03.png 1000w,/static/0e708e7a4d3ff1eb0e88922252f8c691/7a1fb/ReactGrid-illustration03.png 1030w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">ReactGrid compared with another spreadsheet/datagrid components</figcaption>
</figure></p><p>We want to be (and we are) more reactive and cell-oriented. Breaking with the approach known from such alternatives as agGrid, Handsontable, it is necessary to handle events through self-implemented callbacks. Implementing a basic cell change event relies on your implementation. We prepared examples where you can just copy and paste predefined implementation.</p><p>ReactGrid’s content is rerendered only in case when visible data has changed or additional conditions such as a changed number of sticky rows and columns have appeared. Other examples might include handling scrolling (continuously following visible range for virtual scrolling), focusing cell, handling user actions like copying and pasting data. ReactGrid works perfectly even if you display a huge amount of cells — 10 000, 20 000, 100 000 is not a big deal.</p><p><figure class="gatsby-resp-image-figure">
<span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px">
<span class="gatsby-resp-image-background-image" style="padding-bottom:62.8%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC7klEQVQ4y1VTy2oUQRQtJugn+RWCCzfuFVwZFNy48IGgogsRURE0USQgKIrBuIoaxWh8TCYz093T08/qd/e8Jw81sZI53jsZEBeHququOnXuPadEtt6Zkp3koMwj4WbBrR+ehhWnqr5YlVEjsOGkEktfP+Hlm3k0pAUr9mCGDn8feXmo6AxoPE7g81Oiv70pOr+Hwsw84WXBYzcPUHMNtWZpMPwm7MTH3POnuHTlMqq2Dr7E8C1UzDrcNFBEBCI64xeRIEyJcmKe+Brp0y+094fs1L9bj23osa20xBkZiQNZRIj7BfKNDmQrhkOKajERevpoLWioRuIibKfTVuAKQ1pTol200O114RThwuuVxZvFsIt23lK9fh92K+Dbx2WzUqoAVibh5xGcpo0ojZUWWni7vHQOgCAcEI3cv65l7r13zvcjRmTf0UkVbVIaKWXwYS6L4RNsIiRlWLXrqPiGMlMPmtM4xYQ/ymUhJswi6ebUw3DWb0WwEk9xL2kNagNB/gfq1ZiYxr1a2MQ3q/r5/dqX27XIus9kpZ2dnQNBJ+GmzrCLz+ZfqMXlJZB6yE4Kv5OMwXOnFaIWmHi58IqMqcHM/L0oiiClxGAwgDATl+0uFZtdEbSTGWoszl++qB7MzSLb6oIuQ5wl8AIf/UEfv3a3serpuHrjGj5+W961ColyaDz6nGqHP/irx4RL+aHSStR4QZhhJ0mpYjcbhY/e5gCma0G3TQRJhHS9jTq57KXBeB8b1Yzdk/2f6+PWCQ6klwelSTBnuPkNaasK5VDPXHTX+3ACD4bTRJKnSIhwxaygbhucADUx7LQR2qI97B1kMiIKSwQmf8gxIWdJoRxVoyb8mNYRGRRJKltCo7Cz+7KIR0w4eSnTNO6/lH0iVjj+8GSyQVmUu3pqY/B7A72fQ3S2BhhubyEc5KiGJuipssI/45eSh2f3Kw2Z8J9Cml+YEO4SRnWKRFnqWJXGGDyv0GhRlPg/7d3j/YSjE4Wlv+SCYwmq1+gJAAAAAElFTkSuQmCC&#x27;);background-size:cover;display:block"></span>
<img class="gatsby-resp-image-image" alt="Sample component lifecycle that uses ReactGrid" title="Sample component lifecycle that uses ReactGrid" src="/static/d26e44179e3ec744ae5f14d9283711cc/00d43/ReactGrid-illustration04.png" srcSet="/static/d26e44179e3ec744ae5f14d9283711cc/63868/ReactGrid-illustration04.png 250w,/static/d26e44179e3ec744ae5f14d9283711cc/0b533/ReactGrid-illustration04.png 500w,/static/d26e44179e3ec744ae5f14d9283711cc/00d43/ReactGrid-illustration04.png 1000w,/static/d26e44179e3ec744ae5f14d9283711cc/aa440/ReactGrid-illustration04.png 1500w,/static/d26e44179e3ec744ae5f14d9283711cc/2ed34/ReactGrid-illustration04.png 1776w" sizes="(max-width: 1000px) 100vw, 1000px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy"/>
</span>
<figcaption class="gatsby-resp-image-figcaption">Sample component lifecycle that uses ReactGrid</figcaption>
</figure></p><p>Let’s talk about a simplified lifecycle based on ReactGrid. The component containing ReactGrid (AppComponent) has its data (it is a Single Point Of Truth for ReactGrid), which contains data on the basis of which the grid view will be generated.</p><p>The rendered view is ready to handle events coming from the user, e.g. changes committed in the cell editor. However, the component still behaves as read-only because a data update is required to change its contents. We can do this, for example, by implementing the onCellsChanged function (the example comes from the ReactGrid docs).</p><iframe src="https://codesandbox.io/embed/reactgrid-handling-changes-crzfx?fontsize=14&amp;hidenavigation=1&amp;theme=dark" style="width:100%;height:500px;border:0;border-radius:4px;overflow:hidden" title="reactgrid-handling-changes" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe><h2>Touch devices friendly</h2><p>ReactGrid works perfectly with modern web browsers. The same goes for their mobile counterparts along with touchscreens. With ReactGrid, a mobile-friendly spreadsheet-like component, you can use your app in the same fashion and experience the same productivity as on a desktop device. Let’s have a look at the basic mobile usage case — cell selection, fill handle… it just works on ReactGrid.</p><p><img src="/99cfec32a109683006802e80bc14a110/reactgrid-comparison.gif" alt="ReactGrid compared with agGrid and Handsontable"/></p><h2>ReactGrid is NOT…</h2><p>You may wonder why our product “does not have” many popular functionalities, such as grouping, sorting, filtering? This is not really a disadvantage, but a feature! By getting them, you immediately agree to the restrictions imposed by the selected library. Here you manage how you sort your data and how the user can do it. Nothing prevents any cell from being, for example, a filter.</p><p>What about formulas, toolbar, and coordinates? ReactGrid is a component in which we consciously implemented only the appearance and behavior known from typical spreadsheets, but without the implementation of the outer envelope. Our purpose was not to create the next Excel, but create whatever you like around it using component API.</p><h2>Summary</h2><p>In this short article, we showed three main principles which we developed ReactGrid with:</p><ul><li>reactivity — handle every event and process data in your way;</li><li>arbitrary cell placement — forget about row schema and focus on cell,</li><li>mobile-friendly — the same experience no matter what device you use.</li></ul><p>ReactGrid gives you the freedom to display and interact with your data to create a custom view with non-row-oriented data. We use it in our projects as a tool — not a complete solution. Therefore our experience shows that every solution resolved by ReactGrid is unique. We encouraging you to browse our website and Github repo.</p></content:encoded></item></channel></rss>