Building a Smart Data Table in React

Chelsea Devereaux - Nov 6 '23 - - Dev Community

Tables are one of the easiest ways to present data on web pages. Plain HTML tables can meet the basic data presentation needs, but trying to squeeze anything sophisticated from them will quickly make you frustrated.

This is when you need to make some important decisions about your application development path. You can start coding enhancements around old-fashioned HTML tables — at the risk of losing focus on your project — or you look at the existing products that are designed to solve your problems.

Today’s market offers dedicated smart table controls that handle the table part for you and provide additional features, including selection modes, sorting, and column reordering.

Many companies use data tables to show complex reports, dashboards, financial data, sales results, and even interactive spreadsheets. High demand begot specialized supply, so tables are well represented in web component libraries.

In this article, we'll discuss which capabilities are expected from a smart table, assess a few open-source alternatives, and demonstrate how to create superior HTML smart data tables with our solution for the React JavaScript library. We are assuming that you have some web development experience and preferably familiarity with React.

To see the finished code sample, you can find it running here on StackBlitz.

Table Capabilities

There are some key features that people expect from a smart data table.

  • Data features:

    • Load data from various sources, including local files, databases, and APIs
    • Searching, filtering, and sorting options
    • Load and display of large datasets with pagination
    • Ability to import and export data
  • UI features:

    • Showing and hiding columns
    • Inline editing
    • Responsiveness — built-in support for multiple devices
    • Resizable columns to accommodate long data points inside a column (multi-line comments)
    • Horizontal and vertical scroll support
    • Data validations and visualizations, such as sparklines

Modern frameworks have built-in implementations of some of these features. However, you have to code more advanced features yourself.

Open Source Frameworks

Created and maintained by Facebook, React is a massively popular open-source JavaScript library. Currently, it holds around 60% of the JavaScript framework market share. With such widespread adoption in the industry, it’s no wonder that many products have been created to serve applications built on React.

Here are some popular open-source smart data table libraries made for React:

These open-source projects have advantages and disadvantages. Depending on which feature you are looking for, some of these components are better than others.

Create a React App with React Data Grid

Let’s build a new Node-based, React application with React Data Grid, an open-source component that has a look-and-feel close to what we are trying to demonstrate.

Install Node.js. Then, using a command-line console, create a new Node React app:

    npx create-react-app my-app  
    cd my-app  
    npm start
Enter fullscreen mode Exit fullscreen mode

Install the React Data Grid package. Open the package.json file and add these lines in the dependencies section:

    "babel-loader": "^8.1.0",  
    "react-data-grid": "5.0.1",  
    "react-data-grid-addons": "5.0.4",  
    "bootstrap": "^4.5.2"
Enter fullscreen mode Exit fullscreen mode

append the following lines after the browserslist section:

    "devDependencies": {  
      "immutable": "^4.0.0-rc.12"  
    }
Enter fullscreen mode Exit fullscreen mode

To install the React Data Grid package and its dependencies, run the npm install command-line instruction:

    npm install
Enter fullscreen mode Exit fullscreen mode

Add a new data folder under the src folder, and create a new file named data.js. The user will use the data to populate our data grid:

    export const recentSales = [  
      {  
        id: 1,  
        country: "Canada",  
        soldBy: "Bill",  
        client: "Cerberus Corp.",  
        description: "Prothean artifacts",  
        value: 6250,  
        itemCount: 50  
      },  
      {  
        id: 2,  
        country: "Canada",  
        soldBy: "Bill",  
        client: "Strickland Propane",  
        description: "Propane and propane accessories",  
        value: 2265,  
        itemCount: 20  
      },  
      {  
        id: 3,  
        country: "USA",  
        soldBy: "Ted",  
        client: "Dunder Mifflin",  
        description: "Assorted paper-making supplies",  
        value: 4700,  
        itemCount: 10  
      },  
      {  
        id: 4,  
        country: "USA",  
        soldBy: "Ted",  
        client: "Utopia Planitia Shipyards",  
        description: "Dilithium, duranium, assorted shipbuilding supplies",  
        value: 21750,  
        itemCount: 250  
      },  
      {  
        id: 5,  
        country: "USA",  
        soldBy: "Ted",  
        client: "Glengarry Estates",  
        description: "Desks, phones, coffee, steak knives, and one Cadillac",  
        value: 5000,  
        itemCount: 5  
      },  
      {  
        id: 6,  
        country: "Germany",  
        soldBy: "Angela",  
        client: "Wayne Enterprises",  
        description: "Suit armor and run-flat tires",  
        value: 35000,  
        itemCount: 25  
      },  
      {  
        id: 7,  
        country: "Germany",  
        soldBy: "Angela",  
        client: "Stark Industries",  
        description: "Armor and rocket fuel",  
        value: 25000,  
        itemCount: 10  
      },  
      {  
        id: 8,  
        country: "Germany",  
        soldBy: "Angela",  
        client: "Nakatomi Trading Corp.",  
        description: "Fire extinguishers and replacement windows",  
        value: 15000,  
        itemCount: 50  
      },  
      {  
        id: 9,  
        country: "UK",  
        soldBy: "Jill",  
        client: "Spaceley Sprockets",  
        description: "Anti-gravity propulsion units",  
        value: 25250,  
        itemCount: 50  
      },  
      {  
        id: 10,  
        country: "UK",  
        soldBy: "Jill",  
        client: "General Products",  
        description: "Ion engines",  
        value: 33200,  
        itemCount: 40  
      }  
    ];
Enter fullscreen mode Exit fullscreen mode

Add a new components folder under the src folder, and create a new file named OpenSourceTable-Demo.js:

    /my-app  
      /src  
        /components  
          OpenSourceTable-Demo.js
Enter fullscreen mode Exit fullscreen mode

Open the OpenSourceTable-Demo.js file and add the following code. (Here, we import the ReactDataGrid component and declare the React state variables that will be used by the data grid as column definitions and the component’s data source):

    import 'bootstrap/dist/css/bootstrap.min.css';  
    import React, { useState } from 'react';  
    import ReactDOM from "react-dom";  
    import ReactDataGrid from "react-data-grid";  
    import { recentSales } from "../data/data";

    export const OpenSourceTableDemo = () => {

      const [columns, setColumns] = new useState([  
        { key: "id", name: "Id" },  
        { key: "country", name: "Country" },  
        { key: "soldBy", name: "Sold by" },  
        { key: "client", name: "Client" },  
        { key: "description", name: "Description" },  
        { key: "value", name: "Value" },  
        { key: "itemCount", name: "Item Count" }  
      ]);

      const [sales, setSales] = new useState(recentSales);  
    }
Enter fullscreen mode Exit fullscreen mode

Modify the OpenSourceTableDemo component so that it returns the HTML with the ReactDataGrid component:

    return (  
      <div className="card main-panel">  
        <div className="card-header">  
          <h1>Open Source</h1>  
        </div>  
        <div className="card-body">  
        <h5>React Data Grid Demo</h5>  
          <p>  
            Building a Smart Data Table in React with React Data Grid  
          </p>           
          <div className="container-fluid">  
            <div className="row">  
                <ReactDataGrid  
                    columns={columns}  
                    rowGetter={i => sales[i]}  
                    rowsCount={recentSales.length}  
                  />  
            </div>  
          </div>  
        </div>  
      </div>);
Enter fullscreen mode Exit fullscreen mode

Edit the App.js file and replace its contents with the lines below:

    import React from 'react';  
    import './App.css';  
    import { OpenSourceTableDemo } from './components/OpenSourceTable-Demo.js'

    function App() {  
      return (  
        <OpenSourceTableDemo/>  
      );  
    }

    export default App;
Enter fullscreen mode Exit fullscreen mode

Now run the app:

    npm start
Enter fullscreen mode Exit fullscreen mode

Once the app is running, you’ll see the React Data Grid displaying the sales data:

Smart Data Table React

To show some of the component features, let’s start customizing our open-source data grid to support cell value updates, row sorting, and column reordering.

Implement Cell Value Update

Open the OpenSourceTable-Demo.js file and add the editable: true attribute to each of the grid column definitions:

    { key: "id", name: "Id", editable: true },  
    { key: "country", name: "Country", editable: true },  
    { key: "soldBy", name: "Sold by", editable: true },  
    { key: "client", name: "Client", editable: true },  
    { key: "description", name: "Description", editable: true },  
    { key: "value", name: "Value", editable: true },  
    { key: "itemCount", name: "Item Count", editable: true }
Enter fullscreen mode Exit fullscreen mode

Add a new onGridRowsUpdated function to handle cell updates and update the underlying data source:

      const onGridRowsUpdated = ({ fromRow, toRow, updated }) => {  
        const s = sales.slice();  
        for (let i = fromRow; i <= toRow; i++) {  
          s[i] = { ...s[i], ...updated };  
        }  
        setSales(s);  
      };
Enter fullscreen mode Exit fullscreen mode

Modify the ReactDataGrid component to add the enableCellSelect={true} attribute and an onGridRowsUpdated={onGridRowsUpdated} event handler:

    <ReactDataGrid  
      columns={columns}  
      rowGetter={i => sales[i]}  
      rowsCount={recentSales.length}  
      enableCellSelect={true}  
      onGridRowsUpdated={onGridRowsUpdated}  
    />
Enter fullscreen mode Exit fullscreen mode

These changes allow your React Data Grid to handle updates and persist them in the underlying data source:

Smart Data Table React

Implement Row Sorting

Open the OpenSourceTable-Demo.js file and add the sortable: true attribute to each column definition to enable column sortability:

    { key: "id", name: "Id", editable: true, sortable: true },  
    { key: "country", name: "Country", editable: true, sortable: true },  
    { key: "soldBy", name: "Sold by", editable: true, sortable: true },  
    { key: "client", name: "Client", editable: true, sortable: true },  
    { key: "description", name: "Description", editable: true, sortable: true },  
    { key: "value", name: "Value", editable: true, sortable: true },  
    { key: "itemCount", name: "Item Count", editable: true, sortable: true }
Enter fullscreen mode Exit fullscreen mode

Implement the new sortRows function:

      const sortRows = (initialRows, sortColumn, sortDirection) => rows => {  
        const comparer = (a, b) => {  
          if (sortDirection === "ASC") {  
            return a[sortColumn] > b[sortColumn] ? 1 : -1;  
          } else if (sortDirection === "DESC") {  
            return a[sortColumn] < b[sortColumn] ? 1 : -1;  
          }  
        };  
        return sortDirection === "NONE" ? initialRows : [...rows].sort(comparer);  
      };
Enter fullscreen mode Exit fullscreen mode

Modify the ReactDataGrid component to implement the onGridSort event handler and reference the function we have just created:

    <ReactDataGrid  
      columns={columns}  
      rowGetter={i => sales[i]}  
      rowsCount={recentSales.length}  
      enableCellSelect={true}  
      onGridRowsUpdated={onGridRowsUpdated}  
      onGridSort={(sortColumn, sortDirection) =>  
          setSales(sortRows(sales, sortColumn, sortDirection))  
      }  
    />
Enter fullscreen mode Exit fullscreen mode

Go back to the web page and notice how the columns are now sortable:

Smart Data Table React

Implement Column Reordering

Open the OpenSourceTable-Demo.js file and add this declaration after the imports section:

    const {  
      DraggableHeader: { DraggableContainer }  
    } = require("react-data-grid-addons");
Enter fullscreen mode Exit fullscreen mode

Add the draggable: true property to each column definition to enable column repositioning:

    { key: "id", name: "Id", editable: true, sortable: true, draggable: true },  
    { key: "country", name: "Country", editable: true, sortable: true, draggable: true },  
    { key: "soldBy", name: "Sold by", editable: true, sortable: true, draggable: true },  
    { key: "client", name: "Client", editable: true, sortable: true, draggable: true },  
    { key: "description", name: "Description", editable: true, sortable: true, draggable: true },  
    { key: "value", name: "Value", editable: true, sortable: true, draggable: true },  
    { key: "itemCount", name: "Item Count", editable: true, sortable: true, draggable: true }
Enter fullscreen mode Exit fullscreen mode

Add the onHeaderDrop function to handle column drag-and-drop event:

    const onHeaderDrop = (source, target) => {  
      var columnsCopy = columns.slice();  
      const columnSourceIndex = columns.findIndex(  
        i => i.key === source  
      );  
      const columnTargetIndex = columns.findIndex(  
        i => i.key === target  
      );

      columnsCopy.splice(  
        columnTargetIndex,  
        0,  
        columnsCopy.splice(columnSourceIndex, 1)[0]  
      );

      setColumns(columnsCopy.splice());  
      setColumns(columnsCopy);  
    };
Enter fullscreen mode Exit fullscreen mode

Wrap your existing ReactDataGrid component inside a new DraggableContainer component:

      <DraggableContainer onHeaderDrop={onHeaderDrop}>  
          <ReactDataGrid  
                columns={columns}  
                rowGetter={i => sales[i]}  
                rowsCount={recentSales.length}  
                enableCellSelect={true}  
                onGridRowsUpdated={onGridRowsUpdated}  
                onGridSort={(sortColumn, sortDirection) =>  
                  setSales(sortRows(sales, sortColumn, sortDirection))  
                }  
            />  
      </DraggableContainer>
Enter fullscreen mode Exit fullscreen mode

Rerun the app and try moving the grid columns around:

Smart Data Table React

That looks pretty good, but it was a lot of work to implement those features. Now you're on the hook to maintain that code.

Effective Alternatives

If your company develops first-rate, enterprise-grade applications, you should look into more professional products that offer support for multiple JavaScript frameworks and cover a complete functionality range.

We provide several data table products that can be implemented in a React app with a few code lines. These products are packed with a rich set of ready-to-use features that require no customization, unlike the open-source components we’ve discussed above.

Let’s have a look at several lightweight, smart table products provided, including SpreadJS, DataViewJS, and Wijmo FlexGrid.

SpreadJS

SpreadJS is the World's #1 selling JavaScript Spreadsheet with over 450 Excel functions. Using SpreadJS, you can quickly deliver Excel-like spreadsheet experiences with zero dependencies on Excel, with full support for React, Vue, Angular, and TypeScript.

SpreadJS enables you to create financial reports and dashboards, budgeting and forecasting models, scientific, engineering, healthcare, education, science lab notebooks, and other JavaScript applications for standard spreadsheet scenarios.

For more advanced spreadsheet use cases, SpreadJS allows you to create custom spreadsheets, advanced grids, dashboards, reports, and data input forms with the comprehensive API. Powered by a high-speed calculation engine, SpreadJS enables the most complex calculations.

SpreadJS supports integration by importing and exporting features that work seamlessly with the most demanding Excel.xlsx spreadsheets.

DataViewJS

DataViewJS is a DataGrid platform focused on advanced data presentation. It goes beyond the traditional tabular displays by providing several presentation views, including tree, card, masonry, trellis, timeline, Gantt, calendar, and grid.

With a single line of code, DataViewJS' customization allows you to change layouts freely. It fully supports Angular, React, and Vue.JS.

Wijmo FlexGrid

FlexGrid is one of the Wijmo product family, which also includes JavaScript/HTML5 UI Components such as FlexChart, Input, PivotChart, FinancialChart, ReportViewer, and so on. Wijmo's JavaScript DataGrid fully supports Angular, AngularJS, ReactJS, VueJS, and Knockout.

The lightning speed, extensibility, and independence make Wijmo FlexGrid the ultimate JavaScript smart data grid. FlexGrid’s small footprint improves performance and reduces load time by keeping your web applications compact.

You can take advantage of Excel-like features of FlexGrid, such as data aggregation, cell merging, star sizing, and cell freezing. Also, Cell Templates provide limitless templates with support for declarative markup and binding expressions.

All these products are lightweight and work with virtualization, meaning they keep running at high performance even when they are connected to massive data sources.

Implement a Smart Data Table

Let’s see how to implement a Wijmo React DataGrid smart data table in the Node app we’ve built previously.

Install the Wijmo package with npm:

    npm install @grapecity/wijmo.react.all
Enter fullscreen mode Exit fullscreen mode

In the components folder under the src folder, create a new file named WijmoFlexGrid-Demo.js:

    /my-app  
      /src  
        /components  
          WijmoFlexGrid-Demo.js
Enter fullscreen mode Exit fullscreen mode

Open the WijmoFlexGrid-Demo.js file and add the following code. (Here, we import the WijmoFlexGridDemo component and declare the React state variable that will be used by the FlexGrid as the component’s data source):

    import '@grapecity/wijmo.styles/wijmo.css';  
    import 'bootstrap/dist/css/bootstrap.css';  
    import '@grapecity/wijmo.react.grid';  
    import React, { useState } from 'react';  
    import { FlexGrid, FlexGridColumn } from '@grapecity/wijmo.react.grid';  
    import { recentSales } from "../data/data";

    export const WijmoFlexGridDemo = () => {

      const [sales, setSales] = new useState(recentSales);

        }
Enter fullscreen mode Exit fullscreen mode

Modify the WijmoFlexGridDemo component so that it returns the HTML with the FlexGrid component:

    return (  
        <div className="card main-panel">  
          <div className="card-header">  
              <h1>GrapeCity</h1>  
          </div>  
          <div className="card-body">  
          <h5>Wijmo FlexGrid Demo</h5>  
            <p>  
              Building a Smart Data Table in React  
            </p>           
            <div className="container-fluid">  
              <div className="row">  
                <FlexGrid itemsSource={sales}>  
                  <FlexGridColumn width={50} binding='id' header="ID" />  
                  <FlexGridColumn width={200} binding='client' header="Client" />  
                  <FlexGridColumn width={320} binding='description' header="Description" />  
                  <FlexGridColumn width={100} binding='value' header="Value"/>  
                  <FlexGridColumn width={100} binding='itemCount' header="Quantity" />  
                  <FlexGridColumn width={100} binding='soldBy' header="Sold By" />  
                  <FlexGridColumn width={100} binding='country' header="Country" />  
                </FlexGrid>  
              </div>  
            </div>

          </div>  
        </div>);
Enter fullscreen mode Exit fullscreen mode

Edit the App.js file to include the reference to our new WijmoFlexGridDemo component:

    import React from 'react';  
    import './App.css';  
    import { OpenSourceTableDemo } from './components/OpenSourceTable-Demo.js'  
    import { WijmoFlexGridDemo } from './components/WijmoFlexGrid-Demo.js'

    function App() {  
      return (  
        // <OpenSourceTableDemo/>  
        <WijmoFlexGridDemo/>  
      );  
    }

    export default App;
Enter fullscreen mode Exit fullscreen mode

Run the app:

    npm start
Enter fullscreen mode Exit fullscreen mode

Once the app is running, you’ll see the FlexGrid displaying the sales data:

Smart Data Table React

Cell editing is an out-of-the-box feature in FlexGrid. You can double-click the cells and start editing in place:

Smart Data Table React

By default, a FlexGridColumn is editable. But you can disable editing by explicitly defining the isReadOnly property in the FlexGridColumn component:

    <FlexGridColumn width={50} binding='id' header="ID" isReadOnly='true' />
Enter fullscreen mode Exit fullscreen mode

Also, by default, every FlexGrid column is sortable in ascending or descending order.

Smart Data Table React

You can lock column sorting by setting the allowSorting property to “false”:

    <FlexGridColumn width={200} binding='client' header="Client" allowSorting={false}/>
Enter fullscreen mode Exit fullscreen mode

FlexGrid comes with the ability to reposition columns out of the box freely:

Smart Data Table React

You can disable column reordering by setting the FlexGridColumn’s allowDragging property to “false”:

    <FlexGridColumn width={200} binding='client' header="Client" allowDragging={false}/>
Enter fullscreen mode Exit fullscreen mode

So, all the customizations we need to implement on the open-source React Data Grid control are built-in here.

Import Excel Data

Let’s see how we can import Excel data into our FlexGrid component.

Append the following HTML segment after the card-body div in the return() function:

    <div className="card-body">  
      <div className="card-footer">     
          <div className="input-group">  
            <div className="custom-file">  
                <input type="file" id="importFile"/>  
            </div>  
            <div className="input-group-prepend">  
                <input type="button" value="Import"/>  
            </div>  
          </div>  
      </div>
Enter fullscreen mode Exit fullscreen mode

Run the application and notice the import section at the bottom of the page:

Smart Data Table React

Tell the Import button to import the file into our FlexGrid component. Add the onClick function to the input element and pass the name of the function as “import”:

    <input type="button" onClick={load} value="Import"/>
Enter fullscreen mode Exit fullscreen mode

Add a new load function inside the component class. Here, we’ll first obtain the full path of the user selected file:

      var load = () => {  
        let fileInput = document.getElementById("importFile");  
        if (fileInput.files[0]) {

        }  
      }
Enter fullscreen mode Exit fullscreen mode

At this point, we’ll need a reference to the FlexGrid instance. Go back to the component declaration and add an event handler that is triggered when the FlexGrid is initialized:

    <FlexGrid itemsSource={sales} initialized={initializeFlexGrid}>
Enter fullscreen mode Exit fullscreen mode

Add a declaration for the flexgrid variable and a new function to set it up on the FlexGrid initialization:

      const [flexGrid, setFlexGrid] = useState({});  

      var initializeFlexGrid = (flexGrid) => {  
          setFlexGrid(flexGrid);  
      }
Enter fullscreen mode Exit fullscreen mode

Add an import directive to the Wijmo library that handles Excel file contents:

    import * as wjcGridXlsx from "@grapecity/wijmo.grid.xlsx";
Enter fullscreen mode Exit fullscreen mode

Back to the load function, add a call to the loadAsync function of the Wijmo library to load the file’s contents into the FlexGrid:

      var load = () => {  
        let fileInput = document.getElementById("importFile");  
        if (fileInput.files[0]) {  
          wjcGridXlsx.FlexGridXlsxConverter.loadAsync(flexGrid, fileInput.files[0], { includeColumnHeaders: true }, function (workbook) {  
            flexGrid.autoSizeColumns();  
         });  
        }  
      }
Enter fullscreen mode Exit fullscreen mode

The Wijmo.xlsx library depends on a JavaScript zip library to compress files. Let’s install this dependency by running this console command:

    npm install jszip --save
Enter fullscreen mode Exit fullscreen mode

Rerun the app and test the import feature.

    npm start
Enter fullscreen mode Exit fullscreen mode

Open Excel and create a new Demo.xlsx file with these contents:

Smart Data Table React

Back in our React app, click Choose File, select the Demo.xlsx location, and click the Import button to see the result:

Smart Data Table React

Finally, let’s see what’s needed to export data from our FlexGrid to Excel.

Replace the Import button markup with the following HTML snippet to add the new Export button:

    <div className="input-group-prepend">
      <div className="btn-group">
        <input type="button" onClick={load} value="Import" />
        <input
          type="button"
          onClick={save}
          className="input-group-text"
          value="Export"
        />
      </div>
    </div>;
Enter fullscreen mode Exit fullscreen mode

Note that the onClick event handler is pointing to the save function. Let’s implement this function inside the component class:

      var save = () => {

      }

Enter fullscreen mode Exit fullscreen mode

To export FlexGrid data to an .xlsx file, we need to call the saveAsync function of the FlexGridXlsxConverter class:

      var save = () => {  
        wjcGridXlsx.FlexGridXlsxConverter.saveAsync(flexGrid, {  
            includeColumnHeaders: true,  
            includeCellStyles: false,  
            formatItem: false  
        }, "FlexGrid.xlsx");  
      }
Enter fullscreen mode Exit fullscreen mode

Run the app once again and test the Export button:

Smart Data Table React

Now you can open the .xlsx file exported by the FlexGrid component:

Smart Data Table React

The above demo barely scratches the surface of the FlexGrid component. We recommend that you explore the Wijmo family of JavaScript UI Components and Tools page for complete documentation, demos, and examples written for Angular, React, and Vue.

If you want to see the finished code sample, you can find it running here on StackBlitz.

Wrapping Up

You can create and use React-based smart data tables in various ways. Some approaches require far more manual work than others, but you can’t tell that until you have already spent enough time trying to use them.

There are plenty of free and open-source table libraries that might meet your everyday needs. We’ve seen how to implement one of them in this article.

However, when it comes to business and enterprise projects, you, as a developer, don’t want to be distracted and waste your time developing workarounds, additional features, or configuration. This is why a commercial library component suite like Wijmo ensures a solid return on investment.

Built to be framework-agnostic, the Wijmo JavaScript library includes full support for Angular, AngularJS, ReactJS, and VueJS, with the same grid across frameworks. For your organization, this means freedom of choice and the ability to save time and development effort if you decide to migrate to another JavaScript framework.


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .