ProductSheet (React)

This sample uses a TransposedGrid control to display products on the grid columns, and their properties on the grid rows.

Transposed layouts are especitally useful when there are few data items, and each items has many properties.

Learn about FlexGrid | TransposedGrid API Reference

This example uses React.

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import ReactDOM from 'react-dom/client'; import React from 'react'; import useEvent from 'react-use-event-hook'; import { FlexGrid } from '@mescius/wijmo.react.grid'; import { TransposedGrid, TransposedGridRow } from '@mescius/wijmo.react.grid.transposed'; import './app.css'; import { data, columns } from './data'; function App() { // customize cells to show product images and star rating const formatItem = useEvent((s, e) => { if (e.panel == s.cells) { // get binding from row if possible, then from column let binding = s.rows[e.row].binding || s.columns[e.col].binding; switch (binding) { // product image case 'img': e.cell.innerHTML = '<img src="{img}" draggable="false"/>'.replace('{img}', e.cell.textContent); break; // stars for rating case 'rating': let rating = s.getCellData(e.row, e.col, false), html = new Array(Math.floor(rating) + 1).join('&#x2605;'); if (rating > Math.floor(rating)) { html += '&#9734;'; // white star (half star doesn't work...) } e.cell.innerHTML = '<span class="rating">' + html + '</span>'; break; } } }); // customize transposed product grid row/column sizes const loadedRows = useEvent((s) => { s.columns.defaultSize = 200; setTimeout(() => { s.autoSizeColumn(0, true, 10); // auto-size row headers s.autoSizeRows(); // auto-size data rows s.rows[0].height = 180; // make product images large }, 100); }); return (<div className="container-fluid"> <h2> Default Grid </h2> <p> Products are rendered as rows. </p> <FlexGrid className="product-grid" autoGenerateColumns={false} alternatingRowStep={0} showSelectedHeaders='Row' headersVisibility='Row' isReadOnly={true} copyHeaders='Row' selectionMode='CellRange' formatItem={formatItem} columns={columns} itemsSource={data}/> <h2> Transposed Grid </h2> <p> Products are rendered as columns. </p> <TransposedGrid className="product-grid" alternatingRowStep={0} showSelectedHeaders='Row' headersVisibility='Row' isReadOnly={true} copyHeaders='Row' selectionMode='CellRange' autoGenerateRows={false} formatItem={formatItem} loadedRows={loadedRows} itemsSource={data}> {columns.map((c) => <TransposedGridRow binding={c.binding} header={c.header} align={c.align} format={c.format} wordWrap={c.wordWrap}/>)} </TransposedGrid> </div>); } const container = document.getElementById('app'); if (container) { const root = ReactDOM.createRoot(container); root.render(<App />); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo Wijmo TransposedGrid</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.40/system.src.js" integrity="sha512-G6mEj6h18+m3MvzdviSDfPle/TfH0//cXcB33AKlNR/Rha0yQsKefDZKRTkIZos97HEGq2JMV1RT5ybMoQ3WsQ==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html>
.product-grid { max-height: 2000px; } .product-grid .wj-rowheaders .wj-cell { text-transform: uppercase; font-size: 80%; } .product-grid .wj-cell { padding: 12px; border-bottom: none; } .product-grid .wj-cell span.rating { font-size: 200%; color: #e7711b; } .product-grid img { height: 100%; }
import { ObservableArray } from '@mescius/wijmo'; function getData() { return [ { name: 'SAMSUNG 65-INCH Q9FN QLED TV', rating: 4, price: 2539.99, size: 65, type: 'LCD with Quantum Dots', refresh: 120, hdmi: 4.5, img: 'https://cdn.grapecity.com/wijmo/images/1.png' }, { name: 'TCL 6 SERIES 65-INCH ROKU TV', rating: 4, price: 999.99, size: 65, type: 'LCD', refresh: 60, hdmi: 3, img: 'https://cdn.grapecity.com/wijmo/images/2.png' }, { name: 'LG 55-INCH C7 OLED (OLED55C7P)', rating: 4.5, price: 679.99, size: 55, type: 'OLED', refresh: 120, hdmi: 4, img: 'https://cdn.grapecity.com/wijmo/images/3.png' }, { name: 'VIZIO P-SERIES 65-INCH P65-F1', rating: 4, price: 1124.67, size: 65, type: 'LCD', refresh: 120, hdmi: 5, img: 'https://cdn.grapecity.com/wijmo/images/4.png' }, { name: 'TCL 43S517 ROKU SMART 4K TV', rating: 3.5, price: 319.99, size: 43, type: 'LCD', refresh: 60, hdmi: 3, img: 'https://cdn.grapecity.com/wijmo/images/1.png' }, { name: 'SAMSUNG 65-INCH Q6F QLED TV', rating: 3, price: 1597.99, size: 65, type: 'LCD with Quantum Dot', refresh: 120, hdmi: 4, img: 'https://cdn.grapecity.com/wijmo/images/2.png' } ]; } function getDataColumns() { return [ { binding: 'img', header: ' ', align: 'center' }, { binding: 'name', header: 'Model', align: 'center', wordWrap: true }, { binding: 'rating', header: 'Rating', align: 'center', format: 'n1' }, { binding: 'price', header: 'Price', format: 'c2', align: 'center' }, { binding: 'size', header: 'Screen Size (")', align: 'center' }, { binding: 'type', header: 'Screen Type', align: 'center' }, { binding: 'refresh', header: 'Refresh Rate (Hz)', align: 'center' }, { binding: 'hdmi', header: 'HMI ports', format: 'n0', align: 'center' } ]; } export const data = new ObservableArray(getData()); export const columns = getDataColumns();
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, react: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.grid.immutable': 'npm:@mescius/wijmo.grid.immutable/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', "@mescius/wijmo.react.chart.analytics": "npm:@mescius/wijmo.react.chart.analytics/index.js", "@mescius/wijmo.react.chart.animation": "npm:@mescius/wijmo.react.chart.animation/index.js", "@mescius/wijmo.react.chart.annotation": "npm:@mescius/wijmo.react.chart.annotation/index.js", "@mescius/wijmo.react.chart.finance.analytics": "npm:@mescius/wijmo.react.chart.finance.analytics/index.js", "@mescius/wijmo.react.chart.finance": "npm:@mescius/wijmo.react.chart.finance/index.js", "@mescius/wijmo.react.chart.hierarchical": "npm:@mescius/wijmo.react.chart.hierarchical/index.js", "@mescius/wijmo.react.chart.interaction": "npm:@mescius/wijmo.react.chart.interaction/index.js", "@mescius/wijmo.react.chart.radar": "npm:@mescius/wijmo.react.chart.radar/index.js", "@mescius/wijmo.react.chart": "npm:@mescius/wijmo.react.chart/index.js", "@mescius/wijmo.react.core": "npm:@mescius/wijmo.react.core/index.js", '@mescius/wijmo.react.chart.map': 'npm:@mescius/wijmo.react.chart.map/index.js', "@mescius/wijmo.react.gauge": "npm:@mescius/wijmo.react.gauge/index.js", "@mescius/wijmo.react.grid.detail": "npm:@mescius/wijmo.react.grid.detail/index.js", "@mescius/wijmo.react.grid.filter": "npm:@mescius/wijmo.react.grid.filter/index.js", "@mescius/wijmo.react.grid.grouppanel": "npm:@mescius/wijmo.react.grid.grouppanel/index.js", '@mescius/wijmo.react.grid.search': 'npm:@mescius/wijmo.react.grid.search/index.js', "@mescius/wijmo.react.grid.multirow": "npm:@mescius/wijmo.react.grid.multirow/index.js", "@mescius/wijmo.react.grid.sheet": "npm:@mescius/wijmo.react.grid.sheet/index.js", '@mescius/wijmo.react.grid.transposed': 'npm:@mescius/wijmo.react.grid.transposed/index.js', '@mescius/wijmo.react.grid.transposedmultirow': 'npm:@mescius/wijmo.react.grid.transposedmultirow/index.js', '@mescius/wijmo.react.grid.immutable': 'npm:@mescius/wijmo.react.grid.immutable/index.js', "@mescius/wijmo.react.grid": "npm:@mescius/wijmo.react.grid/index.js", "@mescius/wijmo.react.input": "npm:@mescius/wijmo.react.input/index.js", "@mescius/wijmo.react.olap": "npm:@mescius/wijmo.react.olap/index.js", "@mescius/wijmo.react.viewer": "npm:@mescius/wijmo.react.viewer/index.js", "@mescius/wijmo.react.nav": "npm:@mescius/wijmo.react.nav/index.js", "@mescius/wijmo.react.base": "npm:@mescius/wijmo.react.base/index.js", '@mescius/wijmo.react.barcode.common': 'npm:@mescius/wijmo.react.barcode.common/index.js', '@mescius/wijmo.react.barcode.composite': 'npm:@mescius/wijmo.react.barcode.composite/index.js', '@mescius/wijmo.react.barcode.specialized': 'npm:@mescius/wijmo.react.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'react': 'npm:react/umd/react.production.min.js', 'react-dom': 'npm:react-dom/umd/react-dom.production.min.js', 'react-dom/client': 'npm:react-dom/umd/react-dom.production.min.js', 'redux': 'npm:redux/dist/redux.min.js', 'react-redux': 'npm:react-redux/dist/react-redux.min.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js', "react-use-event-hook": "npm:react-use-event-hook/dist/esm/useEvent.js", }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);