var DBG = DBG || false; var DBG1 = true; if (!global.p5VendorJs) throw "Missing p5 Vendor js libs"; // if (!global.p5VendorJs.Redux) throw "Missing p5 Vendor js lib: Redux"; var createReactClass = global.p5VendorJs.createReactClass; var h = global.p5VendorJs.React.createElement; var ReactDOM = global.p5VendorJs.ReactDOM; // var Redux = global.p5VendorJs.Redux; // var ReduxThunk = global.p5VendorJs.ReduxThunk; // var createStoreWithThunkMiddleware = Redux.applyMiddleware(ReduxThunk)(Redux.createStore); // TODO: to vendor.js var baseCellStyle = { height: 26, padding: 4 }; var baseStickyCellStyle = Object.assign({}, baseCellStyle, { position: "absolute", top: "auto", left: "0", 'background-color': "#fff" }); var Defaults = { cellFontSize: 12, rowsPerPage: 10, cellLineHeight: 18, baseCellStyle: baseCellStyle, baseStickyCellStyle: baseStickyCellStyle, } function p5Utils__mapToQuery(map, callback) { var mapCallback = ('function' === typeof callback) ? callback : function (key) { return '' + key + '=' + encodeURIComponent(map.get(key)); }; return Array.from(map.keys()).sort() .map(mapCallback) .join('&') } function p5Utils__mapToQueryWithKeyPrefix(map, keyPrefix, callback) { var mapCallback = ('function' === typeof callback) ? callback : function (key) { return '' + (keyPrefix || '') + key + '=' + encodeURIComponent(map.get(key)); }; return p5Utils__mapToQuery(map, mapCallback) } function p5Utils__objectToQueryWithKeyPrefix(obj, prefix, callback) { if (!obj) return ''; var mapCallback = ('function' === typeof callback) ? callback : function (key) { return '' + key + '=' + encodeURIComponent(obj[key]); }; return Object.keys(obj).sort() .map(mapCallback) .join('&') } var TableAjax_Feature_FunctionsCell = function (props) { DBG && console.warn('DBG::TableAjax_Feature_FunctionsCell::render', { props: props }); return h('div', { style: props.style }, [ (props.primaryKey) ? "F(" + props.primaryKey + ")" : null ]); }; var TableAjax_Feature_SelectedCell = function (props) { DBG && console.warn('DBG::TableAjax_Feature_SelectedCell::render', { props: props }); return h('div', { style: props.style }, [ (props.primaryKey) ? h('input', { type: 'checkbox', title: props.primaryKey }) : null ]); }; var TableAjax_Feature_PrimaryKeyCell = function (props) { return h('div', { style: props.style }, props.primaryKey); }; var TableAjax_Filter_FunctionsCell = function (props) { DBG && console.warn('DBG::TableAjax_Filter_FunctionsCell::render', { props: props }); return h('div', { style: props.style }, [ "(x)" ]); }; var TableAjax_Filter_SelectedCell = function (props) { DBG && console.warn('DBG::TableAjax_Filter_SelectedCell::render', { props: props }); return h('div', { style: props.style }, [ h('input', { type: 'checkbox' }) ]); }; var TableAjax_Filter_PrimaryKeyCell = function (props) { return h('div', { style: props.style }, [ h('input', { type: "text", placeholder: "%", style: { 'box-sizing': "border-box", padding: "0 5px", width: "100%", height: "100%", 'border': "0px", 'border-bottom': "2px solid transparent", 'background-color': "#eee" } }) ]); }; var TableAjax_Label_FunctionsCell = function (props) { DBG && console.warn('DBG::TableAjax_Label_FunctionsCell::render', { props: props }); return h('div', { style: props.style }, [ "(icons)" ]); }; var TableAjax_Label_SelectedCell = function (props) { DBG && console.warn('DBG::TableAjax_Label_SelectedCell::render', { props: props }); return h('div', { style: props.style }, [ h('input', { type: 'checkbox' }) ]); }; var TableAjax_Label_PrimaryKeyCell = function (props) { return h('div', { style: props.style }, props.label); }; var TableAjax_Tbody = createReactClass({ renderDataRow: function (value, rowIdx) { var allColsCount = 13; // TODO: (this.state.cols.length || 0) + 3; var renderRowCell = this.renderRowCell(rowIdx).bind(this); return h('tr', {}, [ this.renderStickyRowCell(rowIdx) ].concat( Array.apply(null, { length: allColsCount }).map(renderRowCell) ) ); }, renderRowCell: function (rowIdx) { var _rowIdx = rowIdx; var _item = this.getItem(rowIdx); return function (value, cellIdx) { var value = (_item) ? Object.values(_item).slice(3)[ cellIdx ] : ''; // TODO: convert item to values Array by fields order - function itemToGridValuesList - defined in parent, passed by prop return h('td', { style: Object.assign({}, Defaults.baseCellStyle, { }) }, value); } }, renderStickyRowCell: function (rowIdx) { var cell1Width = this.props.stickyColWidths[0]; var cell2Width = this.props.stickyColWidths[1]; var cell3Width = this.props.stickyColWidths[2]; var cell1Style = Object.assign({}, Defaults.baseStickyCellStyle, { position: "absolute", top: "auto", left: "0", width: (cell1Width - 1), height: Defaults.baseCellStyle.height - 1 }); var cell2Style = Object.assign({}, Defaults.baseStickyCellStyle, { position: "absolute", top: "auto", left: cell1Width, width: (cell2Width - 1), height: Defaults.baseCellStyle.height - 1 }); var cell3Style = Object.assign({}, Defaults.baseStickyCellStyle, { position: "absolute", top: "auto", left: cell1Width + cell2Width, width: (cell3Width - 1), height: Defaults.baseCellStyle.height - 1 }); var item = this.getItem(rowIdx); var primaryKey = (item) ? item['@primaryKey'] : null; return h('td', { style: Object.assign({}, Defaults.baseCellStyle, { display: "block", position: "absolute", left: 0, top: "auto", width: (cell1Width + cell2Width + cell3Width), padding: 0, 'background-color': "#ddd" }) }, [ h(TableAjax_Feature_FunctionsCell, { style: cell1Style, primaryKey: primaryKey }), h(TableAjax_Feature_SelectedCell, { style: cell2Style, primaryKey: primaryKey }), h(TableAjax_Feature_PrimaryKeyCell, { style: cell3Style, primaryKey: primaryKey }), ]); }, getItem: function (rowIdx) { return (this.props.rows.length > rowIdx) ? this.props.rows[rowIdx] : null; // { primaryKey: rowIdx } // TODO: real data }, shouldComponentUpdate: function (nextProps, nextState) { // receivedRequestId var shouldUpdate = false; if (nextProps.receivedRequestId > this.props.receivedRequestId) shouldUpdate = true; if (nextProps.rowsPerPage != this.props.rowsPerPage) shouldUpdate = true; else if (nextProps.stickyColWidths.join(',') != this.props.stickyColWidths.join(',')) shouldUpdate = true; DBG && console.log("DBG:TableAjax_Tbody:shouldComponentUpdate", { shouldUpdate, props: this.props, nextProps, state: this.state, nextState }); return shouldUpdate; }, render: function () { DBG && console.log("DBG:TableAjax_Tbody:render"); return h('tbody', {}, [ Array.apply(null, { length: this.props.rowsPerPage }).map(this.renderDataRow) ]); } }); // p5UI__TableAjax.props.dataStore: priv.options.tableDataStore, // p5UI__TableAjax.props.dataActions: priv.options.tableDataActions, // p5UI__TableAjax.props.filterStore: priv.options.filterStore, // p5UI__TableAjax.props.filterActions: priv.options.filterActions, // p5UI__TableAjax.props.selectedStore: priv.options.selectedStore, // p5UI__TableAjax.props.selectedActions: priv.options.selectedActions, var p5UI__TableAjax = createReactClass({ // props.namespace // props.width - table max width // @doc: element.scrollLeft = intValue; // set scroll left for dom element // TODO: stickyCols = [ rowFunctions, selectFeature, primaryKey, ...custom fields ] _refContentEl: null, setContentElRef: function (el) { this._refContentEl = el; }, _getStateFromDataStore: function () { var state = this.props.dataStore.getState(); DBG && console.log('DBG::P5UI__TableAjax::_getStateFromDataStore', { state: state }); return { width: state.width, rowsPerPage: state.rowsPerPage || Defaults.rowsPerPage, isLoading: state.isLoading, sentRequestId: state.sentRequestId, receivedRequestId: state.receivedRequestId, rows: state.rows }; }, getItem: function (rowIdx) { return (this.state.rows.length > rowIdx) ? this.state.rows[rowIdx] : null; // { primaryKey: rowIdx } // TODO: real data }, getInitialState: function () { var cols = Array.apply(null, { length: 13 }).map(function (undefinedValue, cellIdx) { var label = "Col("+cellIdx+")" + ( cellIdx % 2 ? "
Col line 2..." : "" ); return label; }); var baseCellStyle = { height: 26, padding: 4 }; var baseStickyCellStyle = Object.assign({}, baseCellStyle, { position: "absolute", top: "auto", left: "0", 'background-color': "#fff" }); return Object.assign({ isLoading: false, filter: null, cols: cols, data: [], rowsPerPage: 10, baseCellStyle: baseCellStyle, baseStickyCellStyle: baseStickyCellStyle, stickyColWidths: [ 50, 25, 75 // 63 ] }, this._getStateFromDataStore()); }, componentDidMount: function () { DBG && console.log('DBG::P5UI__TableAjax::componentDidMount'); this._unsubscribeDataStore = this.props.dataStore.subscribe(this._dataStoreUpdated) this._unsubscribeFilterStore = this.props.filterStore.subscribe(this._filterStoreUpdated) }, componentWillUnmount: function () { this._unsubscribeDataStore() this._unsubscribeFilterStore() }, _dataStoreUpdated: function () { DBG && console.log('DBG::P5UI__TableAjax::_dataStoreUpdated'); this.setState(this._getStateFromDataStore()) }, _filterStoreUpdated: function () { DBG && console.log('DBG::P5UI__TableAjax::_filterStoreUpdated'); var curFilterState = this._getStateFromFilterStore(); var needFetchData = ( !this.state.filter || curFilterState.filterQuery !== this.state.filter.filterQuery || curFilterState.specialFilterQuery !== this.state.filter.specialFilterQuery || curFilterState.currSortCol !== this.state.filter.currSortCol || curFilterState.currSortFlip !== this.state.filter.currSortFlip ); if (needFetchData) { this.setState({ filter: curFilterState }) this.props.dataStore.dispatch(this.props.dataActions.loadData(this.props.namespace, curFilterState)) } }, _getStateFromFilterStore: function () { var curFilterState = this.props.filterStore.getState(); return { filterQuery: p5Utils__mapToQueryWithKeyPrefix(curFilterState.filter, 'f_'), specialFilterQuery: p5Utils__mapToQueryWithKeyPrefix(curFilterState.specialFilter, 'sf_'), currSortCol: curFilterState.currSortCol, currSortFlip: curFilterState.currSortFlip }; }, shouldComponentUpdate: function (nextProps, nextState) { DBG && console.log('DBG::P5UI__TableAjax::shouldComponentUpdate - TODO only when data changed?', { props: this.props, nextProps, state: this.state, nextState }); return true; // var dataChanged = true; // TODO compare this.state.rows with nextState.rows array // var getPk = function (item) { // return item['@primaryKey']; // } // var listPks = this.state.rows.map(getPk).join(','); // var prevPks = nextState.rows.map(getPk).join(','); // DBG && console.log('DBG::P5UI__TableAjax::shouldComponentUpdate', { state: this.state, nextState, listPks, prevPks }); // return ( // this.state.isLoading !== nextState.isLoading // || this.state.width !== nextState.width // || dataChanged // ); }, getTheadCellHeight: function () { var maxLines = this.state.cols.reduce(function (ret, cell) { var label = cell; return Math.max(ret, label.replace('
', '###').replace('
', '###').split("###").length); }, 1) return 2 * 2 + maxLines * Defaults.cellLineHeight; }, renderTheadColNameRowCell: function (value, cellIdx) { var label = this.state.cols[cellIdx]; return h('th', { style: { padding: "2px 120px", 'white-space': 'nowrap', 'line-height': Defaults.cellLineHeight+"px", 'font-size': Defaults.cellFontSize+"px", 'border-bottom-width': "1px" } }, label.replace('
', '###
###').replace('
', '###
###').split("###").map(function (txtOrBr) { return ('
' === txtOrBr) ? h('br') : txtOrBr; })); }, renderTheadFilterRowCell: function (value, cellIdx) { return h('td', { style: { padding: 0 } }, [ // h('input', { type: "text", placeholder: "%", style: { 'box-sizing': "border-box", width: "100%", height: "26px", padding: "4px 5px 0 5px", 'font-size': "12px", 'border': 0, 'border-bottom': "2px solid transparent", 'background-color': "#eee" } }) ]); }, renderStickyTheadNameRowCell: function (item) { var headerCellHeight = this.getTheadCellHeight(); var cell1Width = this.state.stickyColWidths[0]; var cell2Width = this.state.stickyColWidths[1]; var cell3Width = this.state.stickyColWidths[2]; var cell1Style = Object.assign({}, this.state.baseStickyCellStyle, { position: "absolute", top: "auto", left: "0", width: (cell1Width - 1), height: headerCellHeight }); var cell2Style = Object.assign({}, this.state.baseStickyCellStyle, { position: "absolute", top: "auto", left: cell1Width, width: (cell2Width - 1), height: headerCellHeight }); var cell3Style = Object.assign({}, this.state.baseStickyCellStyle, { position: "absolute", top: "auto", left: cell1Width + cell2Width, width: (cell3Width - 1), height: headerCellHeight }); var label = 'Nr'; // TODO: get primaryKey cell label return h('td', { style: Object.assign({}, this.state.baseCellStyle, { display: "block", position: "absolute", left: 0, top: "auto", width: (cell1Width + cell2Width + cell3Width), height: headerCellHeight, padding: 0, 'background-color': "#ddd" }) }, [ h(TableAjax_Label_FunctionsCell, { style: cell1Style }), h(TableAjax_Label_SelectedCell, { style: cell2Style }), h(TableAjax_Label_PrimaryKeyCell, { style: cell3Style, label: label }), ]); }, renderStickyTheadFilterRowCell: function (item) { var cell1Width = this.state.stickyColWidths[0]; var cell2Width = this.state.stickyColWidths[1]; var cell3Width = this.state.stickyColWidths[2]; var cell1Style = Object.assign({}, this.state.baseStickyCellStyle, { position: "absolute", top: "auto", left: "0", width: (cell1Width - 1) }); var cell2Style = Object.assign({}, this.state.baseStickyCellStyle, { position: "absolute", top: "auto", left: cell1Width, width: (cell2Width - 1) }); var cell3Style = Object.assign({}, this.state.baseStickyCellStyle, { position: "absolute", top: "auto", left: cell1Width + cell2Width, width: (cell3Width - 1), padding: "none" }); return h('td', { style: Object.assign({}, this.state.baseCellStyle, { display: "block", position: "absolute", left: 0, top: "auto", width: (cell1Width + cell2Width + cell3Width), height: this.state.baseCellStyle.height + 2, padding: 0, 'background-color': "#ddd" }) }, [ h(TableAjax_Filter_FunctionsCell, { style: cell1Style }), h(TableAjax_Filter_SelectedCell, { style: cell2Style }), h(TableAjax_Filter_PrimaryKeyCell, { style: cell3Style }), ]); }, render: function () { DBG && console.log('DBG::P5UI__TableAjax::render', { state: this.state }); var baseStyle = { 'border-top': "1px solid red", 'border-bottom': "1px solid red", 'min-height': "100px" } // TODO: DBG var allColsCount = 13; // TODO: (this.state.cols.length || 0) + 3; var widthTotal = this.state.width || 1200; var widthStickyCols = this.state.stickyColWidths.reduce(function (a, b) { return a + b; }, 0); var widthScrollableContent = widthTotal - widthStickyCols - 2; return h('div', { className: "p5UI__TableAjax", style: Object.assign(baseStyle, { 'background-color': "#ddd", }) }, [ h('div', { style: { 'background-color': "#f00", color: "#fff", padding: "3px 12px" } }, "namespace: '" + this.props.namespace + "' getTheadCellHeight("+this.getTheadCellHeight()+")"), h('div', { ref: this.setContentElRef, style: { 'margin-left': widthStickyCols + 2, // 'min-height': "300px", 'overflow': "scroll visible", 'padding-bottom': "1px", 'clear': "both", 'width': widthScrollableContent, 'background-color': "#fff", } }, [ h('table', { className: "table table-bordered table-condensed", style: { 'margin-bottom': 0, 'margin-left': "-2px", } }, [ h('thead', {}, [ h('tr', {}, [ this.renderStickyTheadNameRowCell() ].concat( Array.apply(null, { length: allColsCount }).map(this.renderTheadColNameRowCell) )), h('tr', {}, [ this.renderStickyTheadFilterRowCell() ].concat( Array.apply(null, { length: allColsCount }).map(this.renderTheadFilterRowCell) )) ]), h(TableAjax_Tbody, { rowsPerPage: this.state.rowsPerPage, receivedRequestId: this.state.receivedRequestId, rows: this.state.rows, stickyColWidths: this.state.stickyColWidths, // store: this.props.dataStore }) ]) ]), h('hr', { style: { border: "5px solid red" } }), h('pre', { style: { padding: "6px 12px", border: "1px solid red", 'background-color': "#eee" } }, [ "State: ", (this.state.isLoading) ? " loading... " : null, ]), h('div', { style: { padding: "6px 12px", border: "1px solid red", 'background-color': "#eee" } }, [ "TEST btns: ", h('button', { onClick: this._testScrollContentTo100Left }, "scroll 100"), h('button', { onClick: this._testScrollContentTo200Left }, "scroll 200"), h('button', { onClick: this._testScrollContentTo0Left }, "scroll 0"), h('button', { onClick: this._testLoadData }, "load data"), ]) ]); }, _testLoadData: function () { this.props.dataStore.dispatch(this.props.dataActions.loadData(this.props.namespace)); }, _testScrollContentTo100Left: function () { this._refContentEl.scrollLeft = 100; }, _testScrollContentTo200Left: function () { this._refContentEl.scrollLeft = 200; }, _testScrollContentTo0Left: function () { this._refContentEl.scrollLeft = 0; } }); global.p5VendorJs['p5UI__TableAjax'] = p5UI__TableAjax; // export default p5UI__TableAjax