I have a chart where one of the series provided is a 'total' sum value of the other values.
What I'm trying to is to have this column initially behind the others (or hidden) then bring it to the front when that series' legendItem is hovered.
So far I've been able to increase the zIndex on the series to bring the 'total' column to the front on 'mouseover' no problem, but triggering the inverse on 'mouseout' isn't working for me and the 'total' column stays in front.
I've also tried a simlar solution using .toFront(), this worked but was left with the same problem of moving the column back BEHIND on mouseout.
How might I correctly move this column to front/back on legendItem hover?
https://codesandbox.io/s/cold-night-9p0gk?file=/src/components/BarChart.vue:1272-1325
On 'mouseout' you can call toFront method on the stacked series only:
chart.series.forEach(s => {
if (s.name === 'Total') {
s.legendItem.on('mouseover', () => {
s.group.toFront();
});
s.legendItem.on('mouseout', () => {
s.chart.series.forEach(item => {
if (s !== item) {
item.group.toFront();
}
});
});
}
});
Live demo: http://jsfiddle.net/BlackLabel/mpduxwkh/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#toFront
I have a react app in which I'm trying to make each table row fade out whenever a corresponding button to that table row is clicked. The idea is that when a delete button on that row is clicked the whole row will fade out and the rest of the rows below it will move up (without creating any empty space) and if it is the last row that is being removed it will just fade out and table will become smaller.
Currently I'm trying to use refs and CSS to implement this feature, however right now if, for example, I click on "Delete" button in the second row - this row will be removed immediately without any animation, but at the same time the very last row's content will fade out according to the CSS styling that I set up and there will be an empty table row left there. Following that, any other element will be removed without any fade out animation.
So far I have done the following:
1) A component that create dynamic table rows (any necessary data, including the ref, are passed through useContext() from the component with the business logic):
function TableData() {
const { dataset, handleDeletion, wrapperRef } = React.useContext(Context)
return dataset.map((item, index) => {
const { id, name, year, color, pantone_value } = item
return (
<tr key={id} ref={wrapperRef}>
<td>{year}</td>
<td>{name}</td>
<td style={{ backgroundColor: color, width: "100px" }} />
<td>
<button
value={id}
type="button"
class="btn btn-outline-danger"
onClick={handleDeletion}>
<i class="far fa-trash-alt"></i>
</button>
</td>
</tr>
)
})
}
2) In the main component with business logic I set up the ref:
const wrapperRef = React.createRef()
3) And this is a function that handle a click of a delete button:
function handleDeletion(event) {
const id = event.currentTarget.value
const wrapper = wrapperRef.current
wrapper.classList.add('fade')
setDataset(prevDataset => prevDataset.filter(item => item.id !== parseInt(id)))
}
4) Simple CSS animation:
.fade {
opacity: 0;
transition: opacity 5000ms;
}
Could you please let me know what am I doing wrong here and what would need to be fix in order to make it work as planned?
Best regards,
Konstantin
P.S. I'm attaching the full code to this post as well (just in case) and here is the link to the pen with this project.
UPDATE:
1) I added the following className into my 'tr' component:
<tr key={id} className={toDelete && itemToDelete === id ? 'fade' : ''}>
2) In the main component I added the following state hooks:
const [ toDelete, setToDelete ] = React.useState(false)
const [ itemToDelete, setItemToDelete ] = React.useState('')
3) And changed the delete function to the following (with adding an extra useEffect that is applied with those hooks being changed):
function handleDeletion(event) {
const id = event.currentTarget.value
setToDelete(true)
setItemToDelete(id)
//setDataset(prevDataset => prevDataset.filter(item => item.id !== parseInt(id)))
}
React.useEffect(() => {
if (toDelete !== false && itemToDelete !== '') {
setTimeout(() => {
setDataset(prevDataset => prevDataset.filter(item => item.id !== parseInt(itemToDelete)))
}, 500)
}
}, [toDelete, itemToDelete])
I tried to do it different ways, including using setTimeout/setInterval, doing it without a separate useEffect hook in the handleDeletion function, etc. But still can't get the desired result, it is removing it without applying fading animation.
You create wrapperRef and you overwrite it with every map iteration so that it holds the reference on the last table row. That is why you see the last row fade out.
You would probably need an array of refs so that you have the ref for every row. Depending on how big the table is maybe you should look for other solutions because it not recommended to overuse refs
This approach is probably the best.
In addition to Darko's answer, you can also delete the row from the DOM after fading out.
setTimeout(function() { that.setState({ isDeleted: true })}, 500);
https://codepen.io/primaryobjects/pen/qBaMvrq
I am basically implementing a search functionality which is outside of the table, and want to highlight the rows if the search content matches something in the table row.
Also there are multiple tables on the page.
Here's my panel component.
<app-mystuff-dashboard-panel *ngFor="let group of initialGroupedSearchResults | objectFilter: { MyContentSetScope: 'Private', MyContentSetType : 'System', contentSetName: 'Clipboard' } | orderBy: 'contentSetName'"
[contentSet]="group"
[searchText]="searchText"></app-mystuff-dashboard-panel>
And in this above component I am running ngOnChanges, to get the searchText changes(which is a string)
I have tried using [rowClass]="getRowClass" in the ngx-dtatable input, but that function doesn't fire always, like how ngClass function changes everytime anything changes.
getRowClass(row) {
return _includes(row.DynamicProperties.myContentItemKey, this.searchText) ? 'yellowbackground' : 'bluebackground';
}
The row parameter in this function is undefined mostly. Also I have around 100 rows on around 3 tables, and this getRowClass funciton fires only around 5 times for 3 tables combined when the searchText changes..
I have tried re-rendering the table all over again onChange, that doesn't work as well.
if ((changes.searchText && (changes.searchText.currentValue !== changes.searchText.previousValue))) {
console.log(this.searchText);
this.items = this.items.slice();
this.items = [...this.items];
this.items.push({});
this.items.splice(-1, 1);1
this.items = [...this.items];
this.changeDetector.detectChanges();
}
Is there any way to let users to adjust column width of Ant Design Table by drag and drop?
I found some examples that about is for column sorting, but not for column resizing.
Please help, thanks!
UPDATED # 2018-11-13
There is an official example of resizing table column now:
https://ant.design/components/table/#components-table-demo-resizable-column
I have made a working sample - its far from perfect and needs a lot of optimization. Basically you need to use the onHeaderCell and capture onMouseDown, onMouseUp and onMouseMove.
onMouseMove we call setState and basically trigger a re-render with the new column width.
https://codesandbox.io/s/j2zw9nn5w9
onHeaderCell: column => {
let colw = this.state.columnWidth;
return {
onMouseDown: e => {
this.mouseDownX = e.clientX;
this.beginDrag = true;
},
onMouseUp: () => {
this.beginDrag = false;
},
onMouseMove: e => {
if(this.beginDrag === true) {
this.updateColumnWidth(colw +
Math.round((e.clientX - this.mouseDownX)*.05));
}
}
};
}
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;
}
}
}
});