How can i extract and combine inline css style properties - javascript

I read a couple of answers and all of them seems to assume you have a single object containing CSS instead of single properties, I tried the answers and I couldn't make it work, I was wondering what's wrong and what's the best way to do this, here is what I have done so far :
import React from 'react';
import FormLabel from '#material-ui/core/FormLabel';
const label = (props) => {
// <label onClick={props.onClick} className={props.className + ' ' + props.gridClass} style={props.inlineStyle} >{props.label}</label>
let divStyleArray = [];
if (typeof props.inlineStyle.background !== 'undefined') {
divStyleArray.push(props.inlineStyle.background)
delete props.inlineStyle.background
}
if (typeof props.inlineStyle.textAlign !== 'undefined') {
divStyleArray.push(props.inlineStyle.textAlign)
delete props.inlineStyle.textAlign
}
const customStyle = {
width: '100%'
}
const divStyle = Object.assign({}, ...divStyleArray);
return (
<div className={props.gridClass} style={{divStyle}}>
<FormLabel component="label" onClick={props.onClick} style={{ ...customStyle, ...props.inlineStyle }}>{props.label}</FormLabel>
</div>
)
}
export default label;
My goal is to extract a couple of CSS property, give it to the div and then give the rest to whats inside the div
Update 01:
I tried the answered given but it doesn't seem to work properly, here is what i did:
import React from 'react';
import FormLabel from '#material-ui/core/FormLabel';
const label = (props) => {
let inlineStyle = {
...props.inlineStyle
}
const divStyle = {
background: inlineStyle.background,
textAlign: inlineStyle.textAlign,
}
delete inlineStyle.background;
delete inlineStyle.textAlign;
const customStyle = {
width: '100%'
}
return (
<div className={props.gridClass} style={divStyle}>
<FormLabel component="label" onClick={props.onClick} style={{ ...customStyle, ...inlineStyle }}>{props.label}</FormLabel>
</div>
)
}
export default label;

First of all deleting stuff from the props object would be anti-pattern, or at least bad practice as far as I know.
If you only need the two properties you use there you could use this code:
const label = (props) => {
let divStyle = {
background: props.background,
textAlign: props.textAlign,
};
const customStyle = {
width: '100%'
}
return (
<div className={props.gridClass} style={{divStyle}}>
<FormLabel
component="label"
onClick={props.onClick}
style={{
...customStyle,
...props.inlineStyle,
background: undefined,
textAlign: undefined,
}}
>{props.label}</FormLabel>
</div>
)
}

Related

Unable to find draggable with id: b7zc6

Hi i am new to react and stuck with react-beautiful-dnd issue.
I Seem to have configured as per the documentation but still facing this weird issue related to draggable id.
made draggableid as string
provided index as number
assigned innerref properly
But still the issue persist.
Need some expert help in this area.
CodeSandBox Link : Link to react beautiful dnd codesandbox issue code
Also adding the code for quick glance:
import "./styles.css";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import { useState } from "react";
export default function App() {
const [list, setList] = useState(generateData());
const st = {
width: "300px",
cursor: "grab",
display: "flex",
gap: "10px",
flexDirection: "column"
};
const onDragEnd = (result) => {
if (result.destination) {
alert("drag successfull");
}
};
return (
<div className="App">
<h4>Test</h4>
<div>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={st}
className="work-parent"
>
<WorkList list={list} />
</div>
)}
</Droppable>
</DragDropContext>
</div>
</div>
);
}
function WorkList({ list }) {
return list.map((l, index) => <Work key={l.id} work={l} index={index} />);
}
function Work({ work, index }) {
const st = {
padding: "10px",
width: "100%",
color: "white",
backgroundColor: "purple",
width: "200px",
height: "50px"
};
return (
<Draggable draggableId={work.id} key={work.id} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={st}
>
{work.title}
</div>
)}
</Draggable>
);
}
function generateData() {
const data = [];
for (let i = 0; i < 10; i++) {
data.push({
title: "Work - " + i,
id: makeid(5)
});
}
return data;
}
function makeid(length) {
var result = "";
var characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
For the first look your code is correct. The problem that you faced is about upgrading a React v18. As a temporary solution I can advise you to remove <StrictMode> wrapper and your app will work.
You can find more information about this issue here: https://github.com/atlassian/react-beautiful-dnd/issues/2350
UPDATE:
Downgrade to React v17 also might help:
https://github.com/atlassian/react-beautiful-dnd/issues/2407
UPDATE 2:
I have not tested it by myself but seems that this could be a workaround to use React v18 and do not remove <StrictMode>:https://github.com/atlassian/react-beautiful-dnd/issues/2399#issuecomment-1167427762

Clicking a left arrow or a right arrow to switch an element in an array with the element on the left or right React

I am new to React and am trying to build an app in which a user can create a card, delete a card, and change the order of the cards array by clicking left or right arrow to switch elements with the element on the left or on the right.
I am struggling to code this functionaliy. I have the function written to switch the card with that on the left, but this function is not doing anything right now. I also do not get any errors in the console from this function, so I really cannot determine where I am going wrong here.
Here is the code so far:
CardList.js will display the form to add a card and display the array of CardItems, passing the functions to switch these items to the left or right ('moveLeft', 'moveRight') as props.
import React from "react";
import CardItem from "./CardItem";
import CardForm from "./CardForm";
import './Card.css';
class CardList extends React.Component {
state = {
cards: JSON.parse(localStorage.getItem(`cards`)) || []
// when the component mounts, read from localStorage and set/initialize the state
};
componentDidUpdate(prevProps, prevState) { // persist state changes to longer term storage when it's updated
localStorage.setItem(
`cards`,
JSON.stringify(this.state.cards)
);
}
render() {
const cards = this.getCards();
const cardNodes = (
<div style={{ display: 'flex' }}>{cards}</div>
);
return (
<div>
<CardForm addCard={this.addCard.bind(this)} />
<div className="container">
<div className="card-collection">
{cardNodes}
</div>
</div>
</div>
);
}
addCard(name) {
const card = {
name
};
this.setState({
cards: this.state.cards.concat([card])
}); // new array references help React stay fast, so concat works better than push here.
}
removeCard(index) {
this.state.cards.splice(index, 1)
this.setState({
cards: this.state.cards.filter(i => i !== index)
})
}
moveLeft(index,card) {
if (index > 1) {
this.state.cards.splice(index, 1);
this.state.cards.splice((index !== 0) ? index - 1 : this.state.cards.length, 0, card)
}
return this.state.cards
}
moveRight(index, card) {
// ?
}
getCards() {
return this.state.cards.map((card) => {
return (
<CardItem
card={card}
index={card.index}
name={card.name}
removeCard={this.removeCard.bind(this)}
moveLeft={this.moveLeft.bind(this)}
moveRight={this.moveRight.bind(this)}
/>
);
});
}
}
export default CardList;
CardItem is taking in those props and ideally handling moving the card left or right in the array once the left or right icon is clicked.
import React from 'react';
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
class CardItem extends React.Component {
render() {
return (
<div>
<Card style={{ width: '15rem'}}>
<Card.Header as="h5">{this.props.name}</Card.Header>
<Card.Body>
<Button variant="primary" onClick={this.handleClick.bind(this)}>Remove</Button>
</Card.Body>
<Card.Footer style={{ display: 'flex' }}>
<i class="arrow left icon" onClick={this.leftClick.bind(this)} style={{ color: 'blue'}}></i>
<i class="arrow right icon" onClick={this.rightClick.bind(this)} style={{ color: 'blue'}}></i>
</Card.Footer>
</Card>
</div>
)
}
handleClick(index) {
this.props.removeCard(index)
}
leftClick(index, card) {
this.props.moveLeft(index, card)
}
rightClick(index, card) {
this.props.moveRight(index, card)
}
}
export default CardItem;
Not sure where I am going wrong here. Any help would be appreciated
Edit #1
Hey guys, so I wrote out a different function to handle moving the card to the left, and I decided to bind "this" to that method in the constructor because I was getting errors saying the program could not read it. However, I am still getting errors basically saying that everything is not defined when I pass the function from CardList to CardItem as props. Does anybody know what the problem is? I suspect its my syntax when I call the methods in CardItem.
CardList.js
import React from "react";
import CardItem from "./CardItem";
import CardForm from "./CardForm";
import './Card.css';
class CardList extends React.Component {
constructor(props) {
super();
this.moveLeft = this.moveLeft.bind(this);
this.moveRight = this.moveRight.bind(this);
this.state = {
cards: JSON.parse(localStorage.getItem(`cards`)) || []
// when the component mounts, read from localStorage and set/initialize the state
};
}
componentDidUpdate(prevProps, prevState) { // persist state changes to longer term storage when it's updated
localStorage.setItem(
`cards`,
JSON.stringify(this.state.cards)
);
}
render() {
const cards = this.getCards();
const cardNodes = (
<div style={{ display: 'flex' }}>{cards}</div>
);
return (
<div>
<CardForm addCard={this.addCard.bind(this)} />
<div className="container">
<div className="card-collection">
{cardNodes}
</div>
</div>
</div>
);
}
addCard(name) {
const card = {
name
};
this.setState({
cards: this.state.cards.concat([card])
}); // new array references help React stay fast, so concat works better than push here.
}
removeCard(index) {
this.state.cards.splice(index, 1)
this.setState({
cards: this.state.cards.filter(i => i !== index)
})
}
moveLeft(index, card) {
this.setState((prevState, prevProps) => {
return {cards: prevState.cards.map(( c, i)=> {
// also handle case when index == 0
if (i === index) {
return prevState.cards[index - 1];
} else if (i === index - 1) {
return prevState.cards[index];
}
})};
});
}
moveRight(index, card) {
// ?
}
getCards() {
return this.state.cards.map((card) => {
return (
<CardItem
card={card}
index={card.index}
name={card.name}
removeCard={this.removeCard.bind(this)}
moveLeft={this.moveLeft}
moveRight={this.moveRight}
/>
);
});
}
}
export default CardList;
CardItem.js
import React from 'react';
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
class CardItem extends React.Component {
render() {
return (
<div>
<Card style={{ width: '15rem'}}>
<Card.Header as="h5">{this.props.name}</Card.Header>
<Card.Body>
<Button variant="primary" onClick={this.handleClick.bind(this)}>Remove</Button>
</Card.Body>
<Card.Footer style={{ display: 'flex' }}>
<i class="arrow left icon" onClick={leftClick(index, card)} style={{ color: 'blue'}}></i>
<i class="arrow right icon" onClick={rightClick(index, card)} style={{ color: 'blue'}}></i>
</Card.Footer>
</Card>
</div>
)
}
handleClick(index) {
this.props.removeCard(index)
}
leftClick(index, card) {
this.props.moveLeft(index,card)
}
rightClick(index, card) {
this.props.moveRight(index, card)
}
}
export default CardItem;
To update state arrays in React, you shouldn't use splice, push or the [] operator.
Instead use the methods that return a new array object viz. map, filter, concat,slice.
For a detailed explanation, see this article.
So you can do something like :
moveLeft(index,card) {
this.setState((prevState, prevProps)=> {
return {cards: prevState.cards.map((c,i)=> {
// also handle case when index == 0
if(i == index) {
return prevState.cards[index-1];
} else if(i == index-1) {
return prevState.cards[index];
}
})};
});
}
When updating React state using the previous value, always use
setState((prevState,prevProps)=>{ return ...})
as such state updates may be asynchronous. See React docs.
Since you are calling the parent component method from child, it's better to bind these methods in the CardList constructor. Eg:
this.moveLeft = this.moveLeft.bind(this);
this.moveRight ....

How can I include my existing table into my export function?

I am relatively new to React-JS and was wondering how I could pass my variables to my export function. I am using the jsPDF library.
At the time the Summary page is showing up, every thing is already in the database.
The Summary page creates in every round an IdeaTable component, writes it into an array and renders it bit by bit if the users click on the Next button (showNextTable()).
This component can use a JoinCode & playerID to assemble the table that was initiated by this player.
import React, { Component } from "react";
import { connect } from "react-redux";
import { Box, Button } from "grommet";
import IdeaTable from "../playerView/subPages/ideaComponents/IdeaTable";
import QuestionBox from "./QuestionBox";
import { FormUpload } from 'grommet-icons';
import jsPDF from 'jspdf';
export class Summary extends Component {
state = {
shownTable: 0
};
showSummary = () => {};
showNextTable = () => {
const { players } = this.props;
const { shownTable } = this.state;
this.setState({
shownTable: (shownTable + 1) % players.length
});
};
exportPDF = () => {
var doc = new jsPDF('p', 'pt');
doc.text(20,20, " Test string ");
doc.setFont('courier');
doc.setFontType('bold');
doc.save("generated.pdf");
};
render() {
const { topic, players } = this.props;
const { shownTable } = this.state;
const tables = [];
for (let i = 0; i < players.length; i++) {
const player = players[i];
const table = (
<Box pad={{ vertical: "large", horizontal: "medium" }}>
<IdeaTable authorID={player.id} />
</Box>
);
tables.push(table);
}
return (
<Box
style={{ wordWrap: "break-word" }}
direction="column"
gap="medium"
pad="small"
overflow={{ horizontal: "auto" }}
>
<QuestionBox question={topic} />
{tables[shownTable]}
<Button
primary
hoverIndicator="true"
style={{ width: "100%" }}
onClick={this.showNextTable}
label="Next"
/>
< br />
<Button
icon={ <FormUpload color="white"/> }
primary={true}
hoverIndicator="true"
style={{
width: "30%",
background: "red",
alignSelf: "center"
}}
onClick={this.exportPDF}
label="Export PDF"
/>
</Box>
);
}
}
const mapStateToProps = state => ({
topic: state.topicReducer.topic,
players: state.topicReducer.players
});
const mapDispatchToProps = null;
export default connect(mapStateToProps, mapDispatchToProps)(Summary);
So basically how could I include the IdeaTable to work with my pdf export?
If you want to use html module of jsPDF you'll need a reference to generated DOM node.
See Refs and the DOM on how to get those.
Alternatively, if you want to construct PDF yourself, you would use data (e.g. from state or props), not the component references.
Related side note:
On each render of the parent component you are creating new instances for all possible IdeaTable in a for loop, and all are the same, and most not used. Idiomatically, this would be better:
state = {
shownPlayer: 0
};
Instead of {tables[shownTable]} you would have:
<Box pad={{ vertical: "large", horizontal: "medium" }}>
<IdeaTable authorID={shownPlayer} ref={ideaTableRef}/>
</Box>
And you get rid of the for loop.
This way, in case you use html dom, you only have one reference to DOM to store.
In case you decide to use data to generate pdf on your own, you just use this.props.players[this.state.shownPlayer]
In case you want to generate pdf for all IdeaTables, even the ones not shown, than you can't use API that needs DOM. You can still use your players props to generate your own PDF, or you can consider something like React-Pdf

Having trouble with typeahead/predictive test functionality

I'm using material-table and I decided to implement a "typeahead" kind of feature that Google has. Something like this:
To realize this, I wrapped the MTableEditField component with my own, which looks like this:
import React, { useState } from "react";
import { MTableEditField } from "material-table";
const CustomTableEditField = props => {
const [rawValue, setRawValue] = useState(props.value);
const [suggestedValue, setSuggestedValue] = useState("asdasda");
const handleOnChange = value => {
// ... logic to find best match and set state values
};
return (
<MTableEditField
inputProps={
suggestedValue
? {
style: {
backgroundColor: "#557D92",
color: "white",
padding: "offset"
}
}
: {}
}
{...props}
value={suggestedValue ?? rawValue}
onChange={handleOnChange}
/>
);
};
export default CustomTableEditField;
The problem is that when there is a value, it looks like:
I don't want it to change the whole background if there will be a match. I want it to keep the already-typed text, with suggested text appended to it.
I looked into gradients, which are treated as images that can be resized, but I wasn't able to get anything to render.
Is this possible to do at all, or am I just wasting my time?
Edit
Stackblitz
Not sure about the customization of MTableEditField, But you can try some thing like following by writing own component.
1) The main idea is split the words (raw and suggestion) and keep them separate span elements so that we can get full control of the styling.
2) Wrap the span elements in div and write the own onChange event handlers.
PS: this is sample code, Will need to fine tune the code.
Check out the working sample on stackblitz
import React, { useState } from "react";
const dict = ['apple', 'mango', 'berry'];
const CustomTableEditField = props => {
const [rawValue, setRawValue] = useState("");
const [suggestedValue, setSuggestedValue] = useState("");
const handleKeyPress = (event) => {
// console.log('key: ', event.key);
let new_raw;
if (event.key === "Backspace") {
new_raw = rawValue.slice(0, rawValue.length - 1);
} else {
new_raw = `${rawValue}${event.key}`;
}
setRawValue(new_raw);
const suggested = dict.find(word => word.startsWith(new_raw));
if (suggested && new_raw) {
setSuggestedValue(suggested.slice(new_raw.length));
} else {
setSuggestedValue("");
}
}
return (
<div
tabIndex="0"
onKeyDown={handleKeyPress}
style={{border: '1px solid green', height: '30px', color: 'black'}}>
<span>{rawValue}</span>
{suggestedValue && (
<span
style={{backgroundColor: "#557D92",
color: "white",
padding: "offset"}}> {suggestedValue} </span>
)}
</div>
);
};
export default CustomTableEditField;

Need help to make a event handler to work properly in react js

I need to make this logic to be executed every time when input field value is changed:
let warningMessage = ''
if (stateData) {
let list = []
if (props.values.injured_worker_type === 'Seasonal') {
list = stateData.calculation_warnings['Seasonal']
} else {
list = stateData.calculation_warnings[`FullTime-${props.values.working_days}`]
}
const keys = _.keys(list)
const index = _.findIndex(keys, key => {
const fromValue = Number(key.slice(1, key.indexOf('-')))
const toValue = Number(key.slice(key.indexOf('-') + 1, key.length))
return props.values.total_days_paid >= fromValue && props.values.total_days_paid < toValue
})
if (index >= 0) {
warningMessage = list[keys[index]]
}
}
const message = warningMessage ? (
<p style={{ color: 'red', fontSize: '0.85rem' }}> {warningMessage} </p>
) : null
And pass downthis messeage to be outputed as result of this logic:
const message = warningMessage ? (
<p style={{ color: 'red', fontSize: '0.85rem' }}> {warningMessage} </p>
) : null
Here is JSX(html) part when I have that input field:
return (
<Fragment>
<Form.Group grouped style={{ marginTop: '2rem' }}>
<label style={{ fontSize: '0.85rem' }}>8. Total days in preceeding 52 weeks:</label>
<Form.Input
name="total_days_paid"
onChange={this.handleChange}
width="6"
placeholder="200"
required
/>
</Form.Group>
{/* THIS messeage is result of that logic which I need to
be executed every time on changed input */}
{message}
</Fragment>
)
}
Here is not working version of code in stackblitz:
https://stackblitz.com/edit/react-tpk1ik
How I can make this to works?
You code is not up to the mark. You are doing many mistakes in your code.
I have corrected all your code and here is the working solution of handler function https://react-t3luq3.stackblitz.io.
index.js
import React from 'react'
import ReactDom from "react-dom";
import Hello from './Hello';
ReactDom.render(<Hello />, document.getElementById("root"));
Hello.js
import React, { Component, Fragment } from 'react'
import { Form } from 'semantic-ui-react'
import _ from 'lodash'
export default class Hello extends Component{
constructor(props){
super(props);
}
handleChange = e => {
console.log(e.target.value)
}
render(){
const { stateData } = this.props;
let warningMessage = ''
if (stateData) {
let list = []
if (props.values.injured_worker_type === 'Seasonal') {
list = stateData.calculation_warnings['Seasonal']
} else {
list = stateData.calculation_warnings[`FullTime-${props.values.working_days}`]
}
const keys = _.keys(list)
const index = _.findIndex(keys, key => {
const fromValue = Number(key.slice(1, key.indexOf('-')))
const toValue = Number(key.slice(key.indexOf('-') + 1, key.length))
return props.values.total_days_paid >= fromValue && props.values.total_days_paid < toValue
})
if (index >= 0) {
warningMessage = list[keys[index]]
}
}
const message = warningMessage ? (
<p style={{ color: 'red', fontSize: '0.85rem' }}> {warningMessage} </p>
) : null
return (
<Fragment>
<Form.Group grouped style={{ marginTop: '2rem' }}>
<label style={{ fontSize: '0.85rem' }}>8. Total days in preceeding 52 weeks:</label>
<Form.Input
name="total_days_paid"
onChange={this.handleChange}
width="6"
placeholder="200"
required
/>
</Form.Group>
{message}
</Fragment>
)
}
}
index.html
<html>
<body>
<div id="root"></div>
</body>
</html>
The code in your example doesn't help much because it has errors and even when I install the missing dependencies it outputs nothing, but just from reading it I can see an issue.
The logic you want to execute when the input value changes needs to go inside the handleChange method you are trying to call here onChange={this.handleChange}.
For reference, these type of input fields are called Controled Components and you can read more about them in the React Documentation.

Categories