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.
Related
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}
});
I have a Radar chart with labels, I want to have a click event on the Label of the Radar chart but the element always returns null. I have looked at other Stack over flow questions notedly
1 and this 2. one talks about doing it in vanilla JS approach and other one just is not working for me , Can some one point me to what am i doing wrong ?
End goal -> I want to get the label which is clicked and add a strike through toggle to that label so that i can toggle the data point on and off in the radar chart.
My Implementation
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData
};
}
render() {
return (
<div className="chart">
<Radar
data={this.state.chartData}
options={{
title: {
display: true,
text: "Testing",
fontSize: 25
},
legend: {
display: true,
position: "right"
},
onClick: function(evt, element) {
// onClickNot working element null
console.log(evt, element);
if (element.length > 0) {
console.log(element, element[0]._datasetInde);
// you can also get dataset of your selected element
console.log(data.datasets[element[0]._datasetIndex]);
}
}
}}
/>
</div>
);
}
}
**Link to the sample implementation **
Note: This answer implementation doesn't implement strikethrough. Strikethrough could be implemented by putting unicode character \u0366 between each character of the label string. Here's an example how do this with Javascript. The reason I'm not showcasing this here, is because it didn't really look that great when I tested it on codesandbox.
In a newer version of chart.js radial scale point label positions were exposed. In the example below I'm using chart.js version 3.2.0 and react-chartjs-2 version 3.0.3.
We can use the bounding box of each label and the clicked position to determine if we've clicked on a label.
I've used a ref on the chart to get access to the label data.
I've chosen to set the data value corresponding to a label to 0. I do this, because if you were to remove an element corresponding to a label, the label would disappear along with it. My choice probably makes more sense if you see it in action in the demo below.
const swapPreviousCurrent = (data) => {
const temp = data.currentValue;
data.currentValue = data.previousValue;
data.previousValue = temp;
};
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData
};
this.radarRef = {};
this.labelsStrikeThrough = props.chartData.datasets.map((dataset) => {
return dataset.data.map((d, dataIndex) => {
return {
data: {
previousValue: 0,
currentValue: d
},
label: {
previousValue: `${props.chartData.labels[dataIndex]} (x)`,
currentValue: props.chartData.labels[dataIndex]
}
};
});
});
}
render() {
return (
<div className="chart">
<Radar
ref={(radarRef) => (this.radarRef = radarRef)}
data={this.state.chartData}
options={{
title: {
display: true,
text: "Testing",
fontSize: 25
},
legend: {
display: true,
position: "right"
}
}}
getElementAtEvent={(element, event) => {
const clickX = event.nativeEvent.offsetX;
const clickY = event.nativeEvent.offsetY;
const scale = this.radarRef.scales.r;
const pointLabelItems = scale._pointLabelItems;
pointLabelItems.forEach((pointLabelItem, index) => {
if (
clickX >= pointLabelItem.left &&
clickX <= pointLabelItem.right &&
clickY >= pointLabelItem.top &&
clickY <= pointLabelItem.bottom
) {
// We've clicked inside the bounding box, swap labels and label data for each dataset
this.radarRef.data.datasets.forEach((dataset, datasetIndex) => {
swapPreviousCurrent(
this.labelsStrikeThrough[datasetIndex][index].data
);
swapPreviousCurrent(
this.labelsStrikeThrough[datasetIndex][index].label
);
this.radarRef.data.datasets[datasetIndex].data[
index
] = this.labelsStrikeThrough[datasetIndex][
index
].data.previousValue;
this.radarRef.data.labels[index] = this.labelsStrikeThrough[
datasetIndex
][index].label.previousValue;
});
// labels and data have been changed, update the graph
this.radarRef.update();
}
});
}}
/>
</div>
);
}
}
So I use the ref on the chart to get acces to the label positions and I use the event of getElementAtEvent to get the clicked x and y positions using event.nativeEvent.offsetX and event.nativeEvent.offsetY.
When we've clicked on the label I've chosen to update the value of the ref and swap the label data value between 0 and its actual value. I swap the label itself between itself and itself concatenated with '(x)'.
sandbox example
The reason I'm not using state here is because I don't want to rerender the chart when the label data updates.
You could run a function that modifies your dataset:
You would create the function where you have your data set
chartClick(index) {
console.log(index);
//this.setState({}) Modify your datasets properties
}
Pass the function as props
<Chart chartClick={this.chartClick} chartData={this.state.chartData} />
Execute the function when clicked
onClick: (e, element) => {
if (element.length) {
this.props.chartClick(element[0]._datasetIndex);
}
}
I am using a nightlight button library: react-native-selectmultiple-button
In this library there is a prop selected
Description: Type:Boolean. Default is false. The selected prop determines whether the button is selected and highlighted
Is there a way I can change the state of "selected" prop, depending on number of buttons selected?
For example, if I select more than 5 buttons, I want other buttons to be unselectable.
constructor(props) {
super(props)
this.state = {
numberOfbuttonsSelected:0
}
}
{
if(this.state.numberOfbuttonsSelected <5){
<SelectMulipleButton
selected={true}/>}
else{<SelectMulipleButton
selected={false}/>
}
}
The code above won't work any comments or advise would be really appreciated :)
This is the new code:
<View style={{ flexWrap: 'wrap', flexDirection: 'row',backgroundColor:'gray',paddingTop:10,paddingLeft:6,paddingRight:0,borderColor:'white', borderWidth:1}}>
{
multipleData.map(
(interest) =>
<SelectMultipleButton
key={interest}
buttonViewStyle={{
borderRadius: 0,
height: 40,
width: 110,
}}
textStyle={{
fontSize: 15,
}}
highLightStyle={{
borderColor: 'white',
backgroundColor: 'transparent',
textColor: 'white',
borderTintColor: 'white',
backgroundTintColor: '#6AAAC6',
textTintColor: 'white',
}}
multiple={true}
value={interest}
selected={this.state.multipleSelectedData.includes(interest)}
singleTap={valueTap => this.trackSelection(valueTap)} />
)
}
</View>
</ScrollView>
Sorry for the delay in replying. Please see my example component below. I have included explanations in comments inline in the code. Please reach out if you need further help.
export class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
numberOfbuttonsSelected: 0,
multipleSelectedData: []
};
}
//This method is what you mainly need
trackSelection = value => {
if (!this.state.multipleSelectedData.includes(value)) { //This checks if the value already exists in the checked buttons list
if (this.state.numberOfbuttonsSelected < 5) { //Here we check if the number of selected buttons has exceeded the specified number
this.state.multipleSelectedData.push(value);
this.setState({
numberOfbuttonsSelected: this.state.numberOfbuttonsSelected + 1
});
} //else do nothing. Effectively, we are disabling the click on the button.
} else { //we are simply toggling the selection here
this.state.multipleSelectedData.splice(
this.state.multipleSelectedData.indexOf(value), 1
);
this.setState({
numberOfbuttonsSelected: this.state.numberOfbuttonsSelected - 1
});
}
};
render() {
return (
//Customize your render function. I just included one button as an example.
<View>
<SelectMultipleButton
multiple={true}
value={interest} //"interest" is just an example value. Change it according to your requirements for each button.
selected={this.state.multipleSelectedData.includes(interest)}
singleTap={valueTap => this.trackSelection(valueTap)} //valueTap is supposed to be the "value" prop's value for each
//button according to the lib's documentation, but if you're not comfortable using valueTap, you can
//simply pass "interest" (or your own custom value for the particular button) into the trackSelection() method
/>
</View>
);
}
}
EDIT
I went through the code in the lib and the onPress function in the SelectMultipleButton component is why your multiple selection still works:
<TouchableWithoutFeedback
onPress={() => {
if (this.props.multiple) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
} else {
if (!this.state.selected) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
}
}
}
}>
I know it's not a good idea to modify library files, but in this case, instead of using the whole lib, you can copy over this file to your project (don't remove the author credit at the top of this file) and add a prop selectable to it and modify the onPress thus:
<TouchableWithoutFeedback
onPress={() => {
if (this.props.multiple) {
if(this.props.selectable) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
}
} else {
if (!this.state.selected) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
}
}
}
}>
Pass the prop thus:
<SelectMultipleButton
multiple={true}
value={interest}
selectable={this.state.multipleSelectedData.includes(interest) || this.state.numberOfbuttonsSelected < 5}
selected={this.state.multipleSelectedData.includes(interest)}
singleTap={valueTap => this.trackSelection(valueTap)}
/>
This should solve your problem.
I am working on a GatsbyJS project, using Contentful, and have created a template 'BlogPost'. Inside this template, I have imported a 'ResearchSlider' component. However, the images are not displaying at all.
I have passed down the image url data, as props, down to the researchSlider, which is then put inside the component state, and used in a function that passes each piece of image data into the 'Slide' child component, using a for loop. In 'Slide', the piece of image data is used as the value for backgroundImage in the styles of a div.
I would like to understand why they are not displaying and how I can fix this.
Here is the relevant code.
ResearchSlider component:
export default class ResearchSlider extends React.Component {
constructor(props) {
super(props);
this.state = {
images: [this.props.firstSlide,
this.props.secondSlide,
this.props.thirdSlide
],
translateValue: 0,
index: 0
}
this.renderSlides = this.renderSlides.bind(this);
this.handleClickPrevious = this.handleClickPrevious.bind(this);
this.handleClickNext = this.handleClickNext.bind(this);
this.slideWidth = this.slideWidth.bind(this);
}
renderSlides() {
const images = this.state.images;
let slides = []
for(let i = 0; i < images.length; i++)
slides.push(<Slide key={i} image={images[i]} />)
return slides
}
render() {
const { translateValue } = this.state
return(
<div className='slider'>
<div className="slider-wrapper"
style={{
transform: `translateX(${translateValue}px)`,
transition: 'transform ease-out 0.3s'
}}>
{ this.renderSlides() }
</div>
<ClickPrevious slideRight={this.handleClickPrevious}/>
<ClickNext slideLeft={this.handleClickNext}/>
</div>
);
}
'Slide' component:
import React from 'react';
const Slide = ({image}) => {
const styles = {
backgroundImage: `url("${image}")`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: '50% 60%'
}
return <div className="slide" style={styles}></div>
}
export default Slide
Here is a screenshot of the inspection:
contentful assets URLs don't have the protocol attached to them they will in the following format //images.contentful.com/...... when setting this url to the src of an <img> tag the browser will assign the protocol automatically based on the protocol you app is using. But using the url in css you need to append explicitly the protocol to the url.
You Slide component should look like this.
const Slide = ({image}) => {
const styles = {
backgroundImage: `url("https:${image}")`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: '50% 60%'
}
return <div className="slide" style={styles}></div>
}
export default Slide
I'm wondering how to align text in Draft.js just like on the picture below.
I have searched this several days, but I haven't found the solution.
After reading the source code, I found a way for it. Using blockRenderMap, you can add some custom block types like this:
const blockRenderMap: Record<string, DraftBlockRenderConfig> = {
'header-one-right': {
element: 'h1',
wrapper: <StyleHOC style={{ ...blockStylesMap['header-one'], display: 'flex', justifyContent: 'flex-end' }} />,
},
'header-two-right': {
element: 'h2',
wrapper: <StyleHOC style={{ ...blockStylesMap['header-two'], display: 'flex', justifyContent: 'flex-end' }} />,
},
'header-three-right': {
element: 'h3',
wrapper: <StyleHOC style={{ ...blockStylesMap['header-three'], display: 'flex', justifyContent: 'flex-end' }} />,
},
'unstyled-right': {
element: 'div',
wrapper: <StyleHOC style={{ ...blockStylesMap['unstyled'], display: 'flex', justifyContent: 'flex-end' }} />,
},
};
I use flex to avoid wasting time to find a away to override the internal style .public-DraftStyleDefault-ltr.
StyleHOC is quite simple:
const StyleHOC: React.FC<Props> = ({ style, children }) => {
const childrenWithStyle = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { style });
}
return child;
});
return <>{childrenWithStyle}</>;
};
And then you can toggle the blockType using RichUtils.toggleBlockType(editorState, blockType).
The Editor component has a div with the class .public-DraftStyleDefault-ltr. This controls the text alignment of each paragraph that you write. As you create more paragraphs, more of these divs are created to contain the text. The text is wrapped in a span element and .public-DraftStyleDefault-ltr has a default alignment of text-align: left.
I created some css classes for text-align: left, text-align: center, text-align: right and text-align: justify and added this basic for loop to my component for creating text alignment buttons.
const textBlock = document.querySelectorAll(".public-DraftStyleDefault-ltr");
for (let i = 0; i < textBlock.length; i++) {
textBlock[i].classList.toggle(this.props.style);
}
this.props.style is the name of the css class that determines the text-alignment I wanted.
It is a pretty basic fix since this way when you click align right say, the whole document is aligned right. I am planning to work on this so only the selected text is aligned so should be able to update this answer soon. Hope it helps in some way
I tried to make almost the same thing. But my trouble was in text-align property, which was correctly set to block span, but .public-DraftStyleDefault-ltr doesn't react to it.
So, I have made next decision, which get the first div child, and copy it's params:
const paragraphs: any = document.querySelectorAll(".public-DraftStyleDefault-ltr");
for (let i = 0; i < paragraphs.length; i++) {
const paragraph = paragraphs.item(i);
if (paragraph) {
const firstItem = paragraph.querySelectorAll('*').item(0);
// Apply to the parent the first child style
paragraph.style.textAlign = firstItem.style.textAlign;
}
}
To change block alignment you can:
1- set alignment data
const modifiedBlockState = Modifier.setBlockData(editorState.getCurrentContent(),
editorState.getSelection(),Map({align:'align-center'}));
setEditorState(EditorState.push(modifiedBlockState,'change-block-data'));
2- use it in styling function
/*
JSX
blockStyleFn={ block => block.getData().get('align')
this will return 'align-center|left|right' which will be assigned as a classname
*/
<Editor blockStyleFn={ block => block.getData().get('align')} .../>
//CSS
.align-center div{
text-align: center;
}
.align-right div{
text-align: right;
}
.....