I am using react-table in my application.
I am stuck in doing one thing i.e. changing the CSS of columns while a column is being resized.
Currently when you resize a column only cursor changes. What I want is to add border to the selected column.
I searched for this on SO and google as well. But couldn't find anything useful. And In the documentation as well nothing is mentioned about this topic as well.
Update
Now I am able to add border while dragging the column while resizing. I am able to do so by adding and removing the class.
What I did to do so:
Created a var in the state for className:
this.state = {
addBorder: null
}
Passed this class name in my column:
const columns = [{
Header: 'Name',
accessor: 'name', // String-based value accessors!,
headerClassName: this.state.addBorder,
className: this.state.addBorder
}, {
Header: 'Age',
accessor: 'age',
Cell: props => <span className='number'>{2}</span> // Custom cell components!
}, {
id: 'friendName', // Required because our accessor is not a string
Header: 'Friend Name',
accessor: d => d.friend.name // Custom value accessors!
}, {
Header: props => <span>Friend Age</span>, // Custom header components!
accessor: 'friend.age'
}];
return (
<div onMouseUp={this.handleMouseUp}>
<ReactTable
data={data}
columns={columns}
resizable={true}
onResizedChange={(col, e) => {
const column = col[col.length-1];
this.setState({addBorder: column.id})
}} />
</div>
)
}
To remove the class when dragging ends:
handleMouseUp (e) {
this.setState({addBorder: null});
}
But I am still not able to add border on hover.
Now, I am sending my custom HTML in header props. And in my HTML I have made an extra div. And I have moved this div to right. And on hover of this div, I am emitting mouse events and changing CSS accordingly.
But Existing div in the header that is responsible for resizing column is overlapping with my Div.
Header: props => <div className='header-div'> Name <div onMouseOver = {() => {
console.log('mose');
this.setState({className: 'addBorder'});
}} className='hover-div' onMouseOut = {() => {console.log('sdasd');this.setState({className: null});}}> </div></div> ,
From what I understand, you want to add some border when you hover over a column header. If my understanding is correct, you can use :hover pseudo selector over the header class
.hdrCls:hover {
border: 2px solid rgba(0,0,0,0.6) !important;
}
Update :
You can manipulate state in onResizedChange handler exposed by react-table
onResizedChange={(newResized, event) => {
let resizedCol = newResized.slice(-1)[0].id;
if(this.state.activeCol !== resizedCol) {
this.setState({
activeCol: resizedCol,
resizing: true
})
}
}}
Also, make sure you have to make the resizing state to false on mouseup event. For that I have come up with the below solution.
componentDidUpdate(props, state) {
if (this.state.resizing && !state.resizing) {
document.addEventListener('mouseup', this.onMouseUp);
} else if (!this.state.resizing && state.resizing) {
document.removeEventListener('mouseup', this.onMouseUp);
}
}
onMouseUp = (evt) => {
this.setState({
activeCol: '',
resizing: false
});
evt.stopPropagation();
evt.preventDefault();
}
For reference:
const ReactTable = window.ReactTable.default
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
activeCol: '',
resizing: false
}
}
componentDidUpdate(props, state) {
if (this.state.resizing && !state.resizing) {
document.addEventListener('mouseup', this.onMouseUp);
} else if (!this.state.resizing && state.resizing) {
document.removeEventListener('mouseup', this.onMouseUp);
}
}
onMouseUp = (evt) => {
this.setState({
activeCol: '',
resizing: false
});
evt.stopPropagation();
evt.preventDefault();
}
render() {
const data = [{
name:"Mark",
age:24
},
{
name:"Derek",
age:26
}]
const columns = [{
Header: 'Name',
accessor: 'name', // String-based value accessors!,
headerClassName: 'hdrCls',
className: (this.state.activeCol === 'name') && this.state.resizing ? 'borderCellCls' : 'defaultCellCls'
}, {
Header: 'Age',
accessor: 'age',
headerClassName: 'hdrCls',
className: (this.state.activeCol === 'age') && this.state.resizing ? 'borderCellCls' : 'defaultCellCls'
}];
return <ReactTable
data = { data }
columns = { columns }
showPagination= {false}
onResizedChange={(newResized, event) => {
let resizedCol = newResized.slice(-1)[0].id;
if(this.state.activeCol !== resizedCol) {
this.setState({
activeCol: resizedCol,
resizing: true
})
}
}}
/>
}
}
ReactDOM.render( < App / > , document.getElementById("app"))
.hdrCls:hover {
border: 2px solid rgba(0,0,0,0.6) !important;
}
.borderCellCls {
border-right: 2px solid rgba(0,0,0,0.6) !important;
border-left: 2px solid rgba(0,0,0,0.6) !important;
}
.defaultCellCls {
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-table/6.7.6/react-table.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/react-table/6.7.6/react-table.css"></link>
<div id="app"></div>
You can play around with CSS. Hope this is what you want and hope this helps.
Update:
I think you have to play with CSS to achieve what you desire.
.borderCellCls {
border-right: 2px solid rgba(0,0,0,0.6) !important;
border-left: 2px solid rgba(0,0,0,0.6) !important;
}
If you are here to find out how to set className to a column cell (with the react-table), here is the solution:
1)
<tr
{...row.getRowProps()}
>
{row.cells.map((cell) => (
<td
{...cell.getCellProps([
{
className: cell.column.className, // pay attention to this
style: cell.column.style,
// set here your other custom props
},
])}
>
{cell.render('Cell')}
</td>
))}
</tr>
2)
const columns = React.useMemo(
() => [
{
Header: 'Date',
accessor: 'date',
minWidth: 70,
className: 'text-dark fw-bolder fs-6 min-w-70px', // pass className props here
headerClassName: 'text-muted', // or another props like this one
}]
<Table columns={columns} ... />
And finally, those props will be passed to your cells
For TypeScript support follow the instructions in DefinitelyTyped, ie. create the file /src/types/react-table-config.d.ts with the content from the instructions, then add the following to it to support custom properties on your column (add more properties in the last line as required):
// Added to support classes to template from:
// https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-table
export interface ColumnInstance<
D extends Record<string, unknown> = Record<string, unknown>
> extends Omit<ColumnInterface<D>, 'id'>,
ColumnInterfaceBasedOnValue<D>,
UseTableColumnProps<D>,
Record<headerClassName | className, string> {}
Related
I want to hide the selected option in the Dropdown, the option should not appear in the next dropdown. For an example, there are 2 dropdowns, in the first dropdown - i have selected "Hockey" then "hockey" should not be shown in the second dropdown, It should show only "Baseball and badminton".
My JSON data will be appearing in this way:
"details": [
{ "id": "12wer1", "name": "ABC", "age": 15, "game": "badminton" },
{ "id": "78hbg5", "name": "FRE", "age": 21, "game": "Hockey" }
]
Here is the sample Code:
let games = [{ game: "Baseball"}, { game: "Hockey"}, { game: "badminton" }];
class Field extends React.Component {
constructor(props) {
super(props);
this.state = {
details: [{id: '', name: '', age: '', game: ''}]
}
}
...
...
render() {
return (
...
...
{this.state.details.map((y) => (
<Autocomplete
style={{ witdth: 200 }}
options={games}
getOptionLabel={(option) => option.game}
onChange={(value) =>this.onGameChange(value, y.id)}
value={games.filter(i=> i.game=== y.game)}
renderInput={(params) =>
<TextField {...params} label="Games" margin="normal" />
}
/>))}
...
...
)
}
}
onGameChange = (e, id)=> {
let games = this.state.details;
let data = games.find(i => i.id === id);
if (data) {
data.game = value.game;
}
this.setState({ details: games });
}
I have no idea, how to hide the selected option, can anyone help me in this query?
Thanks! in advance.
A possible solution would be to
create an array and store the values in an array when the user selects autocomplete
while passing options, filter the values that have been passed to other autocompletes.
const ary = [111,222,333];
let obj = [{id: 111},{id: 222}];
const i = 1; // this is your index in loop
const ary2 = ary.slice()
ary2.splice(i,1);
console.log(obj.filter((o) => !ary.includes(o.name))); // this should be given to our options list in autocomplete
you can hide this is in CSS easily no need to do anything in ReactJS
autocomplete renders as an unordered list so something like this
.panel > ul > li:first-child {
display:none;
}
I'm using ng2-smart-table to display some data, i've set the selectMode to 'multi' in order to have checkboxes on the left side. In the data i have an array of objects which come with a property "set" which is a boolean and can either be true or false, how do i disable the checkbox if the set property is true? Is there a way to do this?
I've already tried making a new renderComponent etc but then i lose the selectAll functionality plus with a renderComponent the selectRow works different.
Here's a link: https://stackblitz.com/edit/angular-ndmxxg
I have put a button on the Top, which is initialized to true, when you press it, it will disable all the checkboxes;
NOTE: I have set this on click of a button so that you see it in action; If you want to do it after getting a boolean variable from the parent or by-default, you'd have to do this inside ngAfterViewInit()... since we'd have to wait for the ng2-smart-table to be rendered and ready; i left a comment in my stackblitz about it also;
relevant HTML:
<h3>
Event Response in Console
</h3>
<button (click)="onClick()"> Disable checkbox </button>
<hr/>
<ng2-smart-table [settings]="settings" [source]="data" (deleteConfirm)="onDeleteConfirm($event)" (editConfirm)="onSaveConfirm($event)"
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="onRowSelect($event)">
relevant TS:
import { Component, Renderer2, ElementRef, ViewChild } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedMode: boolean = true;
// This will contain selected rows
selectedRows: any;
constructor(private renderer2: Renderer2, private e: ElementRef) { }
ngAfterViewInit() { }
disableCheckboxes() {
var checkbox = this.e.nativeElement.querySelectorAll('input[type=checkbox]');
checkbox.forEach((element, index) => {
/* disable the select all checkbox */
if (index ==0){this.renderer2.setAttribute(element, "disabled", "true");}
/* disable the checkbox if set column is false */
if (index >0 && this.data[index-1].set == false) {
this.renderer2.setAttribute(element, "disabled", "true");
}
});
}
settings = {
selectMode: 'multi',
delete: {
confirmDelete: true,
deleteButtonContent: 'Delete data',
saveButtonContent: 'save',
cancelButtonContent: 'cancel'
},
add: {
confirmCreate: true,
},
edit: {
confirmSave: true,
},
columns: {
id: { title: 'ID', },
name: { title: 'Full Name', },
email: { title: 'Email', },
set: { title: 'Set', }
},
};
data = [
{
id: 1,
name: "Leanne Graham",
email: "Sincere#april.biz",
set: true
},
{
id: 2,
name: "Ervin Howell",
email: "Shanna#melissa.tv",
set: false
},
// ... list of items
{
id: 11,
name: "Nicholas DuBuque",
email: "Rey.Padberg#rosamond.biz",
set: false
}
];
// UserRowSelected Event handler
onRowSelect(event) {
this.selectedRows = event.selected;
}
// Get Selected button click handler
onClick() {
// It will console all the selected rows
this.selectedMode = false;
this.disableCheckboxes();
}
onDeleteConfirm(event) {
console.log("Delete Event In Console")
console.log(event);
if (window.confirm('Are you sure you want to delete?')) {
event.confirm.resolve();
} else {
event.confirm.reject();
}
}
onCreateConfirm(event) {
console.log("Create Event In Console")
console.log(event);
}
onSaveConfirm(event) {
console.log("Edit Event In Console")
console.log(event);
}
}
complete working stackblitz here
Update (in light of questioner's comment below):
relevant CSS:
::ng-deep table tr td:nth-of-type(1),
::ng-deep table tr th:nth-of-type(1)
{ padding:0 !important; display: block;height: 13px; position: relative;}
::ng-deep table tr td:nth-of-type(1) input,
::ng-deep table tr th:nth-of-type(1) input
{ margin:0 !important; position: absolute; top: 15px;}
::ng-deep table tr td:nth-of-type(2),
::ng-deep table tr th:nth-of-type(2)
{ padding: 0 0 0 20px !important;}
I'm looking to add a react element with H.ui.Control. Is this possible? and how might it be done?
// sudo code of what I did
componentDidMount() {
...
let button = new H.ui.Control(this.myButtonControl);
button.setPosition('top-left');
this._ui.addControl('button-control', button);
...
}
myButtonControl() {
return <button className="H_btn">Hello World</button>
}
A new <div class="H_ctl"></div>, appears where the control was suppose to be, but not the button.
While it's not exactly what I wanted to do, I did find a solution. I created a generic class that extends H.ui.Control, in this case ButtonGroupControl.
class ButtonGroupControl extends H.ui.Control {
constructor(buttons: []) {
super();
this._buttons = buttons;
this.addClass('H_grp');
}
renderInternal(el, doc) {
this._buttons.forEach((button, i) => {
let btn = doc.createElement('button');
btn.className = 'H_btn';
btn.innerText = this._buttons[i].label;
btn.onclick = this._buttons[i].callback;
el.appendChild(btn);
})
super.renderInternal(el, doc);
}
}
export default ButtonGroupControl;
Then, inside my map component, I created passed array of items into the control, like so:
const mapToolsControl: ButtonGroupControl = new ButtonGroupControl([
{
label: 'Add Field',
callback: () => {
console.log('callback: adding field');
}
},
{
label: 'Remove Field',
callback: () => {
console.log('callback: remove field');
}
}
]);
Lastly, I added the control to the map like:
this._map.addControl('map-tools-control', mapToolsControl);
This results in the following (it's a link because I don't have enough points to embed yet):
Screenshot of Result
Here is what i have done (adding two buttons to the map)
var U_I = new H.ui.UI(map);
var container = new H.ui.Control();
container.addClass('here-ctrl here-ctrl-group');
var button = new H.ui.base.Element('button', 'here-ctrl-icon map_control');
container.addChild(button);
button.addEventListener('click', function() { alert(1); });
var button = new H.ui.base.Element('button', 'here-ctrl-icon map_center');
container.addChild(button);
button.addEventListener('click', function() { alert(2); });
container.setAlignment('top-right');
U_I.addControl('myControls', container );
U_I.addControl('ScaleBar', new H.ui.ScaleBar() );
the rendering is made by css (here is an extract)
button.here-ctrl-icon {
width: 25px;
height: 25px;
margin: 2px 0 0 2px;
}
.map_control { background: url("images/map_control.png") no-repeat scroll 0 0 transparent; }
.map_center { background: url("images/map_center.png") no-repeat scroll 0 0 transparent; }
H.ui.base.Button(); is not working ... it creates a div
It is not possible to add attributes to button such as alt or title thru the api.
I still have to deal with the addEventListener ... (not working !)
the result :
my new nice controls
I have the following code for retrieving data of the clicked row:
<ReactTable
getTdProps={(state, rowInfo, column, instance) => {
return {
onClick: (e, handleOriginal) => {
if (typeof rowInfo !== "undefined") this.rowClick(rowInfo.row.RecipeName);
if (handleOriginal) {
handleOriginal()
}
}
}
}}
How can I change the background color of the clicked row? Or what is the best way to highlight the clicked row?
Please see here for an answer: Select row on click react-table
Here is my code:
First of all you need a state:
this.state = {
selected: -1
};
-1 is important because otherwise the row with the index 0 will be highlighted without clicking on it.
And getTdProps looks like this:
getTrProps={(state, rowInfo, column, instance) => {
if (typeof rowInfo !== "undefined") {
return {
onClick: (e, handleOriginal) => {
this.setState({
selected: rowInfo.index
});
if (handleOriginal) {
handleOriginal()
}
},
style: {
background: rowInfo.index === this.state.selected ? '#00afec' : 'white',
color: rowInfo.index === this.state.selected ? 'white' : 'black'
},
}
}
else {
return {
onClick: (e, handleOriginal) => {
if (handleOriginal) {
handleOriginal()
}
},
style: {
background: 'white',
color: 'black'
},
}
}
}}
I tried several ways to set an icon, in the displayfield, when an item of the combo is selected with not luck, this is the fiddle for anyone to want try to help with this. very much appreciated any light.
fiddle example
The only solution is to transform the input type combo in a div with this:
fieldSubTpl: [
'<div class="{hiddenDataCls}" role="presentation"></div>',
'<div id="{id}" type="{type}" style="background-color:white; font-size:1.1em; line-height: 2.1em;" ',
'<tpl if="size">size="{size}" </tpl>',
'<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
'class="{fieldCls} {typeCls}" autocomplete="off"></div>',
'<div id="{cmpId}-triggerWrap" class="{triggerWrapCls}" role="presentation">',
'{triggerEl}',
'<div class="{clearCls}" role="presentation"></div>',
'</div>', {
compiled: true,
disableFormats: true
}
],
Override the setRawValue method of the combo like this:
setRawValue: function (value) {
var me = this;
me.rawValue = value;
// Some Field subclasses may not render an inputEl
if (me.inputEl) {
// me.inputEl.dom.value = value;
// use innerHTML
me.inputEl.dom.innerHTML = value;
}
return value;
},
and style your fake combo div like you want.
Thats because an input on HTML can't have HTML like value inside it.
Keep attenction, the get Value method will return you the HTML inside the div, and maybe you should also override it, but thats the only one method.
You will be able to get the selected value with this method:
Ext.fly(combo.getId()+'-inputEl').dom.innerHTML.replace(/<(.|\n)*?>/gm, '');
If I were you I would like to do something like this:
combo.getMyValue();
So add this property to your combo:
getMyValue:function(){
var combo=this;
if(Ext.fly(combo.id+'-inputEl'))
return Ext.fly(combo.id+'-inputEl').dom.innerHTML.replace(/<(.|\n)*?>/gm, '');
},
Here is a working fiddle
Perhaps my solution is similar to a hack, but it works in 6.7.0 and is a bit simpler.
Tested in Chrome. Theme - Material. For another theme will require minor improvements.
Sencha Fiddle live example
Ext.application({
name: 'Fiddle',
launch: function () {
var store = new Ext.data.Store({
fields: [{
name: 'class',
convert: function (value, model) {
if (value && model) {
var name = value
.replace(/(-o-)|(-o$)/g, '-outlined-')
.replace(/-/g, ' ')
.slice(3)
.trim();
model.data.name = name.charAt(0).toUpperCase() + name.slice(1);
return value;
}
}
}, {
name: 'name'
}],
data: [{
class: 'fa-address-book'
}, {
class: 'fa-address-book-o'
}, {
class: 'fa-address-card'
}]
});
var form = Ext.create('Ext.form.Panel', {
fullscreen: true,
referenceHolder: true,
items: [{
xtype: 'combobox',
id: 'iconcombo',
queryMode: 'local',
editable: false,
width: 300,
valueField: 'class',
displayField: 'name',
store: store,
itemTpl: '<div><i class="fa {class}"></i> {name}</div>',
afterRender: () => {
var component = Ext.getCmp('iconcombo');
var element = document.createElement('div');
element.className = 'x-input-el';
element.addEventListener('click', () => component.expand());
component.inputElement.parent().dom.prepend(element);
component.inputElement.hide();
component.addListener(
'change', (me, newValue, oldValue) => {
component.updateInputValue.call(me, newValue, oldValue);
},
component
);
var method = component.updateInputValue;
component.updateInputValue = (value, oldValue) => {
method.call(component, value, oldValue);
var selection = component.getSelection();
if (selection) {
element.innerHTML =
'<div><i class="fa ' + selection.get('class') + '"></i> ' + selection.get('name') + '</div>';
}
};
}
}, {
xtype: 'button',
text: 'getValue',
margin: '30 0 0 0',
handler: function (component) {
var combo = Ext.getCmp('iconcombo');
alert(combo.getValue());
}
}]
});
form.show();
}
});