Propagate style props to React children and apply them to a CSS file - javascript

I am rendering component Dropdown which acts like html dropdown component, but is a collection of divs ordered and unordered lists. I am trying to pass styles to the className elements , which have dropdown.css file rendering the styles, but I am not sure how to target those specific elements all the way from the parent component.
How to target
div className="select-box-current"
with style={{ border: "1px solid #D4D4D4" }}
and
div className="select-box-list"
with
style={{
opacity: 1,
display: "inherit",
animationName: "none",
cursor: "pointer"
}}
The CodeSandblox is here -> https://codesandbox.io/s/weathered-wood-cf8u8?file=/src/App.js:481-614

So with React if you pass props that are the same name, it only select the one that was passed last. So with your two style props, it only would pass the last one. However, it probably isn't the best idea to use the name style anyway, since it isn't descriptive and also mirrors the actual HTML style attribute.
Give the two different styles unique names, in the App.js file:
<div className="App">
<Dropdown
inputName="viola"
list={[
{ yard_id: "1", yard_name: "Yard 1" },
{ yard_id: "2", yard_name: "Yard 2" }
]}
// this style is for className="select-box-current"
currentStyles={{ border: "1px solid #D4D4D4" }}
// this style is for className="select-box-list"
listStyles={{
opacity: 1,
display: "inherit",
animationName: "none",
cursor: "pointer"
}}
/>
</div>
Now we need to pass those two props through your component tree, the next file is the Dropdown.js file.
Before I get to passing the props, I want to comment about something that is wrong, that isn't entirely related.
export const Dropdown = ({ list, ...others }) => {
const copiedProps = {};
Object.keys(others).forEach((key) => {
// these are the props that need to get thru:
if ("style" === key || "className" === key) {
copiedProps[key] = others[key];
}
});
...
The copying of props, isn't necessary and the way that it is done is actually detrimental. If you want to specifically target a value in the incoming props, you can access it directly (ex props.myProp or in this case other.style) or destructuring assignment.
Since you are wanting to only pass the style (now listStyles and currentStyles) and the className I chose to assign them to a variable using the destructuring assignment.
export const Dropdown = ({
list,
currentStyles,
listStyles,
className,
...others
}) => { ... }
Now that we have those props, we want to pass it into your DropdownView which contains the actual elements you're wanting to target.
<DropdownView
dropdownItems={dropdownItems}
activeItem={activeItem}
hover={hover}
setActiveItem={(activeItem) => {
setActiveItem(activeItem);
}}
onMouseOver={(hover) => onMouseOver(hover)}
toggleList={() => toggleList(!hideList)}
hideList={hideList}
className={className}
currentStyles={currentStyles}
listStyles={listStyles}
/>
Okay, now we have the styles and classname where we want them. All you have to do is get the props like we did above, and then set them on the elements.
<div
className="select-box-current"
tabIndex="0"
autoFocus={true}
onClick={() => toggleList()}
style={currentStyles}
>...</div>
I forked the sandbox to include a working example. But node that I didn't set the use the className prop in the DropdowView since it wasn't clear what element would have that.
https://codesandbox.io/s/hardcore-sun-yyx0g?file=/src/DropdownView.jsx

I think instead of using them from the parent div you can directly use those styles to those elements like this. https://codesandbox.io/s/eloquent-lamport-3spzr?file=/src/App.js
But if you want to use those styles from the parent. Then you can pass them using specific name. Like this
<Dropdown
inputName="viola"
list={[
{ yard_id: "1", yard_name: "Yard 1" },
{ yard_id: "2", yard_name: "Yard 2" }
]}
// this style is for className="select-box-current"
selectBoxCurrentStyle={{ border: "1px solid #D4D4D4" }}
// this style is for className="select-box-list"
selectBoxListStyle={{
opacity: 1,
display: "inherit",
animationName: "none",
cursor: "pointer"
}}
/>
Here is the link https://codesandbox.io/s/damp-rain-cyhxb?file=/src/App.js:133-643

Related

React formatting HTML element properties from multiple sources

I am iterating over state that holds the data for cards like
cards: [
{ id: 1, name: "p1", value: "Prize 1", imageSrc: "/path/to/image", bgColor: { background: "#FF6384", border: "4px solid #FF6384" }, isVisible: "" },
in my iteration, I am using a ternary to determine one of the classes like
<div className={`prize-card ${this.state.flipped === card ? "flipped" : ""} `} onClick={() => this.clickHandler(card)}>
I see how I can use the ternary to determine a second class on top of standard "prize-card", but how would I add even a third class based on the bgColor state? In a standard HTML element, I can just:
<div className={card.bgColor}>
But I can't figure out the syntax on how to add the {card.bgColor} to the rest of the className below... the bgColor below is wrapped in asterisks to show what I cannot add without errors.
<div className={ **{card.bgColor}**`prize-card ${this.state.flipped === card ? "flipped" : ""} `} onClick={() => this.clickHandler(card)}>
Any help is appreciated. Thank you in advance.

Color each selected Dropdown item in react semantic-ui

I have the following jsx code (react-semantic-ui) in render method:
{!this.props.loading &&
<ControlRow>
<Grid.Column width={5}>
<Dropdown
multiple
fluid
selection
options={myOptions}
onChange={this.navigateToMyFunc}
/>
...
...
And I am using styled-components to style my elements:
https://github.com/styled-components/styled-components
Unfortunately the only working styling for the Dropdown due to some weird specifics of the environment is indirect from ControlRow:
const ControlsRow = styled(Grid.Row)`
.ui.multiple.dropdown > .label {
color: white !important;
background-color: #2185d0;
}
`
See also the following thread: Dropdown in semantic ui can't be made of multiple selection type when wrapped with styled-components
Now the Dropdown as you can see is of type multiple. Each selected item should be colored according to the specified in the myOptions options. I can pass myOptions to the ControlRow which will make the array to be accessible in it, but I am not sure how to write the styled-components part of it:
.ui.multiple.dropdown > .label {
color: white !important;
background-color: ${props => props.myOptions..??};
}
I need to also know which item it is to select correct myOptions color. Here is how it looks:
Right now it is just always blue, but I need it to be colored according to each option.
Update
Seems like it is an absent feature in semantic-ui-react - coloring by hex - codes (only a few regular color names allowed) - so I posted this feature to their github:
https://github.com/Semantic-Org/Semantic-UI-React/issues/3889
by default semantic-ui supports selected list of colors. If you need custom label color, you can add your custom css classes and apply the class name to the label.
const getOptions = (myOptions : string[]) => {
return myOptions.map((myValue : string) =>({
key: myValue,
text: myValue,
value: myValue,
label: { className: setColorClass(myValue), empty: true, circular: true }
}))
}
function setColorClass(optValue) {
if (optValue === '1') return 'light-green';
else if (optValue === '2') return 'sandy-brown';
else return 'light-coral';
}
in your css class you can have the following classes
.ui.label.light-green {
background-color: lightgreen !important;
}
.ui.label.sandy-brown {
background-color: lightgreen !important;
}
.ui.label.light-coral {
background-color: lightgreen !important;
}
Also if you want to apply label circular color when the value is selected, you can do the following, write a renderLabel function in your react class and apply it in the compnent
function renderLabel(label:any){
return {
content: `${label.text}`,
className: setColorClass(label.text)
}
}
Sample component
<Dropdown
search
selection
closeOnChange
value={myValue}
options={getOptions(myOptions)}
placeholder='Choose from here'
onChange={handleDropdownChange}
renderLabel={renderLabel}
/>
You don't need to use CSS styling for this. And nothing related to Styled Components needs to be done.
SemanticUI lets you use a custom render function for labels.
You would use it like this:
const renderLabel = (option) => ({
color: option.color,
content: option.text,
})
const myOptions = [
{ text: "option one", color: "blue" },
{ text: "option two", color: "red" },
// more options...
]
// ...
<Dropdown
multiple
fluid
selection
options={myOptions}
onChange={this.navigateToMyFunc}
renderLabel={renderLabel} // here
/>
This assumes that your option objects have a color property and a text property. You'll need to adjust to the shape of your option objects.
Also, the color property will need to be one of the available label colors in SemanticUI:
const colors = [
'red',
'orange',
'yellow',
'olive',
'green',
'teal',
'blue',
'violet',
'purple',
'pink',
'brown',
'grey',
'black',
]

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

Css inline style as an attribute in React component

I have custom component in which i map attributes.
In this component I have label which have css class assigned.
But i also want optional inline style for this label.
The problem is that in react I need to surround inline style with curly braces and i can't escape them or set them correctly in component. How to resolve this?
Code from component:
const CustomComponent = ({items, name}) => (
<fieldset>
{items
.map((item, index) => ({item, id: `${name || 'dod'}-${item.value || index}`}))
.map(({item, id}) =>
<div key={id}
className="className1">
<input
id={id}
name={name}
type="text"
/>
<label htmlFor={id} className="className" style={item.style}>
{item.label}
</label>
</div>
)}
</fieldset>
);
Code from rendered .jsx
<CustomComponent
name="name"
items={[{
value: 'value',
label: 'label',
style: {{display: 'inline'}} -> not working
}]}
/>
You only need to include the first {} when you are directly inside a Virutal DOM Object. <... items={object} .../> and then object should be written exactly the same way as other Object Literals. That being said you need {display: 'inline'} instead of {{display: 'inline'}} in
<CustomComponent
name="name"
items={[{
value: 'value',
label: 'label',
style: {{display: 'inline'}} -> not working //use {display: 'inline'}
}]}
/>
I've made a pen for this in Codepen, you can check it Here.
Your style property have to be an object literal, something like so :
item.style = {
display: 'inline'
}
In react you can define your styles in form of JS objects, something like
style: {{display: 'inline'}}
You can abstract this further and have a JS file holding all the styles for your page:
const styles = {
myElement: {
display: 'inline'
}
}
then in the code...
<Component style={styles.myElement} />

How to align text in Draft.js

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;
}
.....

Categories

Resources