How to send props dynamically to styles hook in material UI? - javascript

I am trying to add dynamic background images to divs. I am mapping over an array of objects to return divs. How do I add a background image to the div based on the image URL in the object?
Here is the code.
const useStyles = makeStyles({
div: (props) => ({
width: "100px",
height: 0,
paddingBottom: "100px",
backgroundImage: "how do i know which image to pull",
}),
});
let arr = [
{ photo_url: "some url" },
{ photo_url: "some url" },
{ photo_url: "some url" },
];
function Test() {
const classes = useStyles("how do i send image of that specific object");
return arr.map((obj) => <div className={classes.div}></div>);
}
EDIT: For the sake of simplicity, I chose to use style prop to add dynamic styles and className prop to add generic styles that are common to all divs.

Send the array to useStyles:
const classes = useStyles(array);
Now inside the makeStyles receive the array:
const useStyles = makeStyles(array=>{
let divStyles={}
// create dynamic styles based on the index of the .div class:
array.forEach((bgUrl,index)=>{
divStyles[`&:nth-child(${index+1})`]={ // because the index starts at zero
width: "100px",
height: 0,
paddingBottom: "100px",
backgroundImage: bgUrl,
}
})
return {div:divStyles}
});

Related

React JS create dynamic element on json loop

I am using react JS to build a form creator. Right now I'm focused on the process of rendering the element inside the form, basically it's a div containing some other divs, as a child.
I have my main component (Form) and some child that can be different components, like textbox or dropdown, and so on, with a set of common properties, and a set of specific properties (for instance the dropdown options are not inserted in the textbox component).
I'm also fetching the data from an API that respond with a json with a "type" parameter ("textbox", "dropdown", etc).
I'm looping on the json, and for sure I can just check the type and IF it's textbox create a textbox, IF it's a dropdown create a dropdown... But i think there is a better solution.
This is my code:
import {useState, useEffect} from 'react';
import axios from 'axios';
import Textbox from './Textbox';
import Dropdown from './Dropdown';
function Form(){
const [loading, setLoading] = useState(true);
const [components, setComponents] = useState([]);
useEffect(() => {
if(loading){
fetchComponents()
}
});
const fetchComponents = () => {
axios({
method: 'get',
url: 'http://localhost:8000/api/position'
})
.then(res => {
setComponents(res.data);
setLoading(false);
});
}
const allItems = () => {
return(
components.map((component, key) => {
/** THIS IS WHERE I WANT TO CREATE THE ELEMENT DYNAMICALLY */
const options = JSON.parse(component.options);
if(component.type === 'textbox'){
return <Textbox
key={component.id}
id={ component.id}
name={component.name}
default={{
x: options.position.x,
y: options.position.y,
width: options.size.width,
height: options.size.height
}}
minWidth={100}
minHeight={100}
resizeGrid={[20, 20]}
dragGrid={[20, 20]}
label={component.label}
value={component.value}
bounds="window"
/>
}
if(component.type === 'dropdown'){
return <Dropdown
key={component.id}
id={ component.id}
name={component.name}
default={{
x: options.position.x,
y: options.position.y,
width: options.size.width,
height: options.size.height
}}
minWidth={100}
minHeight={100}
resizeGrid={[20, 20]}
dragGrid={[20, 20]}
label={component.label}
value={component.value}
dropdown_options={component.dropdown_options}
bounds="window"
/>
}
})
)
}
return(
<div>
<div className="box background-quaderno" style={{height: '500px', width: '500px', position: 'relative', overflow: 'auto', padding: '0'}}>
<div style={{height: '100%', width: '100%', padding: '0px', overflow: 'hidden'}}>
{loading ? <></> : allItems()}
</div>
</div>
</div>
)
}
export default Form;
Thanks!

How can I change background-style of div with a selection box?

On my current project in React I need to have the possibility to change the background-style (image, width, height etc.) from a 'div' to nine different options.
I've tried it with following code:
const styleOptions = [
{
backgroundImage: "",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
//backgroundSize: "650px 800px",
backgroundSize: "cover"
},
{
backgroundImage: "url(/PicturesPattern/Picture1.png)",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
//backgroundSize: "650px 800px",
backgroundSize: "cover"
},
...//same code for nine Pictures, only diffrent size values
]
class Example extends React.Component {
state = {
...
...
//for selectionField
isDisabled: false,
backgroundStyle: styleOptions[0]
};
...
...
...
onSelectChange = (event) => {
this.setState({
...
currentStyle: styleOptions[event.value + 1]
});
};
render() {
return (
<>
//div for selectionField
<div className="forBackground" style{this.backgroundStyle}>
...
...
//other code
...
...
</>
)
}
}
But on any change from my selection field, there isnt happening anything to the background of the specific div.
When I put "styleOptions[x]" for x every index possible, I get the specific background image and other style options.
What am I doing wrong?
Hope someone can help me :)
Well, in the onSelectChange function and in the event.value + 1 part you are adding two different types of variables (string and integer). As a result the background styles of div would not change properly. First, you should write this function as below
onSelectChange = (event) => {
this.setState({
backgroundStyle: styleOptions[parseInt(event.target.value) + 1],
});
};
Then, style{this.backgroundStyle} should be changed to style={this.state.backgroundStyle}. I also added the height attributes and assigned a backgroundColor to make the div and changes more visible
But there were also other minor modifications. So, check the sandbox of the corrected version of your code.

Convert a JSX.Element to an HTMLElement

I want to use this library for my react js project https://www.npmjs.com/package/html-to-image.
In my case I want to use a var that contain a simple div and export It as an Image, but there's a problem: my var (node) is a JSX.Element and the "toPng" method of the library accept as a parameter an HTMLElement.
I know that the doc of the library suggests to use methods like this to get an HTMLElement: var node = document.getElementById('my-node') but in my case I can't do something like this because my node isn't in the document.
So there's a way to convert a JSX.Element to an HTMLElement? Thanks in advance ;)
To do that, use renderToStaticMarkup from the react-dom/server library.
import { renderToStaticMarkup } from "react-dom/server"
const output = document.createElement('p')
const staticElement = renderToStaticMarkup(reactElement)
output.innerHTML = staticElement
Question might be old, but while looking for a solution for this I found an elegant way to render a hidden element as an image.
render() {
return (
<div>
<div
id="image-template"
className="d-none"
style={{
width: "200px",
height: "200px",
display: "flex",
justifyContent: "center",
alignItems: "center",
color: "white",
fontSize: "10px",
textAlign: "center",
backgroundColor: "green",
}}
>
<span style={{ margin: "20px" }}>{"Hey, an image rendered but not visible!"}</span>
</div>
</div>
);
}
With the class d-none (from bootstrap) the element is hidden to the user, so it does not matter where you place it. d-none in bootstrap is basically display: none !important;.
Now add a function to create an image:
async createImage() {
const template = document.getElementById("image-template").cloneNode(true);
template.classList.remove("d-none");
const base64Image = await htmlToImage.toPng(template, {
height: 200,
width: 200,
});
// Do whatever you want to do with your image
}
The element is cloned and the d-none/display: none removed to be able to render it with htmlToImage. The result is the base64 data you can use whereever you want.

How to make chat like UI with chat bubbles in React JS

I have some JSON data in dummyData. I am not sure how can I place the chat bubbles on left and right according to the direction. I am using Material UI and context API. Image for the reference. I don't want to use any library other than material UI.
Currently, every chat bubble is positioned to the left. How to position bubbles according to the direction. Code so far (CodeSandbox):
import React from 'react';
import makeStyles from '#material-ui/core/styles/makeStyles';
const useStyles = makeStyles(theme => ({
container: {
bottom: 0,
position: 'fixed'
},
bubbleContainer: {
width: '100%'
},
bubble: {
border: '0.5px solid black',
borderRadius: '10px',
margin: '5px',
padding: '10px',
display: 'inline-block'
}
}));
const ChatLayout = () => {
const classes = useStyles();
const dummyData = [
{
message: '1: This should be in left',
direction: 'left'
},
{
message: '2: This should be in right',
direction: 'right'
},
{
message: '3: This should be in left again',
direction: 'left'
}
];
const chatBubbles = dummyData.map((obj, i = 0) => (
<div className={classes.bubbleContainer}>
<div key={i++} className={classes.bubble}>
<div className={classes.button}>{obj.message}</div>
</div>
</div>
));
return <div className={classes.container}>{chatBubbles}</div>;
};
export default ChatLayout;
You can create separate div of chat bubble and apply CSS. And where you are receiving messages append the bubble div to your user list.

How to create button with text under icon by reactjs?

Now, I have component like this:
code of it:
import React from "react";
import {withStyles} from "material-ui/styles";
import Settings from "material-ui-icons/Settings";
import Button from "material-ui/Button";
const styles = {
button: {
color: "primary",
height: 95,
width: 95,
disableRipple: "true",
focusRipple: "true",
},
icon: {
height: 35,
width: 35,
display: "block",
float: "none",
},
text: {
height: 35,
width: 35,
display: "block",
float: "none",
marginTop: 10,
},
};
/* eslint-disable react/prop-types */
const IconedLabel = ({classes}) => (
<section>
<Button className={classes.iconButton} variant="raised" color="primary">
<Settings className={classes.icon}/>
<div className={classes.text}>Message</div>
</Button>
</section>
);
export default withStyles(styles)(IconedLabel);
But need to button, that in top part contains icon and text message in bottom.
I use reactjs and material-ui lib from here https://material-ui-next.com/demos/buttons/
The Button component uses flexbox to control the layout/alignment of content. To align the content vertically (so the icon is above the text), you can simply change the flex-direction to column.
This style needs to be applied to an element inside the button component, not to the root element. You can use the classes property to override all of the styles in a component.
In this case, you want to add flexDirection: column to the label class.
Documentation on class overrides in material ui v1
Here's a working example. Hope it helps.
const [React, ReactDOM, Button, Settings, withStyles] = [window.React, window.ReactDOM, window['material-ui'].Button, ({className}) => <i className={`material-icons ${className}`}>settings</i>, window['material-ui'].withStyles]
// Ignore code above this line
const styles = theme => ({
button: {
height: 95, // setting height/width is optional
},
label: {
// Aligns the content of the button vertically.
flexDirection: 'column'
},
icon: {
fontSize: '32px !important',
marginBottom: theme.spacing.unit
}
})
const CustomButton = ({ classes }) => (
<Button
/* Use classes property to inject custom styles */
classes={{ root: classes.button, label: classes.label }}
variant="raised"
color="primary"
disableRipple={true}
>
<Settings className={classes.icon} />
Message
</Button>
)
const WrappedCustomButton = withStyles(styles)(CustomButton)
ReactDOM.render(<WrappedCustomButton />, document.querySelector('#root'))
<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://unpkg.com/material-ui#1.0.0-beta.40/umd/material-ui.production.min.js"></script><link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"><div id="root" />
A (potentially bad) solution would simply be:
.MuiIconButton-label {
flex-direction: column
}
I say bad, because you might want to use it in it's standard format elsewhere.
What I opted to do was add a class name nav-bar-icon-wrapper to the IconButton & set the flex direction in it's parent:
.nav-bar-icon-wrapper {
flex-direction: column
}
.MuiIconButton-label {
flex-direction: inherit
}
If I run into instance later where I want the icon/label button to be standard, I'll just add a new class default-icon-wrapper and css that handles that:
.default-icon-wrapper {
flex-direction: row
}
FWIW:
I preach the BEM http://getbem.com/introduction/ convention AND that whenever you make a component, you add an optional modifier prop.
I have functions in a shared dir that looks these:
export function BEMifyThis(modifier) {
return (klass) => BEMify(klass, modifier)
}
export function BEMify(klass, modifier=false) {
if (modifier) {
klass += ` ${klass}-${modifier}`
}
return klass
}
Then I use that everywhere in my component so the user can access the component elements as a group or individually using their modifiers.
import {BEMifyThis} from '../shared/bem'
const BEMify = BEMifyThis(this.props.modifier)
className={"navbar__menu_item")}
becomes
className={BEMify("navbar__menu_item")}
so something like navbar__menu_item becomes navbar__menu_item navbar__menu_item-logout

Categories

Resources