UNPKG

painless-ui

Version:
490 lines (489 loc) 23.9 kB
import React from 'react'; import './Table.css'; import Input from '../InputBox/InputBox'; import Button from '../Button/Button'; import Select from '../Select/Select'; import Option from '../Option/Option'; const defaultPaginationOptions = { currentPage: 1, limit: 10 }; const defaultProps = { className: '', data: [], dataType: 'object', columns: [], loading: false, serverSide: false, responsive: false, paginationOptions: defaultPaginationOptions }; class Table extends React.PureComponent { constructor() { super(...arguments); this.state = { currentPage: 1, sortBy: '', loading: this.props.loading, order: 'asc', searchKeyword: '', filterColumnData: {} }; this.rowSpanSkipDetails = {}; this.colSpanSkipDetails = {}; this.isChangesDetected = false; } setSortController(sortBy) { let order = 'asc'; if (this.state.sortBy === sortBy && this.state.order === 'asc') { order = 'desc'; } ; this.setState({ sortBy, order, localChange: true }); if (this.props.sortOptions !== undefined && this.props.sortOptions.onSort !== undefined && typeof this.props.sortOptions.onSort === 'function') { this.props.sortOptions.onSort(sortBy, order); } } /* * search table data * @params d: Array<any> * @return data:Array<any> */ search(d) { if (this.state.searchKeyword === '' || this.props.serverSide === true) { return d; } const data = [...d]; const searchColumns = []; this.props.columns.forEach((c, i) => { let index = i; if (this.props.dataType !== 'array') { index = c.selector; } if (c.selector !== undefined && c.searchable !== false) searchColumns.push(index); }); const finalData = data.filter((row, i) => { const f = []; searchColumns.forEach((s) => { if (row[s] !== undefined && ('' + row[s]).toLowerCase().includes(this.state.searchKeyword.toLowerCase())) { f.push(row[s]); } }); return f.length > 0; }); const limit = this.props.paginationOptions !== undefined && this.props.paginationOptions.limit ? this.props.paginationOptions.limit : 10; const totalPage = Math.ceil(finalData.length / limit); if (totalPage < this.state.currentPage) { this.setState({ currentPage: 1 }); } return finalData; } /* * sort a table data * @params d: Array<any> * @return data:Array<any> */ sort(d) { if (this.state.sortBy === '') { return d; } const data = [...d]; const { sortBy, order } = this.state; const sortByColumn = this.props.columns.find((column, i) => { if (this.props.dataType === 'array') { return i === parseInt(sortBy); } return column.selector === sortBy; }); if (sortByColumn !== undefined && sortByColumn.onSort !== undefined && typeof sortByColumn.onSort === 'function') { const sortedDataFromView = sortByColumn.onSort(data, order, sortBy); if (sortedDataFromView !== undefined) { return sortedDataFromView; } } if (this.props.serverSide === true) { return d; } data.sort((d1, d2) => { if (d1[sortBy] < d2[sortBy]) { return order === 'asc' ? -1 : 1; } if (d1[sortBy] > d2[sortBy]) { return order === 'asc' ? 1 : -1; } return 0; }); return data; } /* * filter * @params d: Array<any> * @return data: Array<any> */ filter(d) { if (Object.keys(this.state.filterColumnData).length === 0) return d; if (this.props.serverSide) { return d; } const finalData = d.filter((data) => { for (const key in this.state.filterColumnData) { let index = key; if (this.state.filterColumnData.hasOwnProperty(key)) { const element = this.state.filterColumnData[key]; if (this.props.dataType !== 'array') { index = key; } else { index = key; } const columnData = this.props.columns.find((column, i) => { if (this.props.dataType !== 'array') { return column.selector === key; } else { return "" + i === "" + key; } }); if (columnData && columnData.filterData && typeof columnData.filterData === "function") { const filterData = columnData.filterData(data[index], element, data); return filterData === true ? true : false; } if (data[index] !== element) return false; } } return true; }); const limit = this.props.paginationOptions !== undefined && this.props.paginationOptions.limit ? this.props.paginationOptions.limit : 10; const totalPage = Math.ceil(finalData.length / limit); if (totalPage < this.state.currentPage) { this.setState({ currentPage: 1 }); } return finalData; } /* * pagination * @params d:Array<any> * @return details:object */ paginate(d) { const limit = this.props.paginationOptions !== undefined && this.props.paginationOptions.limit ? this.props.paginationOptions.limit : 10; const currentPage = this.state.currentPage; const totalNoOfData = (this.props.paginationOptions !== undefined && this.props.paginationOptions.totalNoOfData && this.props.serverSide === true) ? this.props.paginationOptions.totalNoOfData : d.length; const totalPage = Math.ceil(totalNoOfData / limit); let data = d; if (!this.props.serverSide) data = d.slice((currentPage - 1) * limit, limit * currentPage); if (currentPage > totalPage && this.state.currentPage !== 1) { this.isChangesDetected = true; } return { data, totalPage, totalNoOfData, limit }; } /* * on page change event it will call when pagination button is click */ pageChange(page) { if (this.props.paginationOptions && this.props.paginationOptions.onChange && typeof this.props.paginationOptions.onChange === 'function') { this.props.paginationOptions.onChange(page, this.props.data); } this.setState({ currentPage: page, loading: this.props.serverSide === true, localChange: true }); } /* * get rowSpan */ getRowSpan(column, c, r) { if (column.rowSpan && column.rowSpan[r + 1]) { for (let i = 1; i < column.rowSpan[r + 1]; i++) { if (!this.rowSpanSkipDetails[r + i]) this.rowSpanSkipDetails[r + i] = []; this.rowSpanSkipDetails[r + i].push(c); } return column.rowSpan[r + 1]; } return 1; } /* * get colSpan */ getColSpan(column, c, r) { if (column.colSpan && column.colSpan[r + 1]) { for (let i = 1; i < column.colSpan[r + 1]; i++) { if (!this.colSpanSkipDetails[r]) this.colSpanSkipDetails[r] = []; this.colSpanSkipDetails[r].push(c + i); } return column.colSpan[r + 1]; } return 1; } /* * get derived state from props */ static getDerivedStateFromProps(nextProps, state) { if (state.localChange) { return { localChange: false }; } if (nextProps.loading !== state.loading) { return { loading: nextProps.loading }; } return null; } getSnapshotBeforeUpdate(prevProps) { let isChanged = false; if (prevProps.columns.length !== this.props.columns.length) { isChanged = true; } else { this.props.columns.forEach((c, i) => { if (JSON.stringify(c.rowSpan) !== JSON.stringify(prevProps.columns[i].rowSpan)) { isChanged = true; } else if (JSON.stringify(c.colSpan) !== JSON.stringify(prevProps.columns[i].rowSpan)) { isChanged = true; } }); } if (isChanged) { this.rowSpanSkipDetails = {}; this.colSpanSkipDetails = {}; } return null; } componentDidUpdate(prevProps, prevState) { if (prevState.searchKeyword !== '' && this.state.searchKeyword === '') { this.setState({ currentPage: 1 }); } if (this.isChangesDetected === true) { this.setState({ currentPage: 1, oldState: this.state }, () => { this.isChangesDetected = false; }); } } render() { const props = { ...defaultProps, ...this.props }; let data = this.props.data; //search if (this.props.searchOptions !== undefined && this.props.searchOptions.searchable === true) { data = this.search(this.props.data); } //filter data = this.filter(data); //sort data = this.sort(data); //pagination let paginationUi = React.createElement(React.Fragment, null); if (this.props.paginationOptions !== undefined && this.props.paginationOptions.pagination === true) { const paginationResult = this.paginate(data); let totalPage = 1; data = paginationResult.data; totalPage = paginationResult.totalPage; const totalNoOfData = paginationResult.totalNoOfData; const limit = paginationResult.limit; const from = ((this.state.currentPage - 1) * limit) + 1; const to = (from + limit) - 1; paginationUi = (React.createElement("div", { className: "ui-table-pagination-control" }, React.createElement("span", null, from, " to ", to > totalNoOfData ? totalNoOfData : to, " of ", totalNoOfData), React.createElement("ul", { className: "pull-right" }, React.createElement("li", { className: "ui-table-pagination-next", onClick: () => { if (this.state.currentPage > 1) { this.pageChange(1); } } }, React.createElement(Button, { disabled: !(this.state.currentPage > 1), styleType: "text", rounded: true }, "|\u2039")), React.createElement("li", { className: "ui-table-pagination-prev", onClick: () => { if (this.state.currentPage > 1) { this.pageChange(this.state.currentPage - 1); } } }, React.createElement(Button, { disabled: !(this.state.currentPage > 1), styleType: "text", rounded: true }, "\u2039")), React.createElement("li", { className: "ui-table-pagination-next", onClick: () => { if (this.state.currentPage < totalPage) { this.pageChange(this.state.currentPage + 1); } } }, React.createElement(Button, { disabled: !(this.state.currentPage < totalPage), styleType: "text", rounded: true }, "\u203A")), React.createElement("li", { className: "ui-table-pagination-next", onClick: () => { if (this.state.currentPage < totalPage) { this.pageChange(totalPage); } } }, React.createElement(Button, { disabled: !(this.state.currentPage < totalPage), styleType: "text", rounded: true }, "\u203A|"))))); } return (React.createElement("div", { className: 'ui-table-container ', style: this.props.containerStyle || {} }, React.createElement("div", { className: 'ui-table-toolbar-wrapper pull-left ui-w-100' }, this.props.headerWrapperComponent ? this.props.headerWrapperComponent() : React.createElement(React.Fragment, null), (this.props.searchOptions !== undefined && this.props.searchOptions.searchable === true) ? React.createElement("div", { className: "ui-table-search-container pull-right" }, this.props.searchOptions.searchComponent !== undefined && typeof this.props.searchOptions.searchComponent === 'function' ? React.createElement(React.Fragment, null, this.props.searchOptions.searchComponent((v) => { //onchange event this.setState({ searchKeyword: v, localChange: true }); if (props.searchOptions !== undefined && props.searchOptions.onSearch !== undefined && typeof props.searchOptions.onSearch === 'function') { props.searchOptions.onSearch(v); } })) : React.createElement(Input, { type: "text", placeholder: "Type something...", style: { width: "150px" }, value: this.state.searchKeyword, onChange: (e) => { this.setState({ searchKeyword: e.target.value, localChange: true }); if (props.searchOptions !== undefined && props.searchOptions.onSearch !== undefined && typeof props.searchOptions.onSearch === 'function') { props.searchOptions.onSearch(e.target.value); } } })) : React.createElement(React.Fragment, null)), React.createElement("div", { className: 'ui-table-wrapper ' + (props.responsive ? 'responsive ' : '') }, React.createElement("table", { className: 'ui-table ' + (props.className || '') + (props.noBg ? ' no-bg' : '') + (props.border ? ' border' : ''), style: this.props.style || {} }, this.props.noHeader === true ? React.createElement(React.Fragment, null) : React.createElement("thead", { className: "ui-table-header" }, React.createElement("tr", null, props.columns.map((column, i) => { let selector = ''; const tempFilterData = new Set(); if (props.dataType === 'array' || column.selector === undefined) { selector = i; } else { selector = column.selector; } const sortClassName = selector === this.state.sortBy ? (' sorting ' + this.state.order) : ''; return (React.createElement("th", { onClick: () => { if (column.sortable === true) this.setSortController(selector); }, key: 'th-' + i, className: (column.className ? column.className : "") + " " + (column.sortable === true ? 'sort-column' : '') + sortClassName }, column.name, column.filter ? React.createElement(React.Fragment, null, column.filterRender ? column.filterRender((v) => { const t = { ...this.state.filterColumnData }; let index = i; if (this.props.dataType !== 'array' && column.selector) index = column.selector; if (v === '' || v === undefined) { delete t[index]; } else t[index] = v; this.setState({ filterColumnData: t }, () => { if (column.onFilter) { column.onFilter(this.state.filterColumnData, index); } }); }) : React.createElement(Select, { searchable: true, style: { width: '100%', display: 'block' }, onClick: (e) => e.stopPropagation(), onChange: (e) => { const t = { ...this.state.filterColumnData }; let index = i; if (this.props.dataType !== 'array' && column.selector) index = column.selector; if (e.value === '' || e.value === undefined) { delete t[index]; } else t[index] = e.value; this.setState({ filterColumnData: t }, () => { if (column.onFilter) { column.onFilter(this.state.filterColumnData, index); } }); } }, this.props.data.map((row, i) => { if (props.dataType === 'array') { row.map((d, c) => { if (!tempFilterData.has(d)) { tempFilterData.add(d); return React.createElement(Option, { value: d, key: 'filter-option-' + c + '-' + column.name }, d); } return React.createElement(React.Fragment, null); }); } else { if (column.selector) { const d = row[column.selector]; if (!tempFilterData.has(d)) { tempFilterData.add(d); return React.createElement(Option, { value: d, key: 'filter-option-' + i + '-' + column.name }, d); } return React.createElement(React.Fragment, null); } else return React.createElement(React.Fragment, null); } return React.createElement(React.Fragment, null); // (column.selector) ? ( // console.log(column.selector) // const d = row[column.selector]; // const tempFilterData = new Set(); // if(!tempFilterData.has(d)) { // tempFilterData.add(d); // return <option value={d} key={'filter-option-'+c+'-'+column.name}>{d}</option> // } // }) : <></> }))) : React.createElement(React.Fragment, null))); }))), this.state.loading === true ? (React.createElement("tbody", null, React.createElement("tr", null, React.createElement("td", { colSpan: this.props.columns.length }, "Loading...")))) : (React.createElement("tbody", { className: "ui-table-body" }, data.length === 0 ? React.createElement("tr", null, React.createElement("td", { colSpan: this.props.columns.length }, "No data found")) : data.map((row, r) => { return (React.createElement("tr", { key: 'tr-' + r, className: this.props.rows && this.props.rows.classNameCallback ? this.props.rows.classNameCallback(row, r) : '' }, props.dataType === 'array' ? (row.map((d, c) => { return React.createElement("td", { key: 'td-' + c + '-' + r }, d); })) : (props.columns.map((column, c) => { if (this.rowSpanSkipDetails[r] && this.rowSpanSkipDetails[r].indexOf(c) !== -1) { return React.createElement(React.Fragment, null); } else if (this.colSpanSkipDetails[r] && this.colSpanSkipDetails[r].indexOf(c) !== -1) { return React.createElement(React.Fragment, null); } let value = ''; if (column.render && typeof column.render === 'function') { value = column.render(row, column, r); } else if (column.selector) { value = row[column.selector]; } else { throw new Error('column should have selector property or render function'); } return React.createElement("td", { className: (column.className ? column.className : ""), rowSpan: this.getRowSpan(column, c, r), colSpan: this.getColSpan(column, c, r), key: 'td-' + c + '-' + r }, value); })))); }))))), this.state.loading === true ? (React.createElement(React.Fragment, null)) : (paginationUi))); } } export default Table;