Stack react-bootstrap accordian as horizontal - react-bootstrap

We have requirement where we have some category group which needs to be rendered as accordian and it should look like following;
On expansion, it needs to expand itself and show body. We created separate component which will accept array of string and few helper functions but all of this accordian rendered as vertical on parent component
import { ListGroup, ListGroupItem } from "react-bootstrap";
import { generateUniqueComponentKey } from "../../utils/Helper";
const RenderButtonGroup = ({btnGroups, handleFilterGroupClick, selectedFilterGroup}) => {
console.log('selectedFilterGroup ', selectedFilterGroup)
const renderBtgGroup = () => {
if (btnGroups){
let btnItems = btnGroups && btnGroups.map(item => {
let myKey = generateUniqueComponentKey();
return (
<>
<ListGroupItem
eventKey={myKey}
key={myKey}
variant="warning"
onClick={handleFilterGroupClick}
active={selectedFilterGroup[0] === item ? true : false}
>{item}</ListGroupItem>
</>
)
})
return(
<ListGroup>{btnItems}</ListGroup>
)
}
}
return (
<div className="d-flex">
{renderBtgGroup()}
</div>
)
}
export default RenderButtonGroup;
Not CSS expert so don't know how to achieve this. Thanks in advance

Use the Grid component with 3 columns: https://react-bootstrap.github.io/layout/grid/

Related

How to use mapStateToProp in react redux

I am creating search function, as a request I have to use mapStateToProps to get the data, how should I do in this file?
This is my code for that
import React, { useState, useEffect } from "react";
import useDebounce from "../../custom-hook/index";
import "../Search/index.css";
function Search() {
// State and setter for search term
const [searchTerm, setSearchTerm] = useState("");
// State and setter for search results
const [results, setResults] = useState([]);
// State for search status (whether there is a pending API request)
const [isSearching, setIsSearching] = useState(false);
// Run every 5ms
const debouncedSearchTerm = useDebounce(searchTerm, 500);
// Here's where the API call happens
// We use useEffect since this is an asynchronous action
useEffect(
() => {
// Make sure we have a value (user has entered something in input)
if (debouncedSearchTerm) {
// Set isSearching state
setIsSearching(true);
// Fire off our API call
searchCharacters(debouncedSearchTerm).then(results => {
// Set back to false since request finished
setIsSearching(false);
// Set results state
setResults(results);
});
} else {
setResults([]);
}
},
// This is the useEffect input array
[debouncedSearchTerm]
);
// Pretty standard UI with search input and results
return (
<div>
<input
placeholder="Search..."
className="search"
onChange={e => setSearchTerm(e.target.value)}
/>
{isSearching && <div>Searching ...</div>}
{results.map(result => (
<div key={result.id}>
<h4>{result.title}</h4>
<img
src={`${result.thumbnail.path}/portrait_incredible.${
result.thumbnail.extension}`}
alt="something"
/>
</div>
))}
</div>
);
}
// API search function
function searchCharacters(search) {
console.log(search);
}
export default Search;
As my code above, you can see that in return I have isSearching fucntion, it set by useState, but now I don't do like that, I have to set mapSateToProp to get prop from store
Please help me for this thank you so much
This piece of code is taken from react-redux documentation. You should import connect function form this library and then wrap your component.
function mapStateToProps(state) {
const { todos } = state
return { todoList: todos.allIds }
}
export default connect(mapStateToProps)(TodoList)
Updated:
Since you are using hooks useSelector might work better for you.

Listen for click or doubleclick of the widget

Is there a possibility to listen click or doubleclick event on the widget? I have followed the tutorial of creating the inline widget plugin and now I would like to listen for the click or doubleclick event on the widget from the following demo. https://ckeditor.com/docs/ckeditor5/latest/framework/guides/tutorials/implementing-an-inline-widget.html#demo
if you want to listen to click event in the editor, you should use ClickObserver. However, it's not enabled by default, so you should add it.
The code for listening on click on the placeholder element will look like this:
import ClickObserver from '#ckeditor/ckeditor5-engine/src/view/observer/clickobserver';
const view = editor.editing.view;
const viewDocument = view.document;
view.addObserver( ClickObserver );
editor.listenTo( viewDocument, 'click', ( evt, data ) => {
const modelElement = editor.editing.mapper.toModelElement( data.target);
if ( modelElement.name == 'placeholder' ) {
console.log( 'Placeholder has been clicked.' );
}
} );
double click is not enabled by default, but you can create a custom click observer. You just need to extend a generic DomEventObserver class by a native DOM dblclick event.
Here's the code for that:
import DomEventObserver from '#ckeditor/ckeditor5-engine/src/view/observer/domeventobserver';
class DoubleClickObserver extends DomEventObserver {
constructor( view ) {
super( view );
this.domEventType = 'dblclick';
}
onDomEvent( domEvent ) {
this.fire( domEvent.type, domEvent );
}
}
And then use it in the editor:
const view = editor.editing.view;
const viewDocument = view.document;
view.addObserver( DoubleClickObserver );
editor.listenTo( viewDocument, 'dblclick', () => {
console.log( 'Double click fired.' );
} );
Hope this helps you!

AsyncStorage - error (React Native)

I have a problem with persisting user's data in a react-native application I am currently working on with my team. We have an app with a list of events and when you click on an event, it opens up the event details page and shows a menu to choose yes/no/maybe (attendance menu) which the user can toggle to select whether or not they are attending the event. Our code looks like this:
import React from 'react';
import { AsyncStorage, Text, View, StyleSheet, Picker } from 'react-native';
export class PickerMenu extends React.Component {
state = { choice: '' }
componentDidMount = () => AsyncStorage.getItem('choice').then((value) => this.setState({ 'choice': value }))
setName = (value) => {
AsyncStorage.setItem('choice', value);
this.setState({ 'choice': value });
}
render() {
return (
<View>
<Picker selectedValue = {this.state.choice} onValueChange = {this.setName} itemStyle = {styles.item}>
<Picker.Item label = "Yes" value = "yes" />
<Picker.Item label = "Maybe" value = "maybe" />
<Picker.Item label = "No" value = "no" />
</Picker>
</View>
);
}
}
We are running the build on Xcode. We want the user's choice to still be present after the page has been refreshed or the app has closed - this works HOWEVER,the choice that the user has selected, stays the same for every event. E.g. if they select 'Maybe' for event 1, the selection 'maybe' is highlighted for every other event in our app.
We had an idea of grabbing unique event IDs and implementing this into the AsyncStorage function but we are unsure of how to do this.
Any help will be great and much appreciated - thank-you!
In this case you need to use the async/await to store at the async storage
like this:
setName = async (value) => {
await AsyncStorage.setItem('choice', value);
this.setState({ 'choice': value });
}
Hope i have helped!
Some useful links for you:
https://facebook.github.io/react-native/docs/asyncstorage.html

Re-rendering a single row of a list without re-rendering the entire list

we're trying to implement a contact list that works just like the new Material Design Google Contacts (you must enable the material design skin to see it) using material-ui.
Specifically we're trying to show a checkbox instead of the avatar on row hover.
We'd like to catch and re-render only the interested row (when hovered) and show the avatar/checkbox accordingly... this seems an easy task but we're not able to isolate the render to the hovered row (instead of re-rendering the entire list)
Do you have any suggestion on how to do something like this?
Our temporary solution uses a container component that handles the table:
When a row is hovered we capture it from onRowHover of the Table component and save it in the container state. This triggers a re-render of the entire list with really poor perfomance.
You can see a video of the issue here.
Here is a code sample:
import React from 'react'
import Avatar from 'material-ui/lib/avatar'
import Checkbox from 'material-ui/lib/checkbox'
import Table from 'material-ui/lib/table/table'
import TableHeaderColumn from 'material-ui/lib/table/table-header-column'
import TableRow from 'material-ui/lib/table/table-row'
import TableHeader from 'material-ui/lib/table/table-header'
import TableRowColumn from 'material-ui/lib/table/table-row-column'
import TableBody from 'material-ui/lib/table/table-body'
import R from 'ramda'
export default class ContactsList extends React.Component {
constructor (props) {
super(props)
this.state = { hoveredRow: 0 }
this.contacts = require('json!../../public/contacts.json').map((e) => e.user) // Our contact list array
}
_handleRowHover = (hoveredRow) => this.setState({ hoveredRow })
_renderTableRow = ({ hovered, username, email, picture }) => {
const checkBox = <Checkbox style={{ marginLeft: 8 }} />
const avatar = <Avatar src={picture} />
return (
<TableRow key={username}>
<TableRowColumn style={{ width: 24 }}>
{hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{username}</TableRowColumn>
<TableRowColumn>{email}</TableRowColumn>
</TableRow>
)
}
render = () =>
<Table
height='800px'
fixedHeader
multiSelectable
onRowHover={this._handleRowHover}
>
<TableHeader displaySelectAll enableSelectAll>
<TableRow>
<TableHeaderColumn>Nome</TableHeaderColumn>
<TableHeaderColumn>Email</TableHeaderColumn>
<TableHeaderColumn>Telefono</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} showRowHover>
{this.contacts.map((contact, index) => this._renderTableRow({
hovered: index === this.state.hoveredRow,
...contact }))
}
</TableBody>
</Table>
}
Thank you in advance.
You could wrap your rows into a new component implementing shouldComponentUpdate like so :
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
return this.props.hovered !== nextProps.hovered || ...; // check all props here
}
render() {
const { username, email, ...otherProps } = this.props;
return (
<TableRow { ...otherProps } >
<TableRowColumn style={{ width: 24 }}>
{this.props.hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{this.props.username}</TableRowColumn>
<TableRowColumn>{this.props.email}</TableRowColumn>
</TableRow>
);
}
}
Then you can use it in your ContactList component like so :
this.contacts.map((contact, index) => <ContactRow key={contact.username} {...contact} hovered={index === this.state.hoveredRow} />)
If you don't want to manually implement shouldComponentUpdate, you can use React's PureRenderMixin or check a lib like recompose which provides useful helpers like pure to do so.
EDIT
As pointed out by the OP and #Denis, the approach above doesn't play well with some features of the Table component. Specifically, TableBody does some manipulation on its children's children. A better approach would be to define your ContactRow component like so:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
// do your custom checks here
return true;
}
render() {
const { username, email, ...otherProps } = this.props;
return <TableRow { ...otherProps } />;
}
}
and then to use it like this
<ContactRow { ...myProps }>
<TableRowColumn>...</TableRowColumn>
</ContactRow>
But I guess having TableRow re-render only when necessary is a feature everyone would benefit from, so maybe a PR would be in order :)

How can I do preventDefault before firing a proxy event?

The task is the same as in
this post: capture click on a link and prevent the browser's default behavior. The answer given there was this:
Template:
<a on-click='sayHello' href="#">Activate!</button>
JS:
ractive.on( 'sayHello', function ( event ) {
event.original.preventDefault();
alert( 'Hello world!' );
});
The problem is that the sayHello event handler should not know about what the original event was. The whole point of proxying is that the event handler should not care about the type of the original event. For example, I change the link into a button, there's no more need for preventDefault.
So, my question is how I can invoke preventDefault before or after firing the event proxy.
A nice solution would be to fire several event proxies in a row (if it was possible):
Template:
<a on-click='preventDefault;sayHello' href="#">Activate!</button>
Js:
ractive.on( 'preventDefault', function ( event ) {
event.original.preventDefault();
});
ractive.on( 'sayHello', function ( event ) {
alert( 'Hello world!' );
});
Is this possible somehow? Or is there some other nice solution?
One approach might be to pass an argument that determines whether the default should be prevented:
template:
<a on-click='sayHello:true' href="#">Activate!</a>
<button on-click='sayHello'>Activate!</button>
code:
ractive.on( 'sayHello', function ( event, shouldPreventDefault ) {
if ( shouldPreventDefault ) {
event.original.preventDefault();
}
alert( 'Hello world!' );
});
Or, if you wanted to avoid the boolean trap you could do...
<a on-click='sayHello:{preventDefault:true}' href="#">Activate!</a>
...et cetera.
Chaining events is an interesting idea. I'm not convinced it belongs in the library, but you could certainly do something like
template:
<a on-click='multiple:["preventDefault","sayHello"]' href='#'>Activate!</a>
code:
ractive.on( 'multiple', function ( event, sequence ) {
var eventName, i, len;
len = sequence.length;
for ( i = 0; i < len; i += 1 ) {
this.fire( sequence[i], event );
}
});
This pattern could be expanded upon to include arguments if needed.
I solved it this way, but I'm not totally satisfied.
Template:
<a on-click='pd_and:sayHello' href="#">Activate!</button>
Js:
ractive.on( 'pd_and', function ( event, next ) {
event.original.preventDefault();
var i = args.indexOf(':'), arg;
if (i === -1) {
type = next;
arg = undefined;
} else {
type = next.substr(0, i);
arg = next.substr(i + 1);
}
return ractive.fire(type, event, arg);
});
If your goal is to encapsulate the preventDefault between template and method, then you can roll your own proxy event:
Ractive.events.clickpd = function ( node, fire ) {
node.addEventListener('click', click)
function click(e){
e.preventDefault()
fire({
node: node, //breaks Ractive if not included
original: e //optional
})
}
return {
teardown: function () {
node.removeEventListener('click', click)
}
}
}
And use it like so:
<a on-clickpd='sayGreeting'>{{greeting}}</a>
See http://jsfiddle.net/ka2Pu/2/ for full example.
Docs on custom events are here: http://docs.ractivejs.org/latest/ractive-events

Resources