Ok I have a JQVMAP that I have on my site to select states for a search box. Everything worked great until I added my Clear function.
I also had to incorporate the patch from member HardCode Link to the patch
Found the solution, change line 466 in jqvmap.js file to:
regionClickEvent = $.Event('regionClick.jqvmap');
jQuery(params.container).trigger(regionClickEvent, [code, mapData.pathes[code].name]);
This is how I initialize it:
// with this Code it will select states and change the color of selected states plus save the codes of selected states into a hidden field
$('#omap').vectorMap(
{
map: 'usa_en',
backgroundColor: '#fff',
borderColor: '#000',
borderWidth: 4,
color: '#f4f3f0',
enableZoom: false,
hoverColor: '#fece2f',
hoverOpacity: null,
normalizeFunction: 'linear',
scaleColors: ['#b6d6ff', '#005ace'],
selectedColor: '#db9b15',
selectedRegion: null,
showTooltip: true,
multiSelectRegion: true,
onRegionClick: function(element, code, region) {
if(highlight[code]!=='#db9b15'){
highlight[code]='#db9b15';
origin = $('#search_origin_states');
states = origin.val();
if (states == ""){
origin.val(code);
} else {
origin.val(states + "," + code);
}
} else {
highlight[code]='#f4f3f0';
states = origin.val();
if (states.indexOf(","+code) >= 0) {
states = states.replace(","+code,"");
origin.val(states);
} else if (states.indexOf(code+",") >= 0){
states = states.replace(code+",","");
origin.val(states);
} else {
states = states.replace(code,"");
origin.val(states);
}
}
$('#omap').vectorMap('set', 'colors', highlight);
}
});
I use to have to click each state to clear it. But I wrote a script to clear all in one click.
function search_map_clear(field, map) {
var states = $('#search_' + field + '_states');
var sel_states = states.val();
var highlight2 = [];
$.each(sel_states.split(','), function (i, code) {
highlight2[code] = '#f4f3f0';
$('#' + map).vectorMap('set', 'colors', highlight2);
});
states.val("");
}
This will change all colors back to the original color, but apparently it does not clear the selectedRegions because after clearing if I select any other state all the states that I changed back to original color show back up.
My Question is:
How can I clear the selected states so were I can select different ones without clicking on every state that was selected prior
UPDATE
I have been able to run this command from the console and I can select and deselect states... But it will not deselect a state that was clicked on to select.
$('#omap').vectorMap('select', 'ar');
$('#omap').vectorMap('deselect', 'ar');
I need to clear out the states that have been clicked on...
Here is my jsFiddle that will show you what is happening:
JSFIDDLE DEMO
You store information in the variable highlight, and you clean the map with highlight2. It will not change the information in highlight so that when you trigger onRegionClick() it will change back to what you select.
Use global variable to let the scope of highlight to cross two script, then replace highlight2 by highlight and remove highlight2 declation.
See jsfiddle here, I think this is what you want.
I just added this function to library
setSelectedRegions: function(keys){
for (var key in this.countries) {
this.deselect(key, undefined);
}
var array = keys.split(",");
for (i=0;i<array.length;i++) {
//alert(array[i])
this.select(array[i], undefined);
}
},
and used it later as
jQuery('#vmap').vectorMap('set', 'selectedRegions', 'gb,us');
Related
I have a column for buttons to toggle a modal. The problem is, I don't want to display the button for every single row. I only want to display the button on the first entry of the color.
Note that the colors are unpredictable (you don't know what colors will be displayed beforehand).
For example,
color toggler
black +
red +
red //don't display it here
yellow +
blue +
blue //don't display it here
blue //don't display it here
orange +
red +
black +
black //don't display it here
blue +
I have try to go through the document and some example, but I can't seem to find a solution to it (maybe something that I missed ?).
What I did was storing the first color in the state. Then I did with the theCheckFunc:
let flag = true
if (nextColor !== this.state.color)
this.setState({color: nextColor})
flag = false
return flag
Then in the columns I did.
Cell: props => (this.theCheckFunc(props) && <div onClick={somefunc}> + <div>)
However, everything seems to be frozen. The browser doesn't even respond.
Any good suggestion on how to do this ?
Don't use state with this, since you don't want to re-render based on new input. Instead, compute the array as part of the render.
For example, assuming that when you get to your render statement, you have a random array of colors like this:
['red', 'red', 'black', 'purple', 'purple']
Then this function could create the array you need with the data for render:
function getTableRowData(arr) {
let tableRowData = []
arr.forEach((color, n) => {
let toggler = true
if (n !== 0 && arr[n - 1] === color) {
toggler = false
}
tableRowData.push({ color, toggler, })
})
return tableRowData
}
Then you can iterate over the tableRowData in your render return and have it display the way you want to.
First set your color control variables in state or in class wherever you choose. In this example i'm choosing to control them over state.
constructor(props) {
super(props);
this.state = {
firstRedAlreadyHere: false,
firstBlueAlreadyHere: false,
firstGrayAlreadyHere:false,
....
...
}
}
then open a function to prepare a table. Later Use that function in render() to put table on component.
function putValuesToTable()
{
let table = [];
for (let i = 0; i < (YOUR_LENGTH); i++) {
{
let children = []; /* SUB CELLS */
/* IF RED COLOR IS NEVER CAME BEFORE, PUT A BUTTON NEAR IT */
if(!this.state.firstRedAlreadyHere)
children.push(<td>
<SomeHtmlItem></SomeHtmlItem></td> <td><button </button></td>)
/* ELSE DON'T PUT BUTTON AND CHANGE STATE. */
else
{
children.push(<SomeHtmlItem></SomeHtmlItem>);
this.state.firstRedAlreadyHere = true;
}
table.push(<tr>{children}</tr>);
}
}
return table;
}
I am changing state directly instead of this.setState(); because I don't want to trigger a refresh :). In render function, call putValuesToTable like this
render()
{
return (<div>
<table>
<tbody>
<tr>
<th>SomeParameter</th>
<th>SomeParameter2</th>
</tr>
{this.putValuesToTable}
</tbody>
</table>
</div>);
}
Use this example to extend your code according to your aim.
I'm using a chart with drilldown and I've allowed to select a point (=click). On the event click, I create an HTML table with help of AJAX to list the entities related to the count (if I see 5 items, by clicking on it I'll see who are the 5 items and list them). This becomes a 2nd/3rd level of drilldown (depending I've clicked on the 1st level of 2nd level in the chart)
However, I'd like to remove the selection on first level on the drill up event.
Here is my code (EDITED) :
Edit
I'm adding series like this (sample found here) :
$(function() {
var myChart = new Highcharts.Chart({
chart: {
type: 'column',
renderTo: 'drillDownContainer',
// Deactivate drilldown by selection.
// See reason below in the drilldown method.
selection: function(event) {
return false;
},
drilldown: function(e) {
// Deactivate click on serie. To drilldown, the user should click on the category
if (e.category === undefined)
{
return;
}
// Set subTitle (2nd param) by point name without modify Title
// Giving 1st param as null tells the text will be for the subTitle,.
// For Title, remove the null 1st param and let text.
this.setTitle(null, { text: e.point.name });
if (!e.seriesOptions) {
var chart = this,
drilldowns = {
'Social': [
{"name":"Serie 1","data": [{"id":113,"name":"my data","y":14}
]}
]
};
var categorySeries = drilldowns[e.point.name];
var series;
for (var i = 0; i < categorySeries.length; i++) {
if (categorySeries[i].name === e.point.series.name) {
series = categorySeries[i];
break;
}
}
chart.addSingleSeriesAsDrilldown(e.point, series);
drilldownsAdded++;
// Buffers the number of drilldown added and once the number of series has been reached, the drill down is applied
// So, can't navigate by clicking to a single item.
// However, simply click on the category (xAxis) and then unselect the other series by clicking on legend
// The click has been disabled.
if (drilldownsAdded === 3) {
drilldownsAdded = 0;
chart.applyDrilldown();
}
}
},
drillup: function(e) {
this.setTitle(null, { text: '' }); // Erase subTitle when back to original view
$('#ajaxContainer').html(''); // Remove the table drilldown level3
},
drillupall: function(e) {
debugger;
console.log(this.series);
console.log(this.series.length);
for(i = 0; i < this.series.length; i++)
{
console.log("i = " + i);
console.log(this.series[i].data.length);
for (d = 0; i < this.series[i].data.length; d++)
{
console.log("d = " + d);
console.log(this.series[i].data[d].selected);
}
}
}
}
}); -- End of myChartdeclaration
myChart.addSeries({
color: colorsArray['impact101'],
name: '1-Modéré',
data: [
{
id: '101',
name: 'Type 1',
y: 64,
drilldown: true
},
{
id: '102',
name: 'Type 2',
y: 41,
drilldown: true
}]
}, true);
});
Demo of the point selection : http://jsfiddle.net/8truG/12/
What do I'd like to do? (EDIT)
If I select a point on the 2nd level, then return to 1st level and then back to same drilldown data, the point selected before is not selected anymore.
However, for the 1st level, the selection remains.
On the drillup event, the this.series[x].data[y] corresponds to the data of the 2nd level. Kind of obvious as the drilldown is not finished for all series but event raised as many as there is series.
On the drillupall event, I'm getting the right serie. I can see my 3 series on debug but they are all without any data. So I can't apply this.series[i].data[d].selected as suggested in comment below.
I'm using Highcharts 5.0.9.
Any idea to help me ?
Thanks for your help.
I got helped on the Highcharts forum. See here.
Here is the answer (in case the link above doesn't work in the future):
In this case you can change the state of a specific point with
Point.select() function. Take a look at the example posted below. It
works like that: in drillup() event there is a call of
getSelectedPoints() which returns all selected points from the base
series. Next, there is a call of select() function with false as an
argument on a selected point to unselect it. In addition, the call of
the getSelectedPoints() is located in a timeout, otherwise it would
return an empty array (see
https://github.com/highcharts/highcharts/issues/5583).
CODE:
setTimeout(function() { selectedPoints =
chart.getSelectedPoints()[0];
chart.getSelectedPoints()[0].select(false); }, 0);
API Reference: http://api.highcharts.com/highcharts/Ch ... ctedPoints
http://api.highcharts.com/highcharts/Point.select
Examples: http://jsfiddle.net/d_paul/smshk0b2/
Consequently, here now the code fixing the issue :
drillupall: function(e) {
this.setTitle(null, { text: '' }); // Erase subTitle when back to original view
$('#ajaxContainer').html(''); // Remove the table drilldown level3
var chart = this,
selectedPoints;
setTimeout(function() {
selectedPoints = chart.getSelectedPoints()[0];
// Ensure there is any selectedPoints to unselect
if (selectedPoints != null)
{
selectedPoints.select(false);
}
}, 0);
Regards.
I was hoping to detect when a Kendo grid's row changes, by navigation as opposed to selecting.
By this I mean I would have a grid with selectable: false, in batch edit mode, and I would like to update the data source (in code) when the user tabs to a new row (just as Access does).
I have looked at this example and changed the following properties..
selectable: false,
navigatable: true,
editable: true,
Unfortunately the changed event does not when seem to fire for tabs or arrow keys (when in navigation mode).
Would anyone know any other way I can do as described above (ie know when we have changed row via navigation)
Thanks in advance for any help!
You can use the edit event to determine whether you're in a new row.
Here you go:
selectable: false,
navigatable: true,
editable: true,
edit: function(e) {
if (e.sender.cellIndex($(e.container)) === 0 &&
$(e.container).closest("tr").index() !== 0) {
console.log("next row; update DS");
}
},
You could also store the last row you were in and determine the change using that, if switching between rows in other ways than by tabbing (or when tabbing backwards) is relevant.
If you don't want the grid to be editable, it's more difficult. Here's a quick hack:
var grid = $("#grid").data("kendoGrid");
var elem = $(grid.table)[0];
var handlers = $._data(elem, "events")["keydown"][2];
var oldHandler = handlers.handler;
// replace the existing event handler attached by kendo grid
var newHandler = function (e) {
oldHandler(e);
var current = grid.current();
var closestRow = $(current).closest("tr");
var rowIndex = $(closestRow).index();
if (rowIndex !== grid._lastNavRowIndex) {
if (typeof grid._lastNavRowIndex !== "undefined") {
kendoConsole.log("we just changed to row " + rowIndex);
}
grid._lastNavRowIndex = rowIndex;
}
};
handlers.handler = newHandler;
});
Try it here.
This is probably what you are looking for. When you want events related to the data, you have to look for the DataSource events. When you want events related to the UI, than you look at the Grid events.
I am building a pretty combobox with checkboxes and conditional entries. Everything works out alright, except for two features that I cannot figure out how to implement.
1) I would like to move the label inside the combobox, make it shift the values to the right, and appear in a slightly gray color.
2) I would like the value to ignore certain entries (group headers) selected. Those entries are there for functionality only - to select/unselect groups of other entries.
The entire project is in the zip file. You don't need a server, it's a client base app. Just download the archive, unpack, and launch app.html in your browser.
http://filesave.me/file/30586/project-zip.html
And here's a snapshot of what I would like to achieve.
Regarding your second issue, the best way I see is to override combobox onListSelectionChange to filter the values you don't want:
onListSelectionChange: function(list, selectedRecords) {
//Add the following line
selectedRecords = Ext.Array.filter(selectedRecords, function(rec){
return rec.data.parent!=0;
});
//Original code unchanged from here
var me = this,
isMulti = me.multiSelect,
hasRecords = selectedRecords.length > 0;
// Only react to selection if it is not called from setValue, and if our list is
// expanded (ignores changes to the selection model triggered elsewhere)
if (!me.ignoreSelection && me.isExpanded) {
if (!isMulti) {
Ext.defer(me.collapse, 1, me);
}
/*
* Only set the value here if we're in multi selection mode or we have
* a selection. Otherwise setValue will be called with an empty value
* which will cause the change event to fire twice.
*/
if (isMulti || hasRecords) {
me.setValue(selectedRecords, false);
}
if (hasRecords) {
me.fireEvent('select', me, selectedRecords);
}
me.inputEl.focus();
}
},
And change your onBoundlistItemClick to only select and deselect items in the boundlist not to setValue of the combo:
onBoundlistItemClick: function(dataview, record, item, index, e, eOpts) {
var chk = item.className.toString().indexOf('x-boundlist-selected') == -1;
if ( ! record.data.parent) {
var d = dataview.dataSource.data.items;
for (var i in d) {
var s = d[i].data;
if (s.parent == record.data.id) {
if (chk) { // select
dataview.getSelectionModel().select(d[i],true);
} else { // deselect
dataview.getSelectionModel().deselect(d[i]);
}
}
}
}
},
Regarding your first issue, it is easy to add the label using the displayTpl config option. But this will only add the text you need, without any style (grey color, etc). The combo is using a text input, which does not accept html tags. If you don't need the user to type text, than you may want to change the combo basic behavior and use another element instead of the text input.
I currently have a rather big Grid and am successfully using the RowExpander plugin to display complementary informations on certain rows. My problem is that it's not all rows that contain the aforementioned complementary informations and I do not wish the RowExpander to be active nor to show it's "+" icon if a particular data store's entry is empty. I tried using the conventional "renderer" property on the RowExpander object, but it did not work.
So basically, how can you have the RowExpander's icon and double click shown and activated only if a certain data store's field != ""?
Thanks in advance! =)
EDIT: I found a solution
As e-zinc stated it, part of the solution (for me at least) was to provide a custom renderer that would check my conditional field. Here is my RowExpander:
this.rowExpander = new Ext.ux.grid.RowExpander({
tpl: ...
renderer: function(v, p, record) {
if (record.get('listeRetourChaqueJour') != "") {
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"></div>';
} else {
p.id = '';
return ' ';
}
},
expandOnEnter: false,
expandOnDblClick: false
});
Now, the trick here is that for this particular Grid, I chose not to allow the expandOnEnter and expanOnDblClick since the RowExpander will sometimes not be rendered. Also, the CSS class of the grid cell that will hold the "+" icon is 'x-grid3-td-expander'. This is caused by the fact that the CSS class is automatically set to x-grid3-td-[id-of-column]. So, by setting the id to '' only when I'm not rendering the rowExpander, I'm also removing the gray background of the un-rendered cells. So, no double click, no enter, no icon, no gray-background. It really becomes as if there is strictly no RowExpander involved for the columns where my data store field is empty (when I want no RowExpander).
That did the trick for me. For someone that wishes to preserve the ID of the cell, or that wishes to keep the double click and enter events working, there is nothing else to do other than extending the class I guess. Hope this can help other people stuck in the position I was!
As e-zinc stated it, part of the solution (for me at least) was to provide a custom renderer that would check my conditional field. Here is my RowExpander:
this.rowExpander = new Ext.ux.grid.RowExpander({
tpl: ...
renderer: function(v, p, record) {
if (record.get('listeRetourChaqueJour') != "") {
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"></div>';
} else {
p.id = '';
return ' ';
}
},
expandOnEnter: false,
expandOnDblClick: false
});
Now, the trick here is that for this particular Grid, I chose not to allow the expandOnEnter and expandOnDblClick specifically since the RowExpander will sometimes not be rendered. Also, the CSS class of the grid cell that will hold the "+" icon is 'x-grid3-td-expander'. This is caused by the fact that the CSS class is automatically set to x-grid3-td-[id-of-column]. So, by setting the id to an empty string only when I'm not rendering the rowExpander, I'm also removing the gray background of the cells that won't offer any expanding. So, no double click, no enter, no icon, no gray-background. It really becomes as if there is strictly no RowExpander involved for the columns where my data store field is empty (when I want no RowExpander).
That did the trick for me. For someone that wishes to preserve the ID of the cell, or that wishes to keep the double click and enter events working, there is nothing else to do other than extending the RowExpander class in my opinion. Of course, one could also use Ext.override(), but then all instances of RowExpander would be hit by the override.
I have the same task, there is my solution
var rowExpander = new Ext.ux.grid.RowExpander({
renderer : function(v, p, record){
return record.get('relatedPageCount') > 0 ? '<div class="x-grid3-row-expander"> </div>' : ' ';
}
});
I have overridden render method which test relatedPageCount field in store and render + or white space.
I think I've found a cleaner solution.Give me a feedback pls :)
I extend the toggleRow method of RowExpander and if I match a condition avoid to toggle the row.Otherwise the standard flow continues
Ext.create('customplugins.grid.plugin.ClickRowExpander',{
pluginId : 'rowexpander',
rowBodyTpl : new Ext.XTemplate(
'<p><b>Last Modified By:</b> {usermodify}</p>',
'<p><b>User data:</b> {userdata}</p>',
'<p><b>Correlation ID:</b> {correlationid}</p>',
'<p><b>Description:</b> {descr}</p>'
),
toggleRow : function(rowIdx, record) {
if(record.get('directory')) return false;
customplugins.grid.plugin.ClickRowExpander.prototype.toggleRow.apply(this, arguments);
}
})
This version works in Ext JS 5 and 6 (classic)
One thing is to remove the +/- icon, which can be done via grid viewConfig:
getRowClass: function (record, rowIndex, rowParams, store) {
var yourFieldofChoice = record.get('yourFieldofChoice');
if (yourFieldofChoice == null) {
return 'hide-row-expander';
}
},
Define css for hide-row-expander:
.hide-row-expander .x-grid-row-expander {
visibility: hidden;
}
Now you disable expanding on enter key ('expandOnEnter' config is no longer supported in Ext JS 6) or double click by overriding toggleRow, or if you do not wish the override you create your custom rowexpander built on existing plugin:
Ext.define('RowExpander', {
extend: 'Ext.grid.plugin.RowExpander',
alias: 'plugin.myExpander',
init: function (grid) {
var me = this;
me.grid = grid;
me.callParent(arguments);
},
requiredFields: ['yourFieldofChoice'],
hasRequiredFields: function (rec) {
var valid = false;
Ext.each(this.requiredFields, function (field) {
if (!Ext.isEmpty(rec.get(field))) {
valid = true;
}
});
return valid;
},
toggleRow: function (rowIdx, record) {
var me = this, rec;
rec = Ext.isNumeric(rowIdx)? me.view.getStore().getAt(rowIdx) : me.view.getRecord(rowIdx);
if (me.hasRequiredFields(rec)) {
me.callParent(arguments);
}
}
});
I have handled the beforeexpand event inside the listeners of Ext.ux.grid.RowExpander. beforeexpand method got the whole row data injected. Checking the data conditionally we can return true or false. If we return false it wont expand otherwise it will do.
var expander = new Ext.ux.grid.RowExpander({
tpl: '<div class="ux-row-expander"></div>',
listeners: {
beforeexpand : function(expander, record, body, rowIndex){
var gpdata = record.data.GROUP_VALUES[1].COLUMN_VALUE
if(gpdata == null){
return false;
}
else{
return true;
}
}
}
});