UNPKG

periodicjs.ext.reactapp

Version:
1,210 lines (1,191 loc) 60.2 kB
import React, { Component, /*PropTypes,*/ } from 'react'; import { Link, } from 'react-router'; import * as rb from 're-bulma'; import moment from 'moment'; import numeral from 'numeral'; import utilities from '../../util'; import qs from 'querystring'; import debounce from 'debounce'; import { flatten, } from 'flat'; import { getRenderedComponent, } from '../AppLayoutMap'; // import capitalize from 'capitalize'; // import pluralize from 'pluralize'; import FileReaderInput from 'react-file-reader-input'; import path from 'path'; import { csv2json, json2csv, } from 'json-2-csv'; import RACodeMirror from '../RACodeMirror'; import ResponsiveDatalist from '../ResponsiveDatalist'; import { filterQuerySelectOptions, propTypes, defaultProps, getOptionsHeaders, getHeadersFromRows, excludeEmptyHeaders, getFilterOptions, defaultNewRowData, filterQuerySelectOptionsMap, getFilterSortableOption, } from './TableHelpers'; const filterLabelStyleProps = { alignItems: 'center', display: 'flex', flex: 1, height: '100%', }; class ResponsiveTable extends Component { constructor(props) { // console.log({props}) super(props); // console.debug('this.props.getState()',this.props.getState()); let rows = props.rows || []; rows = (rows.documents) ? rows.documents : rows; let headers = ((!props.headers || !props.headers.length) && rows[0]) ? getHeadersFromRows({ rows: props.rows, sortable: props.sortable, excludeEmptyHeaders: props.excludeEmptyHeaders, }) : props.headers; headers = getOptionsHeaders(props, headers); headers = excludeEmptyHeaders({ headers, excludeEmptyHeaders: props.excludeEmptyHeaders, }); if (props.flattenRowData) { rows = rows.map(row => Object.assign({}, row, flatten(row, props.flattenRowDataOptions))); } this.filterSelectOptions = getFilterOptions({ rows, headers, filters: this.props.filterSelectOptions, simpleSearchFilter: this.props.simpleSearchFilter, }); this.sortableSelctOptions = getFilterSortableOption({ headers, }); // const newRowData = (headers && Array.isArray(headers) && headers.length) // ? headers.reduce((newRow, header) => { // if (header.sortid && header.footerFormElementPassProps && header.footerFormElementPassProps.value) { // newRow[ header.sortid ] = header.footerFormElementPassProps.value; // } // return newRow; // }) // : {}; let numItems = (typeof props.numItems === 'number') ? props.numItems : 0; if (rows.length && typeof props.numItems === 'number' && rows.length > props.numItems) numItems = rows.length; // const numItems = (rows.length > props.nu) typeof props.numItems === 'number' ? props.numItems : rows.length; let numPages = this.props.numPages; const calcNumPages = Math.ceil(numItems / props.limit); if (calcNumPages > numPages) numPages = calcNumPages; this.state = { headers: headers, rows: rows, hasPagination: props.hasPagination, hasHeader: props.hasHeader, hasFooter: props.hasFooter, limit: props.limit, currentPage: props.currentPage, numItems, numPages, numButtons: props.numButtons, isLoading: false, sortProp: this.props.searchField || '_id', sortOrder: 'desc', filterRowData: [], filterRowNewData: defaultNewRowData, newRowData: {}, selectedRowData: {}, selectedRowIndex: {}, showFilterSearch: props.showFilterSearch, disableSort: props.disableSort, hiddenHeaders: props.hiddenHeaders || [], // usingFiltersInSearch: props.usingFiltersInSearch, }; this.searchFunction = debounce(this.updateTableData, 200); this.getRenderedComponent = getRenderedComponent.bind(this); this.addRow = this.updateByAddRow.bind(this); this.replaceRows = this.updateByReplacingRows.bind(this); this.addingRows = this.updateByAddingRows.bind(this); this.selectRow = this.updateSelectedRow.bind(this); this.deleteRow = this.updateByDeleteRow.bind(this); this.moveRowDown = this.updateByMoveRowDown.bind(this); this.moveRowUp = this.updateByMoveRowUp.bind(this); this.updateNewRowText = this.updateNewRowDataText.bind(this); this.updateInlineRowText = this.updateInlineRowDataText.bind(this); this.getFooterAddRow = this.updateGetFooterAddRow.bind(this); this.removeFilterRow = this.removeFilterByDeleteRow.bind(this); this.addFilterRow = this.addFilterByAddRow.bind(this); this.updateNewFilterRowText = this.updateNewFilterRowDataText.bind(this); this.searchField = (this.props.searchField) ? this.props.searchField : (Array.isArray(this.props.headers) && this.props.headers.length) ? this.props.headers[ 0 ].sortid : (Array.isArray(this.state.headers) && this.state.headers.length) ? this.state.headers[ 0 ].sortid : undefined; this.fastUpdateRows = this.fastUpdateRows.bind(this); } componentWillReceiveProps(nextProps) { let rows = nextProps.rows || []; let headers = ((!nextProps.headers || !nextProps.headers.length) && rows[0]) ? getHeadersFromRows({ rows, sortable: nextProps.sortable, excludeEmptyHeaders: nextProps.excludeEmptyHeaders, }) : nextProps.headers; headers = getOptionsHeaders(nextProps); headers = excludeEmptyHeaders({ headers, excludeEmptyHeaders: nextProps.excludeEmptyHeaders, }); if (nextProps.flattenRowData) { rows = (rows||[]).map(row => Object.assign({}, row, flatten(row, nextProps.flattenRowDataOptions))); } // console.debug('nextProps.limit', nextProps.limit); // console.debug('this.state.limit', this.state.limit); this.setState({ headers: headers, rows: rows, hasPagination: nextProps.hasPagination, hasHeader: nextProps.hasHeader, hasFooter: nextProps.hasFooter, limit: nextProps.limit, currentPage: nextProps.currentPage, numItems: nextProps.numItems, numPages: Math.ceil(nextProps.numItems / nextProps.limit), numButtons: nextProps.numButtons, }); } fastUpdateRows({ rows, clearNewRowData, }) { const filteredRows = rows.filter(row => row && Object.keys(row).length > 0); const updatedState = { rows: filteredRows, }; if (clearNewRowData) { updatedState.newRowData = {}; } // console.log({ filteredRows, rows, updatedState, }); this.setState(updatedState, () => { // console.warn('update form', { rows, }, 'this.props.onChange', this.props.onChange.toString()); if (this.props.tableForm) this.props.onChange(updatedState); }); } updateSelectedRow(options) { // console.debug({ options }); this.updateTableData(options); } updateByReplacingRows(newrows) { this.updateTableData({ rows: newrows.concat([]), clearNewRowData: true, }); } toggleHeader({ header, }) { const hiddenIndex = this.state.hiddenHeaders.findIndex(head=>head===header.sortid); const hiddenHeaders = [].concat(this.state.hiddenHeaders); if (hiddenIndex < 0) hiddenHeaders.push(header.sortid); else hiddenHeaders.splice(hiddenIndex, 1); this.setState({ hiddenHeaders, }); } updateByAddingRows(newrows) { let rows = this.state.rows.concat(newrows || []); // this.updateTableData({ rows, clearNewRowData: true, }); this.fastUpdateRows({ rows, clearNewRowData: true, }); } updateByAddRow() { let rows = this.state.rows.concat([]); let newRow = Object.assign({}, this.state.newRowData); rows.splice(rows.length, 0, newRow); // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); // this.updateTableData({ rows, clearNewRowData: true, }); this.fastUpdateRows({ rows, clearNewRowData: true, }); } updateByDeleteRow(rowIndex) { const newrows = [];//.concat(this.state.rows||[]); this.state.rows.forEach((row, i) => { // console.log({ rowIndex, i }); if (i !== rowIndex) { newrows.push(row); } }); this.fastUpdateRows({ rows:newrows, }); } updateByMoveRowUp(rowIndex) { let rows = this.state.rows.concat([]); let deletedRow = rows.splice(rowIndex, 1)[0]; rows.splice(rowIndex - 1, 0, deletedRow); // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.fastUpdateRows({ rows, }); } updateByMoveRowDown(rowIndex) { let rows = this.state.rows.concat([]); let deletedRow = rows.splice(rowIndex, 1)[0]; rows.splice(rowIndex + 1, 0, deletedRow); // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.fastUpdateRows({ rows, }); } updateNewRowDataText(options) { let { name, text, } = options; let updatedStateProp = { newRowData: Object.assign({}, this.state.newRowData, { [name]: text, }), }; this.props.headers.forEach(header => { if (header.sortid !== name && header.formtype && header.defaultValue && !updatedStateProp.newRowData[header.sortid]) { updatedStateProp.newRowData[header.sortid] = header.defaultValue; } }); // console.debug({ updatedStateProp, options }); this.setState(updatedStateProp); } updateInlineRowDataText(options) { let { name, text, rowIndex, } = options; let rows = this.state.rows.concat([]); rows[rowIndex][name] = text; // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.fastUpdateRows({ rows, }); } handleFileUpload(type) { return (e, results) => { let updatefunction = (type === 'replace') ? this.replaceRows : this.addingRows; try { console.debug({ e, results, }); results.forEach(result => { const [e, file,] = result; if (path.extname(file.name) === '.csv') { csv2json(e.target.result, (err, newRows) => { if (err) throw err; // console.debug({ newRows, }, 'e.target.result', e.target.result); updatefunction(newRows); }, { options: this.props.csvOptions, // keys: this.state.headers.map(header => header.sortid), }); } else { let newRows = JSON.parse(e.target.result); updatefunction(newRows); } }); } catch (e) { this.props.errorNotification(e); } }; } removeFilterByDeleteRow(rowIndex) { let rows = this.state.filterRowData.concat([]); rows.splice(rowIndex, 1); this.setState({ filterRowData: rows, }, () => { this.updateTableData({}); }); } addFilterByAddRow() { let rows = this.state.filterRowData.concat([]); let newRow = Object.assign({}, this.state.filterRowNewData); rows.splice(rows.length, 0, newRow); if (newRow.property === '__property__') { this.props.createNotification({ text: 'Please select a property', type: 'error', timed: 5000, }); } else if (newRow.filter_value === '__filter__') { this.props.createNotification({ text: 'Please select a filter', type: 'error', timed: 5000, }); } else { // console.debug('addFilterByAddRow', { rows }); this.setState({ filterRowData: rows, filterRowNewData: defaultNewRowData, }, () => { this.updateTableData({}); }); } } updateNewFilterRowDataText(options) { let { name, text, } = options; let updatedStateProp = { filterRowNewData: Object.assign({}, this.state.filterRowNewData, { [name]: text, }), }; // console.debug({ updatedStateProp, options }); this.setState(updatedStateProp); } updateTableData(options = {}) { let updatedState = { modifyTimeStamp: new Date(), }; let newSortOptions = {}; // console.warn('updateTableData',{options}) // options.rows = undefined; // console.log('updateTableData this', this,{updatedState}); if (options.newrows) { options.rows = undefined; delete options.rows; options.rows = [].concat(options.newrows); } if (options.clearNewRowData) { updatedState.newRowData = {}; } if (typeof options.selectedRowIndex !== undefined) { updatedState.selectedRowIndex = options.selectedRowIndex; } if (typeof options.selectedRowData !== undefined) { updatedState.selectedRowData = options.selectedRowData; } if (!this.props.baseUrl) { const searchField = this.searchField; if (options.rows && Array.isArray(options.rows)) { updatedState = Object.assign({}, updatedState, { rows: [...options.rows, ], }); } else { updatedState.rows = (typeof options.rows !== 'undefined') ? options.rows : this.props.rows; } // console.debug('initial',{ updatedState, }); if (options.sort) { newSortOptions.sortProp = options.sort; if (this.state.sortProp === options.sort) { newSortOptions.sortOrder = (this.state.sortOrder !== 'desc') ? 'desc' : 'asc'; } else { newSortOptions.sortOrder = 'desc'; } updatedState.rows = updatedState.rows.sort(utilities.sortObject(newSortOptions.sortOrder, options.sort)); updatedState.sortOrder = newSortOptions.sortOrder; updatedState.sortProp = options.sort; } else if (this.props.turnOffTableSort){ // updatedState.rows = updatedState.rows; } else if ((this.state.sortOrder || this.state.sortProp) && !this.state.disableSort) { // newSortOptions.sortProp = this.state.sortProp; // newSortOptions.sortOrder = (this.state.sortOrder === 'desc' || this.state.sortOrder === '-') ? 'desc' : 'asc'; // updatedState.rows = updatedState.rows.sort(utilities.sortObject(newSortOptions.sortOrder, newSortOptions.sortProp)); } if (this.props.tableSearch && this.state.filterRowData && this.state.filterRowData.length) { let filteredRows = []; updatedState.rows.forEach(row => { this.state.filterRowData.forEach(filter => { if (row[filter.property]) { switch (filter.filter_value) { case 'like': case 'in': if (row[filter.property].indexOf(filter.value) !== -1) filteredRows.push(row); break; case 'not': if (row[filter.property] !== filter.value) filteredRows.push(row); break; case 'not-like': case 'not-in': if (row[filter.property].indexOf(filter.value) === -1) filteredRows.push(row); break; case 'lt': if (row[filter.property] < filter.value) filteredRows.push(row); break; case 'lte': if (row[filter.property] <= filter.value) filteredRows.push(row); break; case 'gt': if (row[filter.property] > filter.value) filteredRows.push(row); break; case 'gte': if (row[filter.property] >= filter.value) filteredRows.push(row); break; case 'exists': if (typeof row[filter.property] !== 'undefined') filteredRows.push(row); break; case 'size': if (row[filter.property].length > filter.value) filteredRows.push(row); break; case 'is-date': if (moment(row[filter.property]).isSame(filter.value)) filteredRows.push(row); break; case 'lte-date': if (moment(row[filter.property]).isSameOrBefore(filter.value)) filteredRows.push(row); break; case 'lt-date': if (moment(row[filter.property]).isBefore(filter.value)) filteredRows.push(row); break; case 'gte-date': if (moment(row[filter.property]).isSameOrAfter(filter.value)) filteredRows.push(row); break; case 'gt-date': if (moment(row[filter.property]).isAfter(filter.value)) filteredRows.push(row); break; case 'is': default: if (row[filter.property] === filter.value) filteredRows.push(row); break; } } }); // row[ searchField ].indexOf(options.search) !== -1 }); updatedState.rows = filteredRows; // console.debug('updatedState.rows', updatedState.rows, { filteredRows, }); } if (this.props.tableSearch && searchField && options.search) { updatedState.rows = (updatedState.rows).filter(row => { return row[ searchField ] && row[ searchField ].toString().toLowerCase().indexOf(options.search.toString().toLowerCase()) !== -1; }); } updatedState.numPages = Math.ceil(updatedState.rows.length / this.state.limit); updatedState.limit = this.state.limit; updatedState.currentPage = (typeof options.pagenum !== 'undefined') ? options.pagenum : (this.state.currentPage && this.state.currentPage <= updatedState.numPages) ? this.state.currentPage : 1; updatedState.isLoading = false; // else { // if (this.props.tableForm) { // // console.warn('calling on change with baseurl', { updatedState }); // this.props.onChange(updatedState); // } // this.setState(updatedState); this.setState(updatedState, () => { if (this.props.tableForm) { this.props.onChange(updatedState); } }); // } } else { if (options.sort) { newSortOptions.sortProp = options.sort; if (this.state.sortProp === options.sort) { newSortOptions.sortOrder = (this.state.sortOrder === '') ? '-' : ''; } else { newSortOptions.sortOrder = ''; } } else if (this.props.turnOffTableSort){ // updatedState.rows = updatedState.rows; } else if (this.state.sortOrder || this.state.sortProp) { newSortOptions.sortProp = this.state.sortProp; newSortOptions.sortOrder = (this.state.sortOrder === 'desc' || this.state.sortOrder === '-') ? '-' : ''; } if (options.pagenum < 1) { options.pagenum = 1; } this.setState({ isLoading: true, }); let stateProps = this.props.getState(); let fetchURL = `${stateProps.settings.basename}${this.props.baseUrl}&${qs.stringify({ limit: this.state.limit || this.props.limit, sort: (newSortOptions.sortProp) ? `${newSortOptions.sortOrder}${newSortOptions.sortProp}` : undefined, fq: (this.state.filterRowData && this.state.filterRowData.length) ? this.state.filterRowData.map(frd => { return `${frd.property}|||${frd.filter_value}|||${frd.value}`; }) : undefined, query: options.search, allowSpecialCharacters: true, pagenum: options.pagenum || 1, })}`; // console.debug('this.state.filterRowData', this.state.filterRowData, { options, fetchURL, }); let headers = Object.assign({ 'x-access-token': stateProps.user.jwt_token, }, stateProps.settings.userprofile.options.headers); utilities.fetchComponent(fetchURL, { headers, })() .then(response => { // let usingResponsePages = false; // console.debug('this.props.dataMap',this.props.dataMap) // console.log({ response }) if (response.data && response.result && response.status) { // console.log('USE DATA FROM RESPONSE', response.data) // console.log('this.props.dataMap',this.props.dataMap) response = response.data; } this.props.dataMap.forEach(data => { if (data.key === 'rows') { let rows = response[ data.value ] || []; rows = (rows.documents) ? rows.documents : rows; // console.log({ rows }); if (this.props.flattenRowData) { updatedState[ data.key ] = rows.map(row => Object.assign({}, row, flatten(row, this.props.flattenRowDataOptions))); } } else { // if (data.key === 'numPages') { // usingResponsePages = true; // } updatedState[ data.key ] = response[ data.value ]; } }); updatedState.numPages = Math.ceil(updatedState.numItems / this.state.limit); updatedState.limit = this.state.limit; updatedState.currentPage = (typeof options.pagenum !=='undefined') ? options.pagenum : this.props.currentPage; updatedState.isLoading = false; if (options.sort) { updatedState.sortOrder = newSortOptions.sortOrder; updatedState.sortProp = options.sort; } // console.log({ updatedState }); // if (this.props.tableForm) { // // console.warn('calling on change with baseurl', { updatedState }); // this.props.onChange(updatedState); // } // this.setState(updatedState); this.setState(updatedState, () => { if (this.props.tableForm) { this.props.onChange(updatedState); } }); }, e => { this.props.errorNotification(e); }); } } formatValue(value, row, options, header) { try { // console.debug({ value, row, options, header, }); // console.debug(options.rowIndex,this.state.selectedRowIndex) let returnValue = value; if (header && header.stringify) { value = JSON.stringify(value, null, 2); returnValue = JSON.stringify(value, null, 2); } if (header && header.tostring) { value = value.toString(); returnValue = value.toString(); } if (header && header.customCellLayout) { header.customCellLayout.props = Object.assign({}, header.customCellLayout.props, { cell:value, row, }); return this.getRenderedComponent(header.customCellLayout); } if (header && header.tagifyArray) { return value.map((val, kv) => ( <rb.Tag {...header.tagProps} key={kv}>{ (header.tagifyValue) ? val[ header.tagifyValue ].toString() : val.toString()} </rb.Tag>)); } else if (header && header.selectedOptionRowHeader) { return <input type="radio" checked={(options.rowIndex===this.state.selectedRowIndex)?true:false} />; } else if (this.props.useInputRows && header && header.formtype && header.formtype==='code') { let CodeMirrorProps = Object.assign({}, { codeMirrorProps: { lineNumbers: true, value: value, //formElement.value || this.state[ formElement.name ] || getPropertyAttribute({ element:formElement, property:this.state, }); //value: this.state[ formElement.name ] || formElement.value, style: { minHeight:200, }, lineWrapping:true, onChange: function (text){ // console.log({ newvalue }); let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }.bind(this), }, }, header.CodeMirrorProps); let codeProps = Object.assign({ wrapperProps: { style: { overflow: 'auto', backgroundColor: 'white', border: '1px solid #d3d6db', borderRadius: 3, height: 'auto', boxShadow: 'inset 0 1px 2px rgba(17,17,17,.1)', }, }, }, header.codeProps); return <RACodeMirror {...CodeMirrorProps} {...codeProps} />; } else if (this.props.useInputRows && header && header.formtype && header.formtype==='textarea') { return <rb.Textarea {...header.textareaProps} value={value} onChange={(event) => { let text = event.target.value; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }} >{value}</rb.Textarea>; } else if (this.props.useInputRows && header && header.formtype && header.formtype==='text') { return <rb.Input value={value} readOnly={header.readOnly? true: false} {...header.inputProps} onChange={(event) => { let text = event.target.value; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }} >{value}</rb.Input>; } else if (this.props.useInputRows && header && header.formtype && header.formtype === 'select') { let selectOptions = header.formoptions || []; return <rb.Select value={value} {...header.selectProps} onChange={(event) => { let text = event.target.value; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }}> {selectOptions.map((opt, k) => { return <option key={k} disabled={opt.disabled} value={opt.value}>{opt.label || opt.value}</option>; })} </rb.Select>; } else if (this.props.useInputRows && header && header.formtype && header.formtype === 'datalist') { let rowdata = []; if (this.props.__tableOptions) { rowdata = Array.isArray(this.props.__tableOptions[ header.sortid ][ options.rowIndex ]) ? this.props.__tableOptions[ header.sortid ][ options.rowIndex ] : Array.isArray(this.props.__tableOptions[ header.sortid ]) ? this.props.__tableOptions[ header.sortid ] : []; } return <ResponsiveDatalist getState={this.props.getState.bind(this)} value={value} {...header.datalistProps} datalistdata={ rowdata } onChange={(event) => { let text = event; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }} > </ResponsiveDatalist>; } else if (typeof options.idx !=='undefined' && typeof returnValue==='string' && returnValue.indexOf('--idx--')!==-1) { returnValue = returnValue.replace('--idx--', options.idx); } if (typeof options.idx !=='undefined' && typeof returnValue==='string' && returnValue.indexOf('--idx-ctr--')!==-1) { returnValue = returnValue.replace('--idx-ctr--', (options.idx+1)); } if (options.momentFromNow) { returnValue = moment(value).fromNow(); } else if (options.momentFormat) { returnValue = moment(value).format(options.momentFormat); } else if (options.numeralFormat) { returnValue = numeral(value).format(options.numeralFormat); } else if (header && header.wrapPreOutput) { returnValue = <pre {...header.wrapPreOutputProps}>{value}</pre>; } else if (options.icon && value) { // console.debug({value}) if (typeof value !== 'string' && Array.isArray(value)) { let icons = value.map((val, i) => <rb.Icon key={i+Math.random()} {...options.iconProps} icon={val} />); return icons; } else { return <rb.Icon {...options.iconProps} icon={value} />; } } else if (options.image && value) { if (typeof value !== 'string' && Array.isArray(value)) { let images = value.map((val, i) => <rb.Image key={i} {...options.imageProps} src={val} />); return { images, }; } else { return <rb.Image {...options.imageProps} src={value} />; } } if (typeof returnValue === 'undefined' || (returnValue === null && this.props.suppressNullValues)) { return ''; // } else if (typeof returnValue !== 'object') { // return JSON.stringify(returnValue); } else if (returnValue === null) { return 'null'; } else { return returnValue.toString(); } } catch (e) { console.log({ value, row, options, header, }, e); return 'invalid'; } } getHeaderLinkURL(link, row) { let returnLink = link.baseUrl; if (link.params && link.params.length > 0) { link.params.forEach((param) => { returnLink = returnLink.replace(param.key, row[ param.val ]); }); } return returnLink; } updateGetFooterAddRow(header) { if (header.selectedOptionRowHeader) return null; switch (header.formtype) { case 'select': return (<rb.Select value={this.state.newRowData[ header.sortid ] || header.defaultValue} onChange={(event) => { let text = event.target.value; let name = header.sortid; this.updateNewRowText({ name, text, }); }} {...header.footerFormElementPassProps}> {header.formoptions.map((opt, k) => { return <option key={k} disabled={opt.disabled} value={opt.value}>{opt.label || opt.value}</option>; })} </rb.Select>); // break; case 'textarea': return (<rb.Textarea value={this.state.newRowData[ header.sortid ] || ''} onChange={(event) => { let text = event.target.value; let name = header.sortid; this.updateNewRowText({ name, text, }); }} {...header.footerFormElementPassProps}> </rb.Textarea>); // break; case 'code': var CodeMirrorProps = Object.assign({}, { codeMirrorProps: { lineNumbers: true, value: this.state.newRowData[ header.sortid ] || '', //formElement.value || this.state[ formElement.name ] || getPropertyAttribute({ element:formElement, property:this.state, }); //value: this.state[ formElement.name ] || formElement.value, style: { minHeight:200, }, lineWrapping:true, onChange: function (text){ // console.log({ newvalue }); let name = header.sortid; this.updateNewRowText({ name, text, }); }.bind(this), }, }, header.CodeMirrorProps); var codeProps = Object.assign({ wrapperProps: { style: { overflow: 'auto', backgroundColor: 'white', border: '1px solid #d3d6db', borderRadius: 3, height: 'auto', boxShadow: 'inset 0 1px 2px rgba(17,17,17,.1)', }, }, }, header.codeProps); return (<RACodeMirror {...CodeMirrorProps} {...codeProps} />); case 'datalist': let rowdata = this.props.__tableOptions && Array.isArray(this.props.__tableOptions[ header.sortid ]) ? this.props.__tableOptions[ header.sortid ] : []; // console.log('outside this.props', this.props); // const updateFunction = { // onChange: function (event) { // let text = event; // let name = header.sortid; // this.updateNewRowText({ name, text, }); // }.bind(this) // }; return (<ResponsiveDatalist // value={value} {...this.props} datalistdata={rowdata} onChange={(event)=> { // console.log('inside this.props', this.props); let text = event; let name = header.sortid; this.updateNewRowText({ name, text, }); }} // {{...}updateFunction} {...header.datalistProps} > </ResponsiveDatalist>); case 'text': default: return (<rb.Input value={this.state.newRowData[ header.sortid ] || ''} onChange={(event) => { let text = event.target.value; let name = header.sortid; this.updateNewRowText({ name, text, }); }} {...header.footerFormElementPassProps}> </rb.Input>); // break; } } toggleAdvancedSearchFilters() { this.setState({ showFilterSearch: !this.state.showFilterSearch, }); // showFilterSearch:false, // usingFiltersInSearch: false, // showFilterSearch: props.showFilterSearch, // usingFiltersInSearch: props.usingFiltersInSearch } render() { // console.debug('render this.state', this.state); let calcStartIndex = ((this.state.currentPage - 1) * this.state.limit); let startIndex = (!this.props.baseUrl) ? calcStartIndex :0 ; let endIndex = (!this.props.baseUrl) ? ((this.state.limit * this.state.currentPage)) : this.state.limit; let displayRows = this.state.rows.slice(startIndex, endIndex).filter(row=>row && typeof row ==='object' && Object.keys(row).length); // console.warn({ displayRows }); let mergedCustomLayout = (this.props.customLayout && displayRows && displayRows.length) ? (<div style={ Object.assign({ flexDirection: 'rows', display: 'flex', }, this.props.customLayoutStyle) }>{displayRows.map(row => { let mergedLayout = Object.assign({}, this.props.customLayout, { props: Object.assign({}, this.props.customLayout.props, row, { row, }, { __ra_rt_link: (this.props.customLayout.link) ? this.getHeaderLinkURL(this.props.customLayout.link, row) : undefined, }), }); // console.debug({ mergedLayout }); return this.getRenderedComponent(mergedLayout); })}</div>) : null; const { numPages, currentPage, } = this.state; const pageButtons = []; const lastIndex = numPages - 1; let start = currentPage - 2; let end = currentPage; if (start < 0) { end += -start; start = 0; } if (end > lastIndex) { if (start > 0) { start -= (end - lastIndex); if (start < 0) { start = 0; } } end = lastIndex; } if (start > 0) { pageButtons.push(( <li key={0}> <rb.PageButton isActive={currentPage === 1} onClick={()=>this.updateTableData({ pagenum: 1, })} >1 </rb.PageButton> </li> )); pageButtons.push(<li key="dot-before">...</li>); } for (let index = start; index <= end; index += 1) { const inActive = ((index + 1) !== currentPage); if (inActive) { pageButtons.push(( <li key={index}> <rb.PageButton onClick={()=>this.updateTableData({ pagenum: (index + 1), })} >{index + 1}</rb.PageButton> </li> )); } else { pageButtons.push(( <li key={index}> <rb.PageButton color="isPrimary" isActive onClick={() => this.updateTableData({ pagenum: (index + 1), })}> {index + 1} </rb.PageButton> </li> )); } } if (end < lastIndex) { pageButtons.push(<li key="dot-after">...</li>); pageButtons.push(( <li key={lastIndex}> <rb.PageButton onClick={()=>this.updateTableData({ pagenum: (lastIndex + 1), })}> {lastIndex + 1} </rb.PageButton> </li> )); } const showableFilters = this.state.headers.filter(header => this.state.hiddenHeaders.includes(header.sortid) === false); const footer = ( <rb.Pagination> {(this.state.currentPage < 2) ? (<rb.Button state="isDisabled"> Previous </rb.Button>) : (<rb.PageButton onClick={()=>this.updateTableData({ pagenum: (this.state.currentPage - 1), })}>Previous</rb.PageButton>)} <ul> {pageButtons} </ul> {(this.state.currentPage >= this.state.numPages) ? (<rb.Button state="isDisabled"> Next </rb.Button>) : (<rb.PageButton onClick={()=>this.updateTableData({ pagenum: (this.state.currentPage+1), })}>Next</rb.PageButton>)} </rb.Pagination>); var fbts= <span/>; if(this.props.filterSearch){ fbts = <rb.Button style={(this.state.showFilterSearch) ? { background: '#69707a', color: '#f5f7fa', borderColor:'transparent', } : (this.state.filterRowData.length > 0) ? { background: '#222324', color: 'white', borderColor:'transparent', } : undefined} {...this.props.filterButtonProps} onClick={() => { this.toggleAdvancedSearchFilters(); }} >Advanced</rb.Button>; } return ( <rb.Container {...this.props.containerProps}> {(this.props.tableSearch) ? (<rb.Addons {...this.props.filterAddonProps} > <rb.Input {...this.props.filterSearchProps} onChange={(data) => { this.searchFunction({ search: data.target.value, }); this.searchInputTextVal = data.target.value; //TODO: this is janky fix it }} ref={(input) => { this.searchTextInput = input; }} /> <rb.Button {...this.props.searchButtonProps} onClick={() => { this.searchFunction({ search: this.searchInputTextVal, }); }} >Search</rb.Button> {fbts} </rb.Addons>) : null} {(this.state.showFilterSearch) ? <div className="__ra_rt_asf" {...this.props.searchFilterContainerProps}> <rb.Message header="Advanced Search Filters" > <rb.Table {...this.props.searchFilterTableProps}> <rb.Thead> <rb.Tr> <rb.Th >Property</rb.Th> <rb.Th>Filter</rb.Th> <rb.Th>Value</rb.Th> <rb.Th>Options</rb.Th> </rb.Tr> </rb.Thead> <rb.Tbody> {(this.state.filterRowData && this.state.filterRowData.length) ? this.state.filterRowData.map((filterRowDatum, l) => { return <rb.Tr key={l}> <rb.Td>{filterRowDatum.property}</rb.Td> <rb.Td>{filterQuerySelectOptionsMap[filterRowDatum.filter_value]}</rb.Td> <rb.Td>{filterRowDatum.value}</rb.Td> <rb.Td><rb.Button onClick={() => { this.removeFilterRow(l); }}>{'⤫'}</rb.Button></rb.Td> </rb.Tr>; }) : null} </rb.Tbody> <rb.Tfoot> <rb.Tr> <rb.Th> <rb.Select value={this.state.filterRowNewData.property || '__property__'} onChange={(event) => { let text = event.target.value; let name = 'property'; this.updateNewFilterRowText({ name, text, }); }} >{this.filterSelectOptions.map((filter, fp) => { return <option value={filter.value} key={fp} disabled={filter.disabled}>{filter.label}</option>; })}</rb.Select> </rb.Th> <rb.Th> <rb.Select value={this.state.filterRowNewData.filter_value || '__filter__'} onChange={(event) => { let text = event.target.value; let name = 'filter_value'; this.updateNewFilterRowText({ name, text, }); }} >{filterQuerySelectOptions.map((filter, ft) => { return <option value={filter.value} key={ft} disabled={filter.disabled}>{filter.label}</option>; })} </rb.Select> </rb.Th> <rb.Th> <rb.Input value={this.state.filterRowNewData.value || ''} onChange={(event) => { let text = event.target.value; let name = 'value'; this.updateNewFilterRowText({ name, text, }); }} /> </rb.Th> <rb.Th> <rb.Button style={{ width:'100%', }} onClick={() => { this.addFilterRow(); }}>Add filter</rb.Button> </rb.Th> </rb.Tr> </rb.Tfoot> </rb.Table> <rb.Content {...this.props.searchFilterTableNoteProps}> <p><strong>Notes:</strong></p> <ul> <li><strong>Date Values:</strong> For date filters, Moment is used for date filters with the following moment format: YYYY-MM-DDTHH:MM:SS</li> <li><strong>Boolean values</strong> "true" is converted to <em>true</em></li> </ul> <p><strong>Export:</strong></p> <rb.Button icon="fa fa-download" onClick={() => { this.props.fileSaver({ data: this.state.rows, filename: window.location.pathname.replace(/\//gi, '_')+'.json', }); }}>JSON</rb.Button> <rb.Button icon="fa fa-download" onClick={() => { // console.debug('this.state.rows', this.state.rows); json2csv(this.state.rows, (err, csv) => { // console.debug('before csv',csv ); this.props.fileSaver({ data: csv, type:'text/csv;charset=utf-8', filename: window.location.pathname.replace(/\//gi, '_')+'.csv', }); }, { checkSchemaDifferences: false, delimiter: { wrap:'"', }, }); }}>CSV</rb.Button> <rb.Button icon="fa fa-download" onClick={() => { // console.debug('this.state.rows', this.state.rows); let headers = []; this.state.headers.forEach(header => { if (header.sortid) headers.push(header.sortid); }); let filtered_rows = this.state.rows.map(row => { let copy = Object.assign({}, row); Object.keys(copy).forEach(key => { if (headers.indexOf(key) === -1) { delete copy[ key ]; } }); return copy; }); // console.log({ filtered_rows }); json2csv(filtered_rows, (err, csv) => { // console.debug('before csv',csv ); this.props.fileSaver({ data: csv, type:'text/csv;charset=utf-8', filename: window.location.pathname.replace(/\//gi, '_')+'.csv', }); }, { checkSchemaDifferences: false, delimiter: { wrap:'"', }, }); }}>Simple CSV</rb.Button> <hr/> </rb.Content> <rb.Table {...this.props.searchFilterPaginationProps}> <rb.Tbody> <rb.Tr> <rb.Td> <rb.Group> <rb.Label style={filterLabelStyleProps}> Sort by </rb.Label> <rb.Select value={this.state.sortProp || 'createdat'} onChange={(event) => { let text = event.target.value; this.setState({ sortProp: text, }, () => { this.updateTableData({}); }); }} >{this.sortableSelctOptions.map((filter, fp) => { return <option value={filter.value} key={fp} disabled={filter.disabled}>{filter.label}</option>; })} </rb.Select> <rb.Select value={this.state.sortOrder || 'desc'} onChange={(event) => { let text = event.target.value; this.setState({ sortOrder: text, }, () => { this.updateTableData({}); }); }} > <option value="asc">ASC</option> <option value="desc">DESC</option> </rb.Select> </rb.Group> </rb.Td> <rb.Td> <rb.Group> <rb.Label style={filterLabelStyleProps}> Showing </rb.Label> <rb.Select value={this.state.limit} onChange={(event) => { let text = event.target.value; this.setState({ limit: text, }, () => { this.updateTableData({}); }); }} >{( (this.props.includeAllLimits ) ? this.props.numOfLimits.concat([this.state.numItems,]) : this.props.numOfLimits).map((lim, lp) => { return <option value={lim} key={lp} disabled={lim.disabled}>{lim}</option>; })} </rb.Select> <rb.Label style={filterLabelStyleProps}> of {this.state.numItems} rows </rb.Label> </rb.Group> </rb.Td> <rb.Td> <rb.Group> <rb.Label style={filterLabelStyleProps}> Page </rb.Label> <rb.Select value={this.state.currentPage} onChange={(event) => { let text = event.target.value; this.searchFunction({ pagenum: text, }); }} >{([this.state.numPages,].reduce((result, key) => { let usableLimit = (key < 500) ? key : 500; for (let i = 1; i <= usableLimit; i++){ result.push(i); } return result; }, [])).map((lim, lp) => { return <option value={lim} key={lp} disabled={lim===this.state.currentPage}>{lim}</option>; })} </rb.Select> <rb.Label style={filterLabelStyleProps}> of {this.state.numPages} </rb.Label> </rb.Group> </rb.Td> </rb.Tr> </rb.Tbody> </rb.Table> <hr /> <details> <summary> <strong>Hide columns:</strong> </summary> <rb.Columns isMultiline> {this.state.headers.map((header, h) => { return (<rb.Column key={h}> <label><input type="checkbox" checked={this.state.hiddenHeaders.includes(header.sortid) ? 'checked' : ''} onChange={(/*event*/) => { this.toggleHeader({ header, /* event, */ }); }} />{header.label || header.sortid}</label> </rb.Column>); })} </rb.Columns> </details> </rb.Message> </div> : null} <div style={Object.assign({ overflow:'hidden', height:'100%', }, this.props.tableWrappingStyle)}> {(this.state.isLoading) ? (<div style={{ textAlign: 'center', position: 'absolute', height: '80%', width: '100%', opacity: '.9', background: 'white', display: 'flex', alignSelf: 'stretch', justifyContent: 'center', alignItems: 'center', }}> <rb.Button color="isWhite" state="isLoading">Loading</rb.Button> </div>) : null } {(displayRows.length === 0 && this.props.emptyLayout)