Procházet zdrojové kódy

Fixed bug in TableAjax filter; + default cell format for p5:string in TableAjax

Piotr Labudda před 7 roky
rodič
revize
d69a2d4f3b

+ 9 - 1
SE/se-lib/TableAjax.php

@@ -419,9 +419,15 @@ class TableAjax extends ViewAjax {
 		UI::inlineJS(APP_PATH_WWW . '/static/p5UI/FieldCheckboxSearch.js', []); // p5UI__FieldCheckboxSearch
 		UI::inlineCSS(APP_PATH_WWW . '/static/p5UI/FieldCheckboxLoading.css');
 		UI::inlineJS(APP_PATH_WWW . '/static/p5UI/FieldCheckboxLoading.js', []); // p5UI__FieldCheckboxLoading
+		// echo UI::h('style', [ 'type' => "text/css" ], "
+		// 	.stickyCol1 { display:none }
+		// ");
 		UI::inlineJS(__FILE__ . '.createTableFiltersStateObject.js', [
 			'DBG' => ('1' === V::get('DBG_JS', '', $_GET)),
 		]);
+		UI::inlineJS(__FILE__ . '.p5UI__TableAjax.js', [
+			'DBG' => ('1' === V::get('DBG_JS', '', $_GET)),
+		]);
 		UI::inlineJS(__FILE__ . '.TableAjax.js', [
 			'URI_BASE' => Request::getPathUri(),
 			'URI_WPS' => Request::getPathUri() . 'wps.php',
@@ -460,7 +466,7 @@ class TableAjax extends ViewAjax {
 		$specialFilters = (method_exists($acl, 'getSpecialFilters')) ? $acl->getSpecialFilters() : null;
 		$className = __CLASS__;
 		UI::inlineJS(__FILE__ . '.init.js', [
-			'GUI_SHOW_CHECKBOXES' => true, // ('1' === V::get('TEST_CHECKBOX', '', $_GET)),
+			'GUI_SHOW_CHECKBOXES' => 1, // ('1' === V::get('TEST_CHECKBOX', '', $_GET)),
 			'CHECKBOX_ID_CONTEXT' => V::get('TEST_CHECKBOX_CONTEXT', '', $_GET), // TODO: test generate @selected context
 			'TABLE_AJAX_NODE_ID' => $this->_htmlID,
 			'NAMESPACE' => $acl->getNamespace(),
@@ -2148,7 +2154,9 @@ jQuery(document).ready(function(){
 							} break;
 							case 'string': {
 								$columnConfig->type = 'p5:string';
+								$columnConfig->format = $acl->getXsdFieldParam($col, 'format');
 								$columnConfig->formatByValue = $acl->getXsdFieldParam($col, 'formatByValue');
+								$columnConfig->formatEmpty = $acl->getXsdFieldParam($col, 'formatEmpty');
 								if ($aliasMap = $acl->getXsdFieldParam($col, 'aliasMap')) $columnConfig->aliasMap = $aliasMap;
 							} break;
 						}

+ 106 - 81
SE/se-lib/TableAjax.php.TableAjax.js

@@ -17,6 +17,7 @@ var ReduxThunk = global.p5VendorJs.ReduxThunk;
 var createStoreWithThunkMiddleware = Redux.applyMiddleware(ReduxThunk)(Redux.createStore); // TODO: to vendor.js
 var p5UI__FieldCheckboxSearch = global.p5VendorJs['p5UI__FieldCheckboxSearch'];
 var p5UI__FieldCheckboxLoading = global.p5VendorJs['p5UI__FieldCheckboxLoading'];
+var p5UI__TableAjax = global.p5VendorJs['p5UI__TableAjax'];
 
 if (!String.prototype.startsWith) { // TODO: to global js utils
 	String.prototype.startsWith = function(search, pos) {
@@ -224,6 +225,7 @@ function selectedActions(idContext) {
 		return { type: 'SET_ERROR_MSG', errorMsg: errorMsg }
 	}
 	function toggle(namespace, primaryKey, checked) {
+		DBG && console.log('DBG::toggle action: (pks:['+primaryKey+'])...');
 		if ('select-all' === primaryKey) return toggleAll(namespace, checked);
 
 		return function(dispatch, getState) {
@@ -1144,6 +1146,7 @@ var TableAjax = function() {
 		mapEditorContainer: 'window' // 'window' or 'dock'
 	};
 
+	var _reactUITableNode; // TODO: react table node
 	var _uiNodeCont; // container holding table
 	var _uiNodeSelectedInfo;
 	var _uiNodeSpecialFilters;
@@ -1179,7 +1182,7 @@ var TableAjax = function() {
 	 initialize the plugin.
 	 */
 	priv.init = function() {
-		_uiNodeCont = priv.options.id;
+		_uiNodeCont = priv.options.id; // <div id="{_htmlID}" style="..."><table class="AjaxTable...
 		_state = {};// init state
 		_state.page = 1;
 		_state.specialFilters = {};
@@ -1188,6 +1191,7 @@ var TableAjax = function() {
 			jQuery(window).on('resize', priv.onWindowResize)
 		}
 		priv.initialRender();// set up _uiNode$...
+		// priv.initialReactRender(); // set up _reactUITableNode
 		priv.options.types.string = ((priv.options.types || {}).string || {});
 		priv.options.types.number = ((priv.options.types || {}).number || {});
 		priv.options.types.bool = ((priv.options.types || {}).bool || {});
@@ -1270,6 +1274,10 @@ var TableAjax = function() {
 				priv.options.router.apply(this);
 			});
 		}
+
+		if (_reactUITableNode) {
+
+		}
 	};
 
 	priv.onRender = function(e) {
@@ -1331,11 +1339,77 @@ var TableAjax = function() {
 			jQuery(_uiNodeCont).prev('.doubleScroll-scroll-wrapper').css({display:'none'})
 		}
 	}
+	priv.uiFixStickyCols = function () {
+		var stickyCol1Width = priv.getStickyCol1Width();
+		var stickyCol2Width = priv.getStickyCol2Width();
+		var stickyCol3Width = priv.getStickyCol3Width();
+		if (undefined === _tableWidth) {
+			_tableWidth = true;
+			var contW = jQuery(_uiNodeCont).width();
+			var th1 = _uiNode$Table.find('.stickyCol1:first');
+			var th2 = th1.next();
+			var th1W = th1.innerWidth();
+			var th2W = 50 + 2 * 5;//th2.innerWidth();
+			// var colsW = stickyCol1Width + stickyCol2Width + stickyCol3Width;
+			var colsW = priv.getStickyColsSumWidth();
+			jQuery(_uiNodeCont).css({
+				width: '' + (contW - colsW) + 'px',
+				marginLeft: '' + colsW + 'px',
+				minHeight:  '360px',
+				overflowX: 'scroll',
+				overflowY: 'visible',
+				paddingBottom: '1px',
+				clear: 'both',
+			});
+		}
+		_uiNode$Table.find('.stickyCol1').css({
+			position: 'absolute',
+			left: '0',
+			top: 'auto',
+			width: '' + (stickyCol1Width) + 'px',
+		});
+		//_uiNode$Table.find('.filter').find('.stickyCol1').css({height:'34px'});
+		var sortStickyColHeight = _uiNode$Table.find('.sort').find('th:last').outerHeight();
+		_uiNode$Table.find('.sort').find('.stickyCol').css({height: sortStickyColHeight + 'px'});
+		var filterStickyColHeight = _uiNode$Table.find('.filter').find('th:last').outerHeight() + 1;
+		_uiNode$Table.find('.filter').find('.stickyCol').css({height: filterStickyColHeight + 'px'});
+		if (priv.options.checkboxes) _uiNode$Table.find('.stickyCol2').css({
+			position: 'absolute',
+			left: '' + (stickyCol1Width) + 'px',
+			top: 'auto',
+			width: '' + (stickyCol2Width) + 'px',
+			borderRight: '1px solid silver',
+			overflow: 'hidden'
+		});
+		_uiNode$Table.find('.stickyCol3').css({
+			position: 'absolute',
+			left: '' + (stickyCol1Width + stickyCol2Width) + 'px',
+			top: 'auto',
+			width: '' + (stickyCol3Width) + 'px',
+			borderRight: '1px solid silver',
+			overflow: 'hidden'
+		});
+		_uiNode$Table.find('tbody').find('.stickyCol').css({
+			height: '' + (29 + 1) + 'px',
+		});
+	};
 	priv.getStickyCol1Width = function () { return (12 + 3) * 4 + 2 * 5 + 1; }
 	priv.getStickyCol2Width = function () { return (priv.options.checkboxes) ? 20 + 2 * 5 + 1 : 0; }
 	priv.getStickyCol3Width = function () { return 50 + 2 * 5 + 1; }
 	priv.getStickyColsSumWidth = function () { return priv.getStickyCol1Width() + priv.getStickyCol2Width() + priv.getStickyCol3Width(); }
 
+	priv.initialReactRender = function () { // TODO: render table as React node
+		_reactUITableNode = document.createElement('div')
+		_reactUITableNode.setAttribute('class', "TableAjax-reactUITableNode");
+		_uiNodeCont.parentNode.appendChild(_reactUITableNode);
+		ReactDOM.render(
+			h(p5UI__TableAjax, {
+				namespace: priv.options.namespace
+			}),
+			_reactUITableNode
+		);
+	};
+
 	priv.initialRender = function() {
 		_uiNode$Table = $('<table class="AjaxTable table table-striped table-hover table-bordered table-condensed"></table>').appendTo(_uiNodeCont);
 			_head = $('<thead></thead>').prependTo(_uiNode$Table);
@@ -1404,8 +1478,11 @@ var TableAjax = function() {
 			_headSpecialFilter = true;
 		}
 		if (!_body) {
-			_uiNode$Table.find('tbody').remove();
-			_bodyNode = $('<tbody></tbody>').insertAfter(_head);
+			DBG && console.log('DBG:renderTable !_body', { _bodyNode })
+			// _uiNode$Table.find('tbody').remove();
+			// _bodyNode = $('<tbody></tbody>').insertAfter(_head);
+			// _uiNode$Table.find('tbody > tr').remove();
+			_bodyNode.find('tr').remove();
 
 			// find out what rows to show next...
 			var rowsAdded = 0;
@@ -1439,57 +1516,7 @@ var TableAjax = function() {
 		}
 		_foot = $(_uiNodeCont).next('.foot');
 
-		var stickyCol1Width = priv.getStickyCol1Width();
-		var stickyCol2Width = priv.getStickyCol2Width();
-		var stickyCol3Width = priv.getStickyCol3Width();
-		if (undefined === _tableWidth) {
-			_tableWidth = true;
-			var contW = jQuery(_uiNodeCont).width();
-			var th1 = _uiNode$Table.find('.stickyCol1:first');
-			var th2 = th1.next();
-			var th1W = th1.innerWidth();
-			var th2W = 50 + 2 * 5;//th2.innerWidth();
-			var colsW = stickyCol1Width + stickyCol2Width + stickyCol3Width;
-			jQuery(_uiNodeCont).css({
-				width: '' + (contW - colsW) + 'px',
-				marginLeft: '' + colsW + 'px',
-				minHeight:  '360px',
-				overflowX: 'scroll',
-				overflowY: 'visible',
-				paddingBottom: '1px',
-				clear: 'both',
-			});
-		}
-		_uiNode$Table.find('.stickyCol1').css({
-			position: 'absolute',
-			left: '0',
-			top: 'auto',
-			width: '' + stickyCol1Width + 'px',
-		});
-		//_uiNode$Table.find('.filter').find('.stickyCol1').css({height:'34px'});
-		var sortStickyColHeight = _uiNode$Table.find('.sort').find('th:last').outerHeight();
-		_uiNode$Table.find('.sort').find('.stickyCol').css({height: sortStickyColHeight + 'px'});
-		var filterStickyColHeight = _uiNode$Table.find('.filter').find('th:last').outerHeight() + 1;
-		_uiNode$Table.find('.filter').find('.stickyCol').css({height: filterStickyColHeight + 'px'});
-		if (priv.options.checkboxes) _uiNode$Table.find('.stickyCol2').css({
-			position: 'absolute',
-			left: '' + (stickyCol1Width) + 'px',
-			top: 'auto',
-			width: '' + stickyCol2Width + 'px',
-			borderRight: '1px solid silver',
-			overflow: 'hidden'
-		});
-		_uiNode$Table.find('.stickyCol3').css({
-			position: 'absolute',
-			left: '' + (stickyCol1Width + stickyCol2Width) + 'px',
-			top: 'auto',
-			width: '' + stickyCol3Width + 'px',
-			borderRight: '1px solid silver',
-			overflow: 'hidden'
-		});
-		_uiNode$Table.find('tbody').find('.stickyCol').css({
-			height: '' + (29 + 1) + 'px',
-		});
+		priv.uiFixStickyCols();
 
 		if (_data.total == 0 && priv.options.hidePagerOnEmpty) {
 			$('.btn-toolbar', _foot).remove();
@@ -1592,22 +1619,31 @@ var TableAjax = function() {
 				fieldWidget = (function(fldName, fieldProps) {
 					var _fieldName = fldName;
 					var _fieldProps = fieldProps;
+					var _formatDefault = _.get(_fieldProps, 'format', null);
 					var _formatByValue = _.get(_fieldProps, 'formatByValue', null);
+					var _formatEmpty = _.get(_fieldProps, 'formatEmpty', null);
 					var _aliasMap = _.get(_fieldProps, 'aliasMap', null);
 					return function(val, fieldPK, row) {
+						var outValue = '';
 						if (_formatByValue && val in _formatByValue) {
-							val = p5Utils__format(_formatByValue[val], [val])
-							// console.log('p5:string value', val, '_aliasMap', _aliasMap)
-							if (_aliasMap) {
-								Object.keys(_aliasMap).map(function (mapKey) {
-									var mapField = _aliasMap[mapKey]
-									if (undefined !== row[mapField]) {
-										val = val.replace(new RegExp('\{' + mapKey + '\}', 'g'), row[mapField]);
-									}
-								})
-							}
+							outValue = p5Utils__format(_formatByValue[val], [val])
 						}
-						return val;
+						else if (val && _formatDefault) {
+							outValue = p5Utils__format(_formatDefault, [val])
+						}
+						else if (!val && _formatEmpty) {
+							outValue = p5Utils__format(_formatEmpty, [])
+						}
+						// console.log('p5:string value', val, '_aliasMap', _aliasMap)
+						if (_aliasMap) {
+							Object.keys(_aliasMap).map(function (mapKey) {
+								var mapField = _aliasMap[mapKey]
+								if (undefined !== row[mapField]) {
+									outValue = outValue.replace(new RegExp('\{' + mapKey + '\}', 'g'), row[mapField]);
+								}
+							})
+						}
+						return outValue;
 					}
 				}(fldName, fieldProps));
 				break;
@@ -1805,12 +1841,11 @@ var TableAjax = function() {
 	};
 
 	priv.renderRow = function(props) {
-		var rowNode = $('<tr></tr>'),
-				rowPK = (_state.primaryKey in props) ? props[_state.primaryKey] : null,
-				cellNode,
-				columnName, columnProps, val, cellCnt, format, showTooltip, fldWidgetNode,
-				dbg = (priv.options.debug || DBG)
-		;
+		var rowNode = $('<tr></tr>');
+		var rowPK = (_state.primaryKey in props) ? props[_state.primaryKey] : null;
+		var cellNode;
+		var columnName, columnProps, val, cellCnt, format, showTooltip, fldWidgetNode;
+		var dbg = (priv.options.debug || DBG);
 		//  ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event);"
 		rowNode.attr('ondrop', "p5TA_onDrop(event, '"+rowPK+"', '"+priv.options.namespace+"')")
 		rowNode.attr('ondragover', "p5TA_onDragOver(event, this, '"+rowPK+"', '"+priv.options.namespace+"')")
@@ -2132,15 +2167,6 @@ var TableAjax = function() {
 
 		if (_state.primaryKey && priv.options.checkboxes) {
 			var cellNode = $('<td class="stickyCol stickyCol2" style="padding:3px 5px"></td>').appendTo(rowNode);
-			// ReactDOM.render(
-			// 	h(P5UI__TableAjaxRowCheckbox, {
-			// 		disabled: true,
-			// 		primaryKey: 'empty',
-			// 		store: priv.options.selectedStore,
-			// 		actions: priv.options.selectedActions
-			// 	}),
-			// 	cellNode.get(0)
-			// );
 		}
 		for (var i = 0; i < _state.colsSorted.length; i++) {
 			var columnName = _state.colsSorted[i],
@@ -2765,7 +2791,6 @@ var TableAjax = function() {
 			if (state.data.cols && Object.keys(state.data.cols).length > 0) {
 				priv.setStateCols(state.data.cols, state.data.primaryKey);
 				priv.setStateData(state.data);
-				renderParts['head'] = true;
 			} else {
 				priv.setStateData(state.data);
 			}

+ 114 - 0
SE/se-lib/TableAjax.php.p5UI__TableAjax.js

@@ -0,0 +1,114 @@
+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 cellFontSize = 12;
+var cellLineHeight = 18;
+
+var p5UI__TableAjax = createReactClass({
+	getInitialState: function () {
+		var cols = Array.apply(null, { length: 13 }).map(function (undefinedValue, cellIdx) {
+			var label = "Col("+cellIdx+")" + ( cellIdx % 2 ? "<br>Col line 2..." : "" );
+			return label;
+		});
+		return {
+			isLoading: false,
+			cols: cols,
+			data: [],
+			rowsPerPage: 10,
+		};
+	},
+	getTheadCellHeight: function () {
+		var maxLines = this.state.cols.reduce(function (ret, cell) {
+			var label = cell;
+			return Math.max(ret, label.replace('<br>', '###').replace('<br/>', '###').split("###").length);
+		}, 1)
+		return 2 * 2 + maxLines * cellLineHeight;
+	},
+	renderRowCell: function (rowIdx) {
+		var _rowIdx = rowIdx;
+		return function (value, cellIdx) {
+			return h('td', {}, "("+_rowIdx+"/"+cellIdx+")");
+		}
+	},
+	renderTheadColNameRowCell: function (value, cellIdx) {
+		var label = this.state.cols[cellIdx];
+		return h('th', {
+			style: {
+				padding: "2px 120px",
+				'white-space': 'nowrap',
+				'line-height': cellLineHeight+"px",
+				'font-size': cellFontSize+"px",
+				'border-bottom-width': "1px"
+			}
+		}, label.replace('<br>', '###<br>###').replace('<br/>', '###<br>###').split("###").map(function (txtOrBr) {
+			return ('<br>' === txtOrBr) ? h('br') : txtOrBr;
+		}));
+	},
+	renderTheadFilterRowCell: function (value, cellIdx) {
+		return h('td', { style: { padding: 0 } }, [
+			// <input class="filter" placeholder="%" type="text" value="" style="box-sizing: border-box; width: 100%; height: 21px; border-top: 0px; border-bottom: 2px solid transparent;">
+			h('input', {
+				type: "text",
+				placeholder: "%",
+				style: {
+					'box-sizing': "border-box",
+					padding: "0 5px",
+					width: "100%",
+					height: "21px",
+					'border': "0px",
+					'border-bottom': "2px solid transparent",
+					'background-color': "#eee"
+				}
+			})
+		]);
+	},
+	renderRow: function (value, rowIdx) {
+		var allColsCount = 13; // TODO: (this.state.cols.length || 0) + 3;
+		var renderRowCell = this.renderRowCell(rowIdx);
+		return h('tr', {}, Array.apply(null, { length: allColsCount }).map(renderRowCell));
+	},
+	render: function () {
+		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;
+
+		return h('div', {
+			className: "p5UI__TableAjax",
+			style: Object.assign(baseStyle, {
+			})
+		}, [
+			h('div', { style: { 'background-color': "#f00", color: "#fff", padding: "3px 12px" } }, "namespace: '" + this.props.namespace + "' getTheadCellHeight("+this.getTheadCellHeight()+")"),
+			h('div', {
+				style: {
+					'margin-left': "163px",
+					'min-height': "360px",
+					'overflow': "scroll visible",
+					'padding-bottom': "1px",
+					'clear': "both",
+					'width': "1187px",
+				}
+			}, [
+				h('table', {
+					className: "table table-bordered table-condensed",
+				}, [
+					h('thead', {}, [
+						h('tr', {}, Array.apply(null, { length: allColsCount }).map(this.renderTheadColNameRowCell)),
+						h('tr', {}, Array.apply(null, { length: allColsCount }).map(this.renderTheadFilterRowCell))
+					]),
+					h('tbody', {}, [
+						Array.apply(null, { length: this.state.rowsPerPage }).map(this.renderRow)
+					])
+				])
+			])
+		]);
+	}
+});
+
+global.p5VendorJs['p5UI__TableAjax'] = p5UI__TableAjax;