Ver código fonte

added test TableAjax checkboxes

Piotr Labudda 8 anos atrás
pai
commit
8bd124070d

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

@@ -420,6 +420,7 @@ class TableAjax extends ViewAjax {
 		UI::inlineJS(APP_PATH_WWW . '/static/p5UI/FieldCheckboxLoading.js', []); // p5UI__FieldCheckboxLoading
 		UI::inlineJS(__FILE__ . '.TableAjax.js', [
 			'URI_BASE' => Request::getPathUri(),
+			'DBG' => ('1' === V::get('DBG_JS', '', $_GET)),
 		]);
 		$filterInit = $this->_filterInit;
 		$forceFilterInit = $this->_forceFilterInit;

+ 346 - 124
SE/se-lib/TableAjax.php.TableAjax.js

@@ -1,6 +1,226 @@
 if (!URI_BASE) throw "Missing URI_BASE"; // => Request::getPathUri()
 if (!global.jQuery) throw "Missing jQuery"
 var $ = global.jQuery
+var DBG = DBG || false;
+
+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 p5UI__FieldCheckboxSearch = global.p5VendorJs['p5UI__FieldCheckboxSearch'];
+var p5UI__FieldCheckboxLoading = global.p5VendorJs['p5UI__FieldCheckboxLoading'];
+
+
+function p5Utils__arrayRemove(array, element) {
+	return array.filter(e => e !== element);
+}
+
+function selectedStore(state, action) { // TODO: fetch from ajax on init, and on change rows in table
+	var prevState = state || { isLoading: false, selected: [], loading: [], listPrimaryKeys: [] };
+	DBG && console.log('DBG::selectedStore', { prevState, action });
+	switch (action.type) {
+		case 'SET_CHECKED': return Object.assign(prevState, {
+			loading: p5Utils__arrayRemove(prevState.loading, action.primaryKey),
+			selected: prevState.selected.concat(action.primaryKey)
+		});
+		case 'SET_UNCHECKED': return Object.assign(prevState, {
+			loading: p5Utils__arrayRemove(prevState.loading, action.primaryKey),
+			selected: p5Utils__arrayRemove(prevState.selected, action.primaryKey)
+		});
+		case 'SET_LIST_CHECKED': return Object.assign(prevState, {
+			loading: [], // TODO: p5Utils__arrayExcept(prevState.loading, action.listPrimaryKeys),
+			selected: [].concat(action.listPrimaryKeys), // TODO: p5Utils__arrayUniqueUnion(prevState.selected, action.listPrimaryKeys)
+		});
+		case 'SET_LIST_UNCHECKED': return Object.assign(prevState, {
+			loading: [], // TODO: p5Utils__arrayExcept(prevState.loading, action.listPrimaryKeys),
+			selected: [], // TODO: p5Utils__arrayUniqueUnion(prevState.selected, action.listPrimaryKeys)
+		});
+		case 'SET_LOADING': return Object.assign(prevState, {
+			loading: prevState.loading.concat(action.primaryKey)
+		});
+		case 'SET_LIST_LOADING': return Object.assign(prevState, {
+			loading: [].concat(action.listPrimaryKeys)
+		});
+		case 'SET_PRIMARY_KEYS': return Object.assign(prevState, {
+			// TODO: remove from loading and selected pks which are not in action.listPrimaryKeys, exclude 'select-all'
+			listPrimaryKeys: [].concat(action.listPrimaryKeys),
+		});
+		default: return prevState;
+	}
+}
+function selectedActions() {
+	function setChecked(primaryKey) {
+		return { type: 'SET_CHECKED', primaryKey: primaryKey }
+	}
+	function setUnchecked(primaryKey) {
+		return { type: 'SET_UNCHECKED', primaryKey: primaryKey }
+	}
+	function setLoading(primaryKey) {
+		return { type: 'SET_LOADING', primaryKey: primaryKey }
+	}
+	function setListChecked(listPrimaryKeys) {
+		return { type: 'SET_LIST_CHECKED', listPrimaryKeys: listPrimaryKeys }
+	}
+	function setListUnchecked(listPrimaryKeys) {
+		return { type: 'SET_LIST_UNCHECKED', listPrimaryKeys: listPrimaryKeys }
+	}
+	function setListLoading(listPrimaryKeys) {
+		return { type: 'SET_LIST_LOADING', listPrimaryKeys: listPrimaryKeys }
+	}
+	function toggle(primaryKey, checked) {
+		if ('select-all' === primaryKey) return toggleAll(checked);
+
+		return function(dispatch, getState) {
+			// var state = getState();
+			dispatch(setLoading(primaryKey));
+			p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle checkbox for row:' + primaryKey + ' ...'});
+
+			return new Promise(function (resolve, reject) {
+				setTimeout(function () {
+					var returnCode = Math.round(Math.random());
+					DBG && console.log('DBG::async toggle action: (pk:'+primaryKey+') fetched resultCode', returnCode);
+					if (!returnCode) {
+						reject("Error");
+						return;
+					}
+					resolve();
+				}, 1000);
+			}).then(function () {
+				p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle checkbox for row:' + primaryKey + '; return random success'});
+				DBG && console.log('DBG::async toggle action: (pk:'+primaryKey+') dispatch(SET_CHECKED)');
+				dispatch( checked ? setChecked(primaryKey) : setUnchecked(primaryKey) );
+			}).catch(function (e) {
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'TODO: toggle checkbox for row:' + primaryKey + '; return random fail - error: ' + e});
+				DBG && console.warn('DBG::async toggle action: (pk:'+primaryKey+') ERROR dispatch(SET_UNCHECKED)');
+				dispatch( checked ? setUnchecked(primaryKey) : setChecked(primaryKey) );
+			});
+		}
+	}
+	function setPrimaryKeys(listPrimaryKeys) { // TODO: should set list primaryKeys, change checkboxes to loading and fetch its state, then update gui
+		DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+'])...');
+		return function(dispatch, getState) {
+			dispatch({ type: 'SET_PRIMARY_KEYS', listPrimaryKeys: listPrimaryKeys });
+			dispatch( setListLoading(listPrimaryKeys) );
+			p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: update primary keys list:' + listPrimaryKeys.join(',') + '...'});
+
+			return new Promise(function (resolve, reject) {
+				setTimeout(function () {
+					var returnCode = 1;
+					var listSelected = listPrimaryKeys.filter(function (pk) {
+						return Math.round(Math.random());
+					})
+					DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) fetched', { returnCode, listSelected });
+					if (!returnCode) {
+						reject("Error");
+						return;
+					}
+					resolve(listSelected);
+				}, 1000);
+			}).then(function (listSelected) {
+				p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: update primary keys list:' + listPrimaryKeys.join(',') + '; return success'});
+				DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) dispatch success');
+				var fullListPks = ['select-all'].concat(listPrimaryKeys);
+				dispatch( setListUnchecked(fullListPks) );
+				dispatch( setListChecked(listSelected) );
+			}).catch(function (e) {
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'TODO: update primary keys list:' + listPrimaryKeys.join(',') + '; return fail - error: ' + e});
+				DBG && console.warn('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) ERROR dispatch error');
+				var fullListPks = ['select-all'].concat(listPrimaryKeys);
+				dispatch( setListUnchecked(fullListPks) );
+			});
+		}
+	}
+	function toggleAll(checked) {
+		var primaryKey = 'select-all';
+		return function(dispatch, getState) {
+			// var state = getState();
+			dispatch(setLoading(primaryKey));
+			var state = getState();
+			var listPrimaryKeys = state.listPrimaryKeys;
+			p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle all:' + listPrimaryKeys.join(',') + '...'});
+
+			return new Promise(function (resolve, reject) {
+				setTimeout(function () {
+					var returnCode = 1;
+					DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) fetched resultCode', returnCode);
+					if (!returnCode) {
+						reject("Error");
+						return;
+					}
+					resolve();
+				}, 1000);
+			}).then(function () {
+				p5UI__notifyAjaxCallback({type: 'warning', msg: 'TODO: toggle all:' + listPrimaryKeys.join(',') + '; return success'});
+				DBG && console.log('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) dispatch success');
+				var fullListPks = ['select-all'].concat(listPrimaryKeys)
+				dispatch( checked ? setListChecked(fullListPks) : setListUnchecked(fullListPks) );
+			}).catch(function (e) {
+				p5UI__notifyAjaxCallback({type: 'error', msg: 'TODO: toggle all:' + listPrimaryKeys.join(',') + '; return fail - error: ' + e});
+				DBG && console.warn('DBG::setPrimaryKeys action: (pks:['+listPrimaryKeys.join(',')+']) ERROR dispatch error');
+				var fullListPks = ['select-all'].concat(listPrimaryKeys)
+				dispatch( checked ? setListUnchecked(fullListPks) : setListChecked(fullListPks) );
+			});
+		}
+	}
+	return {
+		setPrimaryKeys: setPrimaryKeys,
+		setChecked: setChecked,
+		setUnchecked: setUnchecked,
+		toggle: toggle
+	}
+}
+
+global['P5UI__TableAjaxRowCheckbox'] = createReactClass({
+	// props.primaryKey: PropTypes.string.isRequired
+	// props.store: Redux store with state: { isLoading bool, selected array of strings }
+	// props.actions: Redux store actions
+	getStateFromStore: function () {
+		var state = this.props.store.getState();
+		return {
+			disabled: this.props.disabled ? true : false,
+			isLoading: (-1 !== state.loading.indexOf(this.props.primaryKey)),
+			checked: (-1 !== state.selected.indexOf(this.props.primaryKey))
+		};
+	},
+	getInitialState: function() {
+		return this.getStateFromStore();
+	},
+	componentDidMount: function () {
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::componentDidMount', { primaryKey: this.props.primaryKey });
+		this.unsubscribe = this.props.store.subscribe(this.storeUpdated)
+	},
+	componentWillUnmount: function () {
+		this.unsubscribe()
+	},
+	storeUpdated: function () {
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::storeUpdated', { primaryKey: this.props.primaryKey });
+		this.setState(this.getStateFromStore())
+	},
+	shouldComponentUpdate: function (nextProps, nextState) {
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::shouldComponentUpdate', { primaryKey: this.props.primaryKey }, {state: this.state, nextState});
+		return (
+			this.state.isLoading !== nextState.isLoading
+			|| this.state.checked !== nextState.checked
+		);
+	},
+	handleChange: function (checked) { // handleChange: function (event) {
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::handleChange', { primaryKey: this.props.primaryKey, checked: checked });
+		this.props.store.dispatch( this.props.actions.toggle( this.props.primaryKey, checked ) )
+	},
+	render: function () {
+		DBG && console.log('DBG::P5UI__TableAjaxRowCheckbox::render', { primaryKey: this.props.primaryKey, checked: this.state.checked, isLoading: this.state.isLoading });
+		return h(p5UI__FieldCheckboxLoading, {
+			size: 20,
+			color: '#999',
+			checked: this.state.checked,
+			disabled: this.state.disabled,
+			isLoading: this.state.isLoading,
+			onChange: this.handleChange
+		});
+	}
+});
 
 var TableAjax = function() {
 	var priv = {}; //private api
@@ -73,7 +293,6 @@ var TableAjax = function() {
 	var _currDpOp; // clicked datetimepicker operator
 	var _filterFields = {}; // array with filter DOM elements
 	var _filterTimeout; // timer for delayed filtering
-	var _uniqueCols = {}; // array with checked rows /// TODO: mv to _state.
 	var _checkToggleChecked = false; // check-all toggle state
 	var _exportFieldsSelect = {};
 
@@ -128,10 +347,15 @@ var TableAjax = function() {
 				priv.options.router.apply(this);
 			});
 		}
+
+		if (priv.options.checkboxes) {
+			priv.options.selectedStore = createStoreWithThunkMiddleware(selectedStore)
+			priv.options.selectedActions = selectedActions()
+		}
 	};
 
 	priv.onRender = function(e) {
-		if (priv.options.debug) console.log('onRender.arguments:', arguments.length, arguments, 'e:', e);
+		if (priv.options.debug || DBG) console.log('onRender.arguments:', arguments.length, arguments, 'e:', e);
 		// console.log("priv.onRender arguments:", arguments);
 		// console.trace();
 		if (arguments.length > 1) {
@@ -169,9 +393,10 @@ var TableAjax = function() {
 
 	priv.onWindowResize = function() {
 		var stickyCol1Width = (12 + 3) * 4 + 2 * 5 + 1;
-		var stickyCol2Width = 50 + 2 * 5 + 1;
+		var stickyCol2Width = (priv.options.checkboxes) ? 20 + 2 * 5 + 1 : 0;
+		var stickyCol3Width = 50 + 2 * 5 + 1;
 		var contW = jQuery(_uiNodeCont).parent().width();
-		var colsW = stickyCol1Width + stickyCol2Width;
+		var colsW = stickyCol1Width + stickyCol2Width + stickyCol3Width;
 		jQuery(_uiNodeCont).css({width:'' + (contW - colsW) + 'px'})//, marginLeft:'' + colsW + 'px', overflowX:'scroll', overflowY:'visible', paddingBottom:'1px'});
 
 		var wh = jQuery(window).height();   // returns height of browser viewport
@@ -228,7 +453,6 @@ var TableAjax = function() {
 		if (!_body) {
 			_uiNode$Table.find('tbody').remove();
 			_bodyNode = $('<tbody></tbody>').insertAfter(_head);
-			//_bodyNode.on('change', '.unique', priv.rowChecked);
 
 			// find out what rows to show next...
 			var rowsAdded = 0;
@@ -263,7 +487,8 @@ var TableAjax = function() {
 		_foot = $(_uiNodeCont).next('.foot');
 
 		var stickyCol1Width = (12 + 3) * 4 + 2 * 5 + 1;
-		var stickyCol2Width = 50 + 2 * 5 + 1;
+		var stickyCol2Width = (priv.options.checkboxes) ? 20 + 2 * 5 + 1 : 0;
+		var stickyCol3Width = 50 + 2 * 5 + 1;
 		if (undefined === _tableWidth) {
 			_tableWidth = true;
 			var contW = jQuery(_uiNodeCont).width();
@@ -271,7 +496,7 @@ var TableAjax = function() {
 			var th2 = th1.next();
 			var th1W = th1.innerWidth();
 			var th2W = 50 + 2 * 5;//th2.innerWidth();
-			var colsW = stickyCol1Width + stickyCol2Width;
+			var colsW = stickyCol1Width + stickyCol2Width + stickyCol3Width;
 			jQuery(_uiNodeCont).css({width:'' + (contW - colsW) + 'px', marginLeft:'' + colsW + 'px', minHeight: '360px', overflowX:'scroll', overflowY:'visible', paddingBottom:'1px'});
 		}
 		_uiNode$Table.find('.stickyCol1').css({
@@ -282,9 +507,10 @@ var TableAjax = function() {
 		});
 		//_uiNode$Table.find('.filter').find('.stickyCol1').css({height:'34px'});
 		var sortStickyColHeight = _uiNode$Table.find('.sort').find('th:last').outerHeight();
-		_uiNode$Table.find('.sort').find('.stickyCol1').css({height: sortStickyColHeight + 'px'});
-		_uiNode$Table.find('.sort').find('.stickyCol2').css({height: sortStickyColHeight + 'px'});
-		_uiNode$Table.find('.stickyCol2').css({
+		_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',
@@ -292,11 +518,22 @@ var TableAjax = function() {
 			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',
+		});
 
 		if (_data.total == 0 && priv.options.hidePagerOnEmpty) {
 			$('.btn-toolbar', _foot).remove();
 		}
-		if (priv.options.debug) console.log('table created');
+		if (priv.options.debug || DBG) console.log('table created');
 		if (typeof priv.options.tableCreated == 'function') {
 			priv.options.tableCreated.call(_uiNode$Table.get(0), {table: _uiNode$Table.get(0)});
 		}
@@ -524,7 +761,7 @@ var TableAjax = function() {
 							linkRemoveClassName: 'remove',
 							linkRemoveAddClassNames: 'glyphicon glyphicon-remove',
 							onSelect: function(recordID, geomShape) {
-								if (priv.options.debug) console.log('recordID:', recordID, 'geomShape:', geomShape);
+								if (priv.options.debug || DBG) console.log('recordID:', recordID, 'geomShape:', geomShape);
 								priv.mapEditorShow();
 								_mapEditor.TableAjaxMapSelectRecord(recordID, geomShape);
 							},
@@ -552,11 +789,11 @@ var TableAjax = function() {
 											p5UI__notifyAjaxCallback(payload);
 											var data = res.body;
 											if (data && data.record && data.record.the_geom) {
-												if (priv.options.debug) console.log('data.record.the_geom:2:', data.record.the_geom);
+												if (priv.options.debug || DBG) console.log('data.record.the_geom:2:', data.record.the_geom);
 												geomField.setValue(data.record.the_geom);
 											}
 											else if (data && data.record) {
-												if (priv.options.debug) console.log('data.record.the_geom:2:', data.record.the_geom);
+												if (priv.options.debug || DBG) console.log('data.record.the_geom:2:', data.record.the_geom);
 												geomField.setValue(data.record.the_geom);
 											}
 											_mapEditor.TableAjaxMapRefresh();
@@ -611,7 +848,7 @@ var TableAjax = function() {
 				rowPK = (_state.primaryKey in props) ? props[_state.primaryKey] : null,
 				cellNode,
 				columnName, columnProps, val, cellCnt, format, showTooltip, fldWidgetNode,
-				dbg = priv.options.debug
+				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+"')")
@@ -620,16 +857,22 @@ var TableAjax = function() {
 
 		if (priv.options.rowFunctions || priv.options.filtersClean) {
 			cellNode = priv.renderCellRowFunctions(priv.options.rowFunctions, rowPK, props);
-			cellNode.addClass('text-right stickyCol1');
+			cellNode.addClass('text-right stickyCol stickyCol1');
 			cellNode.appendTo(rowNode);
 		}
 
 		//create checkbox
-		if (_state.primaryKey && priv.options.checkboxes) {
-			var check = _uniqueCols[props[_state.primaryKey]] != undefined ? 'checked' : '';
+		if (_state.primaryKey && priv.options.checkboxes) { // row checkbox cell
 			var checkable = props['checkable'] === false ? 'disabled' : '';
-			cellNode = $('<td></td>').appendTo(rowNode);
-			$(p5Utils__format('<input class="unique" {0} {1} type="checkbox" />', [check, checkable])).appendTo(cellNode);
+			cellNode = $('<td class="stickyCol stickyCol2" style="padding:3px 5px"></td>').appendTo(rowNode);
+			ReactDOM.render(
+				h(P5UI__TableAjaxRowCheckbox, {
+					primaryKey: rowPK,
+					store: priv.options.selectedStore,
+					actions: priv.options.selectedActions
+				}),
+				cellNode.get(0)
+			);
 		}
 
 		if (_state.primaryKey) rowNode.data('unique', rowPK);
@@ -645,7 +888,7 @@ var TableAjax = function() {
 
 			val = props[columnName];
 			cellNode = $('<td></td>').appendTo(rowNode);
-			if (i === 1) cellNode.addClass('stickyCol2');// primaryKey(ID)
+			if (i === 1) cellNode.addClass('stickyCol stickyCol3');// primaryKey(ID)
 			if (i !== 1) cellNode.on('dblclick', {id:rowPK, col:columnName}, priv.rowDblClicked);
 			cellCnt = (i === 1)? '<div></div>' : '<span></span>';
 			cellCnt = $(cellCnt).appendTo(cellNode);// fix dblclick for copy
@@ -674,7 +917,7 @@ var TableAjax = function() {
 			}
 			if ('ref' === columnProps.type) showTooltip = false;
 			if ('ref' === columnProps.type) cellNode.css('padding', "3px")
-			if ('ref' === columnProps.type && priv.options.debug) console.log('renderRow rowPK('+rowPK+') columnProps', columnProps)
+			if ('ref' === columnProps.type && (priv.options.debug || DBG)) console.log('renderRow rowPK('+rowPK+') columnProps', columnProps)
 			// if ('ref' === columnProps.type) cellNode.html('<a href="index.php?_route=ViewTableAjax' +
 			// 	'&namespace=' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType +
 			// 	'&backRefNS=' + priv.options.namespace +
@@ -723,7 +966,7 @@ var TableAjax = function() {
 				moreFunctions = [],
 				idx
 		;
-		if (priv.options.debug) console.log('TableAjax::renderCellRowFunctions: rowFunctions', rowFunctions);
+		if (priv.options.debug || DBG) console.log('TableAjax::renderCellRowFunctions: rowFunctions', rowFunctions);
 		idx = keys.indexOf('hist');
 		if (idx !== -1) {
 			histFunc = keys.splice(idx, 1);
@@ -746,10 +989,10 @@ var TableAjax = function() {
 		e.preventDefault();
 		e.stopPropagation();
 		if (!e.data || !e.data.rowPK) {
-			if (priv.options.debug) console.log('NO data');
+			if (priv.options.debug || DBG) console.log('NO data');
 			return false;
 		}
-		if (priv.options.debug) console.log('TableAjax::popoverCellMoreFunctions: rowPK', e.data.rowPK, 'moreFunctions', e.data.more, 'rowFunctions', e.data.rowFunctions);
+		if (priv.options.debug || DBG) console.log('TableAjax::popoverCellMoreFunctions: rowPK', e.data.rowPK, 'moreFunctions', e.data.more, 'rowFunctions', e.data.rowFunctions);
 		var lastId = _popoverCell.data('rowid');
 		var lastCol = _popoverCell.data('col');
 		var rowPK = e.data.rowPK;
@@ -841,7 +1084,7 @@ var TableAjax = function() {
 	};
 
 	priv.ajaxLoadMoreFunctionsCell = function(rowPK) {
-		var dbg = priv.options.debug;
+		var dbg = (priv.options.debug || DBG);
 		if (_popoverCellAjaxXhr) {
 			_popoverCellAjaxXhr.abort();
 		}
@@ -922,11 +1165,20 @@ var TableAjax = function() {
 		var rowNode = $('<tr></tr>');
 
 		if (priv.options.rowFunctions || priv.options.filtersClean) {
-			$('<td class="text-right stickyCol1">&nbsp;</td>').appendTo(rowNode);
+			$('<td class="text-right stickyCol stickyCol1">&nbsp;</td>').appendTo(rowNode);
 		}
 
 		if (_state.primaryKey && priv.options.checkboxes) {
-			$('<td><input disabled type="checkbox" /></td>').appendTo(rowNode);
+			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],
@@ -936,7 +1188,7 @@ var TableAjax = function() {
 			if (columnProps.hidden) continue;// "unique" is hidden - TODO: rm "unique" from _state.colsSorted ?
 
 			if (i == 1) {// TODO: 1 is pk - use better check
-				$('<td class="stickyCol2">&nbsp;</td>').appendTo(rowNode);
+				$('<td class="stickyCol3">&nbsp;</td>').appendTo(rowNode);
 			} else {
 				$('<td>&nbsp;</td>').appendTo(rowNode);
 			}
@@ -949,7 +1201,7 @@ var TableAjax = function() {
 		var currentNode = _uiNode$Table.find('thead').find('.' + nodeClass)
 		var node = $('<tr class="sort ' + nodeClass + '"></tr>')
 		if (priv.options.tblFunctions || priv.options.filtersClean) {
-			var headCell = $('<th class="text-right head-info stickyCol1"></th>').appendTo(node)
+			var headCell = $('<th class="text-right head-info stickyCol stickyCol1"></th>').appendTo(node)
 			$.map(priv.options.tblFunctions, function (funObj, funName) {
 				var funHtml = $('<a></a>')
 				funHtml.attr('href', funObj.href || '#')
@@ -963,10 +1215,15 @@ var TableAjax = function() {
 		}
 
 		if (_state.primaryKey && priv.options.checkboxes) {
-			var checked = _checkToggleChecked ? 'checked' : ''
-			var headCell = $('<th></th>').appendTo(node)
-			var elem = $(p5Utils__format('<input {0} class="checkToggle" type="checkbox" />', [checked])).appendTo(headCell)
-			elem.on('change', priv.checkToggleChanged)
+			var headCell = $('<th class="stickyCol stickyCol2" style="padding:3px 5px"></th>').appendTo(node);
+			ReactDOM.render(
+				h(P5UI__TableAjaxRowCheckbox, {
+					primaryKey: 'select-all',
+					store: priv.options.selectedStore,
+					actions: priv.options.selectedActions
+				}),
+				headCell.get(0)
+			);
 		}
 
 		for (var i = 0; i < _state.colsSorted.length; i++) {
@@ -975,7 +1232,7 @@ var TableAjax = function() {
 			if (props.hidden) continue
 
 			var headCell = $('<th class="ta-ordering"></th>').appendTo(node)
-			if (i == 1) headCell.addClass('stickyCol2')
+			if (i == 1) headCell.addClass('stickyCol stickyCol3')
 
 			if (props.type != 'special' && props.type != 'geom') { // TODO: props.isSortable
 				headCell.on('click', {column: column}, priv.columnClicked)
@@ -1066,7 +1323,7 @@ var TableAjax = function() {
 
 		//create the functions column
 		if (priv.options.rowFunctions || priv.options.filtersClean) {
-			var headCell = $('<th class="text-right stickyCol1"></th>').appendTo(node);
+			var headCell = $('<th class="text-right stickyCol stickyCol1"></th>').appendTo(node);
 			headCell.css({padding: '0'});
 			if (priv.options.filtersClean) {
 				var elem = $('<button title="Kasuj filtry" class="btn btn-xs btn-link glyphicon glyphicon-remove"></button>').appendTo(headCell);
@@ -1076,31 +1333,21 @@ var TableAjax = function() {
 
 		//create the filter checkbox
 		if (_state.primaryKey && priv.options.checkboxes) {
-			tooltip = priv.options.types.bool.filterTooltip || 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked';
-			headCell = $('<th></th>').appendTo(node);
-			elem = $('<input class="filter indeterminate" checked type="checkbox" />').appendTo(headCell);
-			$.map(_state.filters.filterCols, function(colProps, col) {
-				if (col == "unique") {
-					if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
-					else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
-					else if (colProps.filter == '') elem.addClass('indeterminate');
-				}
-			});
-
-			if (tooltip) {
-				elem.tooltip({
-					title: tooltip.trim(),
-					html: true,
-					container: 'body',
-					trigger: 'hover',
-					placement: 'top',
-					delay: {
-						show: 500,
-						hide: 100
+			// tooltip = priv.options.types.bool.filterTooltip || 'Pokaż tylko:<br/>nieokreślony,<br/>zaznaczony,<br/>niezaznaczony';
+			headCell = $('<th class="stickyCol stickyCol2" style="padding:0 5px"></th>').appendTo(node);
+
+			ReactDOM.render(
+				h(p5UI__FieldCheckboxSearch, {
+					size: 20,
+					color: '#888',
+					staticLabel: ["Pokaż:", "wszystkie,", "wybrane,", "niewybrane"].join("\n"),
+					// store: priv.options.selectedStore,
+					onChange: function (selected) {
+						console.log('TODO p5UI__FieldCheckboxSearch.props.onChange', { selected }); // TODO: filter ajax
 					}
-				});
-			}
-			elem.on('click', {column: "unique"}, priv.filterChanged);
+				}),
+				headCell.get(0)
+			);
 		}
 
 		//create the column filters
@@ -1113,7 +1360,7 @@ var TableAjax = function() {
 
 			headCell = $('<th></th>').appendTo(node);
 			headCell.css({padding: '0'});
-			if (i == 1) headCell.addClass('stickyCol2');
+			if (i == 1) headCell.addClass('stickyCol stickyCol3');
 
 			// @mark MarkTableAjaxFilterColType
 			switch (props.type || 'string') {
@@ -1146,7 +1393,7 @@ var TableAjax = function() {
 											dp.on('changeDate', {column: column, input: $('input.filter', elem)}, priv.dpClicked);
 									}
 									else
-									if (priv.options.debug) console.log('datepicker plugin not found');
+									if (priv.options.debug || DBG) console.log('datepicker plugin not found');
 							}
 							elem.on('keyup', 'input.filter', {column: column}, priv.filterChanged);
 							break;
@@ -1247,7 +1494,7 @@ var TableAjax = function() {
 		var nodeClass = 'tblAjax__' + 'footer__toolbar__info',
 				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
 				node;
-		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.debug || DBG) console.log('Render: ', nodeClass);
 		node = $('<div class="foot-info ' + nodeClass + '"></div>');
 		//console.log('renderFooterInfo total', _data.total, 'page', _state.page, 'priv.options.pageSize', priv.options.pageSize);
 		var fromRow = Math.max(_state.page - 1, 0) * _state.pageSize;
@@ -1266,7 +1513,7 @@ var TableAjax = function() {
 				node;
 		var currPage = _state.page;
 		var totalPages = Math.ceil(_data.total / _state.pageSize);
-		if (priv.options.debug) console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage);
+		if (priv.options.debug || DBG) console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage);
 		/// console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage, '_state.page', _state.page);
 		if (_data.total > 0) {
 			node = $('<div class="btn-group ' + nodeClass + '"></div>');
@@ -1319,7 +1566,7 @@ var TableAjax = function() {
 		var nodeClass = 'tblAjax__' + 'footer__toolbar__pagesizes',
 				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
 				node;
-		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.debug || DBG) console.log('Render: ', nodeClass);
 		if (_data.total > 0 && priv.options.pageSizes.length > 0) {
 			node = $('<div class="btn-group dropup pagesize ' + nodeClass + '"></div>');
 			var btn = $('<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#">Liczba wierszy&nbsp;</button>').appendTo(node);
@@ -1344,7 +1591,7 @@ var TableAjax = function() {
 		var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
 				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
 				node;
-		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.debug || DBG) console.log('Render: ', nodeClass);
 		if (priv.options.columnPicker) {
 			node = $('<div class="btn-group dropup columnpicker ' + nodeClass + '"></div>');
 			var btn = $('<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#">Kolumny&nbsp;</button>').appendTo(node);
@@ -1388,7 +1635,7 @@ var TableAjax = function() {
 		var nodeClass = 'tblAjax__' + 'footer__toolbar__functions',
 				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
 				node;
-		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.debug || DBG) console.log('Render: ', nodeClass);
 		if (priv.options.tblFunctions) {
 			node = $('<div class="btn-group ' + nodeClass + '"></div>');
 			$.map(priv.options.tblFunctions, function(funObj, funName){
@@ -1413,7 +1660,7 @@ var TableAjax = function() {
 		var nodeClass = 'tblAjax__' + 'footer__toolbar__export',
 				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
 				node;
-		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.debug || DBG) console.log('Render: ', nodeClass);
 		if (priv.options.exportFields && priv.options.exportFields.length) {
 			node = $('<div class="btn-group dropup pagesize ' + nodeClass + '"></div>');
 			var btn = $('<button class="btn btn-sm dropdown-toggle" data-toggle="dropdown" href="#">Export&nbsp;</button>').appendTo(node);
@@ -1607,11 +1854,11 @@ var TableAjax = function() {
 	priv.update = function(callback) {
 		var skipCols, resetChecked;// undefined
 		if (!priv.options.url) {
-			if (priv.options.debug) console.log('no url found');
+			if (priv.options.debug || DBG) console.log('no url found');
 			return;
 		}
 
-		if (priv.options.debug) console.log(p5Utils__format('requesting data from url:{0}', priv.options.url));
+		if (priv.options.debug || DBG) console.log(p5Utils__format('requesting data from url:{0}', [priv.options.url]));
 
 		var initUrlAdd = '';
 		var filtersInitSet = false;
@@ -1646,7 +1893,7 @@ var TableAjax = function() {
 		}).then(function (response) {
 			return response.json()
 		}).then(function (data) {
-			if (priv.options.debug) console.log('loadDataAjax:fetch:update: request finished response data:', data);
+			if (priv.options.debug || DBG) console.log('loadDataAjax:fetch:update: request finished response data:', data);
 			if ('success' == data.type) {
 				// p5UI__notifyAjaxCallback(data);
 				return data;
@@ -1657,7 +1904,7 @@ var TableAjax = function() {
 			}
 			return null;
 		}).then(function (data) {
-			if (priv.options.debug) console.log('loadDataAjax:fetch:update: request finished data:', data);
+			if (priv.options.debug || DBG) console.log('loadDataAjax:fetch:update: request finished data:', data);
 			if (!data) return;
 			if (data && data.cols) {
 				priv.setStateCols(data.cols, data.primaryKey);
@@ -1743,14 +1990,14 @@ var TableAjax = function() {
 
 		renderParts = Object.keys(renderParts);
 
-		if (priv.options.debug) console.log('setState::renderParts: ', renderParts);//TODO:DBG:RMME
+		if (priv.options.debug || DBG) console.log('setState::renderParts: ', renderParts);//TODO:DBG:RMME
 		if (renderParts.length > 0) {
 			jQuery(_uiNodeCont).trigger('TableAjax:render', renderParts);
 		}
 	};
 
 	priv.setStateCols = function(cols, primaryKey) {
-		if (priv.options.debug) console.log('priv.setStateCols: ', {primaryKey: primaryKey, cols: cols});
+		if (priv.options.debug || DBG) console.log('priv.setStateCols: ', {primaryKey: primaryKey, cols: cols});
 		// console.log("priv.setStateCols");
 		// console.trace();
 		_state.cols = cols;
@@ -1807,7 +2054,17 @@ var TableAjax = function() {
 		_state.specialFilters = newSpecialFilters;
 	};
 
-	priv.setStateData = function(pData, skipCols, resetChecked) {
+	priv.setStateData = function(pData) {
+		var listPrimaryKeys = (pData && pData.rows && pData.rows.length)
+			? pData.rows.map(function (item) {
+					return item['@primaryKey'];
+				})
+			: []
+		;
+		jQuery(_uiNodeCont).trigger('TableAjax:updateRows', { primaryKeys: listPrimaryKeys });
+		if (priv.options.checkboxes) {
+			priv.options.selectedStore.dispatch( priv.options.selectedActions.setPrimaryKeys(listPrimaryKeys) );
+		}
 		var data = $.extend(true, {}, pData);
 		data.cols = _state.cols;// always use old cols - change cols mved to priv.setStateCols
 
@@ -1818,27 +2075,8 @@ var TableAjax = function() {
 		if (_state.page > 1) {
 			_totalPages = Math.ceil(_data.total / priv.options.pageSize);
 		}
-
-		//keep any previously checked rows around?
-		if (resetChecked === true || resetChecked === undefined)
-			_uniqueCols = {};
-		else {
-			for (var key in _uniqueCols)
-				_uniqueCols[key] = priv.getRow(key);
-		}
-
-		if (_state.primaryKey) {
-			//add rows that needs to be pre-checked
-			$.each(_data.rows, function(index, row) {
-				if (row["checked"] === true)
-					_uniqueCols[row[_state.primaryKey]] = row;
-			});
-		}
 	};
 
-	/*
-	 sorts the data on the current sorting column
-	 */
 	priv.sort = function() {
 		if (!_data.cols[_state.filters.currSortCol]) _state.filters.currSortCol = "";
 		if (!_state.filters.currSortCol) return;
@@ -1866,10 +2104,10 @@ var TableAjax = function() {
 	 */
 	priv.filterChanged = function(e) {
 		//clear old timer if we're typing fast enough
-		if (priv.options.debug) console.log('filterChanged::#1');
+		if (priv.options.debug || DBG) console.log('filterChanged::#1');
 		if (_filterTimeout) {
 			clearTimeout(_filterTimeout);
-			if (priv.options.debug) console.log('filterChanged::#2 previous filtering cancelled');
+			if (priv.options.debug || DBG) console.log('filterChanged::#2 previous filtering cancelled');
 		}
 
 		var filter = this.value
@@ -1924,7 +2162,7 @@ var TableAjax = function() {
 	priv.filtersClean = function(e) {
 		if (_filterTimeout) {
 			clearTimeout(_filterTimeout);
-			if (priv.options.debug) console.log('filtering cancelled - filtersClean');
+			if (priv.options.debug || DBG) console.log('filtering cancelled - filtersClean');
 		}
 
 		var filtersActive = false;
@@ -1961,7 +2199,7 @@ var TableAjax = function() {
 	 * what: triggers filtering on the value
 	 */
 	priv.specialFilterChanged = function(e) {
-		if (priv.options.debug) console.log('specialFilterChanged e.data:', e.data);
+		if (priv.options.debug || DBG) console.log('specialFilterChanged e.data:', e.data);
 		var state = {};
 		state.specialFilters = _state.specialFilters;
 		state.specialFilters[e.data.group] = e.data.value;
@@ -2023,7 +2261,7 @@ var TableAjax = function() {
 				}
 			},
 			onSaveFeature: function(selectedRecordId, selectedFeatureExtent) {
-				if (priv.options.debug) console.log('onSaveFeature (',selectedRecordId,'/',selectedFeatureExtent,') ', this);
+				if (priv.options.debug || DBG) console.log('onSaveFeature (',selectedRecordId,'/',selectedFeatureExtent,') ', this);
 				if (!selectedRecordId) {
 					alert('Brak zaznaczonego rekordu - wybierz rekord z tabeli');
 					return;
@@ -2196,7 +2434,7 @@ var TableAjax = function() {
 	};
 
 	priv.mapEditorChanged = function(e) {
-		if (priv.options.debug) console.log('mapEditorChanged option(',priv.options.mapEditor,')');
+		if (priv.options.debug || DBG) console.log('mapEditorChanged option(',priv.options.mapEditor,')');
 		priv.options.mapEditor = !priv.options.mapEditor;
 		if (priv.options.mapEditor) {
 			priv.mapEditorShow();
@@ -2229,7 +2467,7 @@ var TableAjax = function() {
 	priv.pageSizeChanged = function(e) {
 		e.preventDefault();
 		var val = $(this).text();
-		if (priv.options.debug) console.log(p5Utils__format('pagesize changed to:{0}', [val]));
+		if (priv.options.debug || DBG) console.log(p5Utils__format('pagesize changed to:{0}', [val]));
 
 		if (parseInt(val) == priv.options.pageSize) {
 			return false;
@@ -2249,7 +2487,7 @@ var TableAjax = function() {
 	 */
 	priv.columnClicked = function(e) {
 		e.preventDefault();
-		if (priv.options.debug) console.log(p5Utils__format('col:{0} clicked', [e.data.column]));
+		if (priv.options.debug || DBG) console.log(p5Utils__format('col:{0} clicked', [e.data.column]));
 
 		//set the new sorting column
 		if (_state.filters.currSortCol == e.data.column) _state.filters.currSortFlip = !_state.filters.currSortFlip;
@@ -2617,29 +2855,13 @@ var TableAjax = function() {
 		jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
 	};
 
-	/*
-	 when: clicking a row checkbox
-	 what: toggles checked state on row, and add/removes it from checked array
-	 */
-	priv.rowChecked = function(e) {
-		var elem = $(this);
-
-		//get the row's unique value
-		var unique = elem.closest('tr').data('unique');
-		if (priv.options.debug) console.log(p5Utils__format('row({0}) {1}', [unique, elem.is(':checked') ? 'checked' : 'unchecked']));
-
-		//store the row in checked array
-		if (elem.is(':checked')) _uniqueCols[unique] = priv.getRow(unique);
-		else delete _uniqueCols[unique];
-	};
-
 	/*
 	 when: clicking anywhere on a row
 	 what: row data and other info is returned to caller
 	 */
 	// priv.rowClicked = function(e) {// TODO: not used
 	// 	if (!_state.primaryKey) {
-	// 		if (priv.options.debug) console.log('no unique column specified');
+	// 		if (priv.options.debug || DBG) console.log('no unique column specified');
 	// 		return;
 	// 	}
 	//
@@ -2762,11 +2984,11 @@ var TableAjax = function() {
 					})(inlineEditBox$Node);
 				},
 				error: function(err) {
-					if (priv.options.debug) console.log('err');
+					if (priv.options.debug || DBG) console.log('err');
 				}
 			});
 		} else {
-			if (priv.options.debug) console.log('NO data');
+			if (priv.options.debug || DBG) console.log('NO data');
 			return false;
 		}
 	};
@@ -2850,7 +3072,7 @@ var TableAjax = function() {
 				_popoverCellCurrent.popover('show');
 			}
 		} else {
-			if (priv.options.debug) console.log('NO data');
+			if (priv.options.debug || DBG) console.log('NO data');
 			return false;
 		}
 		return;
@@ -2873,7 +3095,7 @@ var TableAjax = function() {
 	};
 
 	publ.init = function(options) {
-		if (priv.options.debug) console.log('TableAjax initialization...');
+		if (priv.options.debug || DBG) console.log('TableAjax initialization...');
 		//merge supplied options with defaults
 		$.extend(priv.options, defaults, options);
 		priv.init();
@@ -2932,7 +3154,7 @@ var TableAjax = function() {
 		}).then(function (response) {
 			return response.json()
 		}).then(function (data) {
-			if (priv.options.debug) console.log('loadDataAjax:fetch:loadPage: request finished, data:', data);
+			if (priv.options.debug || DBG) console.log('loadDataAjax:fetch:loadPage: request finished, data:', data);
 			// p5UI__notifyAjaxCallback(data);
 			if ('success' == data.type) {
 				state = {data: {}};

+ 1 - 0
SE/se-lib/TableAjax.php.init.js

@@ -1,5 +1,6 @@
 jQuery(document).ready(function(){
 	jQuery('#' + TABLE_AJAX_NODE_ID).TableAjax({
+		checkboxes: GUI_SHOW_CHECKBOXES || 0,
 		namespace: NAMESPACE,
 		url: URL_LOAD_AJAX_BASE,
 		getCsvTheGeomAjaxUrl: URL_GET_CSV_THE_GEOM_AJAX,