Beginner: Styling ReactJS Checkbox using CSS Pseudo Elements - css

I am trying to style reactjs checkbox component using CSS. It uses pseudo-elements :after and :before.
Mock it up on html and css and it worked! Fiddle Link
input[type="checkbox"]#checkbox + label::before {
content: "";
display: inline-block;
vertical-align: -25%;
height: 2ex;
width: 2ex;
background-color: white;
border: 1px solid #c3c4c6;
border-radius: 4px;
margin-right: 0.5em;
}
input[type="checkbox"]#checkbox:checked + label::after {
content: '';
position: absolute;
width: 1.2ex;
height: 0.4ex;
background: rgba(0, 0, 0, 0);
top: 0.5ex;
left: 0.4ex;
border: 3px solid #60cd18;
border-top: none;
border-right: none;
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
}
But when i tried to implement it on my reactjs component that display checkbox,maybe the :after doesn't work. Fiddle ReactJS Component Link
How should i achieve the same style on ReactJS checkbox component?

There's couple of ways to use pseudo elements on React such as Radium, but afaik it doesn't support :after/:before, instead according to this and this, it suggest creating an element in place of the pseudo-elements. I didn't find any React example of this and i think the availability of pseudo elements was to avoid creating unnecessary DOM elements.
With all that limitations, my current solution for customising the checkbox in react so that it works on most browsers was to create an element that acts,looks, and feel like a checkbox(imposter), but not a checkbox by itself (input type="checkbox"). I achieved this by using ReactJS to trigger the visibility of the span element and font-awesome icon.
Example
/* html */
<div id="container">
</div>
/* JS */
var Checkbox = React.createClass({
getDefaultProps: function() {
return {
value: true,
name: '',
borderWidth: '1px',
borderStyle: 'solid',
borderColor: '#c3c4c6',
borderRadius: '4px',
checkColor: '#60cd18',
height: '1px',
width: '',
namePaddingLeft: '10px',
namePaddingRight: ''
};
},
render: function() {
var style = {
boxStyle: {
borderWidth: this.props.borderWidth,
borderStyle: this.props.borderStyle,
borderColor: this.props.borderColor,
borderRadius: this.props.borderRadius,
paddingLeft: this.props.width,
paddingRight: this.props.width,
paddingTop: this.props.height,
paddingBottom: this.props.height
},
show: {
visibility: 'visible',
color: this.props.checkColor
},
hidden: {
visibility: 'hidden',
color: this.props.checkColor
},
name: {
paddingLeft: this.props.namePaddingLeft,
paddingRight: this.props.namePaddingRight
}
};
return (
<div>
<span style={style.boxStyle}>
<i className="fa fa-check fa-lg" style={(this.props.value) ? style.show : style.hidden}></i>
</span>
<span style={style.name}>{this.props.name}</span>
</div>
);
}
});
var WrapperCheckbox = React.createClass({
getInitialState: function(){
return {value: false}
},
handleClick: function(){
console.log('Click Fires!');
this.setState({value: !this.state.value});
},
render: function(){
return (
<div onClick={this.handleClick}>
<Checkbox name='Reserve Guarantee' value={this.state.value}/>
</div>
);
}
});
React.render(<WrapperCheckbox />, document.getElementById('container'));

Related

CSSTransition to make a slide out drawer in css grid layout

I am trying to make a slide out drawer utilizing the npm package react-transition-group. For whatever reason, I cannot seem to get the drawer to slide out from left to right on clicking the additional criteria button. If you can solve this issue without using the package, that is ok too!
Here is the code I am trying to get to work as a React component:
{/* DeveloperSearch.js */}
<CSSTransition
in={sidebarClicked}
appear
timeout={1000}
classNames="drawer"
mountOnEnter
unmountOnExit
>
<div className="DevSearch__additional-search-criteria">
Additional Search Criteria
<div className="DevSearch__additional-search-criteria-individual">
<div
style={{
fontSize: '0.8rem',
marginBottom: '5px',
fontWeight: 'bold',
}}
>
Only show people who match more than {criteriaMatch}% of all
search criteria
</div>
<input
className="form-control"
type="number"
value={criteriaMatch}
onChange={(e) => setCriteriaMatch(e.target.value)}
min={0}
max={100}
step={5}
/>
</div>
</div>
</CSSTransition>
I also have a css file that is specifically for the CSS Transition component called DeveloperSearch.css:
.drawer-exit {
width: 250px;
}
.drawer-exit.drawer-exit-active {
width: 250px;
transition: width 1000ms ease-in;
}
.drawer-exit-done {
width: 0px;
}
.drawer-enter {
width: 250px;
}
.drawer-enter.drawer-enter-active {
width: 250px;
transition: all 1000ms ease-in;
}
Unfortunately, my results are no where near what I was wanting, as the drawer does not seem to slide out at all...
I also have replicated this issue in a codesandbox that can be found by clicking here. Thanks for your help!
Here is a pure css based solution but this is a bit hacky
Markup
const Drawer = ({ transitionExit, handleExit }) => (
<div
onClick={handleExit}
className={`drawer ${transitionExit ? "exit" : ""}`}
>
<p>Home</p>
<p>About</p>
<p>Contact</p>
<p>Close Drawer</p>
</div>
);
export default function App() {
const [isOpen, setIsOpen] = useState(false);
const [transitionExit, setTransitionExit] = useState(false);
const handleExit = () => {
setTransitionExit(true);
setTimeout(() => {
setIsOpen(false);
setTransitionExit(false);
// timeout should be less than animation time otherwise state might still be true
// after animation ends and drawer appears for few milliseconds
}, 450);
};
return (
<div className="App">
<div className="wrapper">
<div className="sidebar_container">
<button onClick={() => setIsOpen(true)}>open</button>
</div>
{isOpen && (
<div className={`container ${transitionExit ? "exit" : ""}`}>
<Drawer handleExit={handleExit} transitionExit={transitionExit} />
</div>
)}
</div>
</div>
);
}
CSS
.wrapper {
height: 90vh;
max-width: 60vw;
display: grid;
grid-template-columns: 30% 70%;
overflow: hidden;
margin: 40px;
}
.sidebar_container {
width: 100px;
height: 100%;
background-color: rgb(250, 207, 213);
padding: 30px;
position: relative;
z-index: 30;
}
#keyframes containerTransitionEnter {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#keyframes drawerTransitionEnter {
0% {
opacity: 0;
left: -10vw;
}
100% {
opacity: 1;
left: 0vw;
}
}
#keyframes containerTransitionExit {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes drawerTransitionExit {
0% {
opacity: 1;
left: 0vw;
}
100% {
opacity: 0;
left: -10vw;
}
}
.container {
position: relative;
z-index: 10;
height: 90vh;
animation: containerTransitionEnter 0.5s;
}
.drawer {
box-sizing: border-box;
position: relative;
height: 90vh;
width: 25vw;
padding: 20px;
background-color: rgb(4, 118, 156);
border-right: 1px solid rgba(0, 0, 0, 0.3);
animation: drawerTransitionEnter 0.5s;
}
p {
margin-bottom: 10px;
color: white;
}
.container.exit {
animation: containerTransitionExit 0.5s;
}
.drawer.exit {
animation: drawerTransitionExit 0.5s;
}
Here is the link to codesandbox
Since you are using react you can use Material UI for this Here
and you can try this in your case
<Drawer
className={classes.drawer}
variant=''
anchor='left'
open={open}
classes={{
paper: classes.drawerPaper,
}}>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'ltr' ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{arr.map((text, index) => (
<ListItem
button
key={text}
onClick={
text === 'Home'
? goToHome
: text === 'About'
? handleOpenAbout
: text === 'Contact'
? goToContact
: text == 'Team'
? goToMyTea,
: goToDashboard
}>
<ListItemIcon>
{text === 'Home' ? (
<HomeIcon />
) : text === 'About' ? (
<NoteAddIcon />
) : text === 'About' || text === 'Contact' ? (
<ListAltIcon />
) : text === 'Dashboard' ? (
<DashboardIcon />
) : (
<></>
)}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
You should not delete div that has class="DevSearch__additional-search-criteria drawer-enter-done" from the DOM. In this case, Transition will not work. If you want to delete it, you must use css animation.
In this way, after adding div to the DOM, put animation on it to enter as a slider

MaterialUI withStyles, trying drilling down to a disabled switches css override

I'm developing with react and MaterialUI on the front end and I have a bunch of customized inputs. Everything is working pretty well except for this one. No matter what combination of selectors I use I can't seem to point to the right one to change this black color.
Also, it'd be nice to have a clear way to identify just by looking at the element selector, to drill down into the right component. Is there anyway to do this (teach a man to fish kind of thing).Here is the image of the element when I inspect it and the color I'm trying to get at.
here is the style object:
toggleToUse = {
switchBase: {},
thumb: {
color: colorUsedByInputs,
opacity: 0.6,
marginLeft: '10.2px'
},
track: {
background: 'grey',
opacity: '1 !important',
borderRadius: 20,
position: 'relative',
'&:before, &:after': {
display: 'inline-block',
position: 'absolute',
top: '50%',
width: '50%',
transform: 'translateY(-50%)',
color: '#fff',
textAlign: 'center'
}
},
checked: {
'&$switchBase': {
color: '#185a9d',
transform: 'translateX(32px)',
'&:hover': {
backgroundColor: 'rgba(24,90,257,0.08)'
}
},
'& $thumb': {
backgroundColor: '#fff'
},
'& + $track': {
background: 'linear-gradient(to right, rgba(43, 56, 97, 0.7), #2b3861)',
'&:before': {
opacity: 1
},
'&:after': {
opacity: 0
}
}
}
};
Here is the image of the element when I inspect it and the color I'm trying to get at.
<Switch classes={{ track: classes.track }} />
track: {
'.Mui-disabled + &&': {
backgroundColor: 'hotpink',
},
},
This will work for a default MUI Switch. If needed, you can increase the specificity by adding additional & to the selector. If all fails, please provide a codesandbox and precisely state what color you want in which scenario.

CSS Ripple effect with pseudo-element causing reflow

I'm trying to create the material ripple effect with styled-components (which is unable to import the material web-components mixins). I want to stick with using the after element for the foreground effect, to keep the accesibility tree intact.
However, most notably on mobile, the ripple transition is causing reflow in the button's content. It would seem to happen because of the display change (from none to block), but I have tried some alternatives which don't share this artifact, and this side-effect is still present.
Here's my code (I'm using some props to set the ripple, but you can hard-set them if you want to reproduce): [Here was an outdated version of the code]
Thanks for the attention.
Edit: The bug only happens when I add a hover effect to the button, very weird. Below follows the link and a code sample (you will have to set a react repository in order to reproduce it, unfortunately)
https://github.com/Eduardogbg/ripple-hover-reflow-bug
import React, { useRef, useReducer } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components'
const ButtonBase = styled.button`
cursor: pointer;
width: 250px;
height: 6vh;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
outline: none;
position: relative;
overflow: hidden;
border-width: 0;
background-color: cyan;
:hover {
filter: brightness(1.06);
}
::after {
content: '';
pointer-events: none;
width: ${({ ripple }) => ripple.size}px;
height: ${({ ripple }) => ripple.size}px;
display: none;
position: absolute;
left: ${({ ripple }) => ripple.x}px;
top: ${({ ripple }) => ripple.y}px;
border-radius: 50%;
background-color: ${({ ripple }) => ripple.color};
opacity: 0;
animation: ripple ${({ ripple }) => ripple.duration}ms;
}
:focus:not(:active)::after {
display: block;
}
#keyframes ripple {
from {
opacity: 0.75;
transform: scale(0);
}
to {
opacity: 0;
transform: scale(2);
}
}
`
const rippleReducer = ref => (ripple, event) => {
const { x, y, width, height } = ref.current.getBoundingClientRect()
const size = Math.max(width, height)
return {
...ripple,
size,
x: event.pageX - x - size / 2,
y: event.pageY - y - size / 2
}
}
const DEFAULT_RIPPLE = {
size: 0,
x: 0,
y: 0,
color: 'white',
duration: 850
}
const Button = props => {
const ref = useRef(null)
const [ripple, dispatch] = useReducer(
rippleReducer(ref),
{ ...DEFAULT_RIPPLE, ...props.ripple }
)
return (
<ButtonBase
ref={ref}
className={props.className}
ripple={ripple}
onClick={event => {
event.persist()
dispatch(event)
}}
>
{props.children}
</ButtonBase>
)
}
ReactDOM.render(
<div style={{
backgroundColor: 'red',
width: '500px', height: '500px',
display: 'grid',
placeItems: 'center'
}}>
<Button>
<span style={{ fontSize: '30px' }}>
abacabadabaca
</span>
</Button>
</div>,
document.getElementById('root')
);
The problem seems to be related to this chromium bug that was supposedly solved a few years ago: Image moves on hover when changing filter in chrome
Setting transform: translate3d(0,0,0); looks like a fix, though my eye isn't pixel-perfect.

Hide the dropdown list when clicking or scrolling on outside

I want to close my dropdown list after clicking or scrolling outside the pane. Still the dropdown box is open all time when we scrolling outside the dropdown box.. This is my code..
static defaultProps = { // <-- DEFAULT PROPS
wrapperStyle: {
display: 'inline',
},
menuStyle: {
borderRadius: '3px',
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.1)',
padding: '2px 0',
fontSize: '90%',
position: 'fixed',
minWidth: '300px',
overflow: 'auto',
maxHeight: '250px',
display: 'inline',
}
}
..............................................................
<ReactAutocomplete
name="ReferredBy"
items = {patientsMasterData.ReferredBy && patientsMasterData.ReferredBy.map(referredObj =>(
{options:referredObj.RefName,
values:referredObj.RefID}
))
}
shouldItemRender={(item, value) => item.options.toLowerCase().indexOf(value.toLowerCase()) > -1}
getItemValue={(item) => item.options}
renderItem={(item, highlighted) =>
<div
key={item.values}
style={{ backgroundColor: highlighted ? '#3db4e5' : '#FFFFFF',cursor:'pointer', border:'1px solid lighten($grey-element,30%)',padding: '5px}}
{item.options}</div>}
inputProps={{placeholder:'Select...'}}
menuStyle={this.props.menuStyle}
wrapperStyle={this.props.wrapperStyle}
value={this.state.value}
onChange{e=>this.setState({value:e.target.value})}
onSelect={value => this.setState({ value })}
/>
& the css portion,
&_value1 {
flex:2;
white-space: normal;
width: 100%;
// overflow-y: auto;
font-size: 14px;
position: relative;
z-index: 2;
display: inline-block;
input, textarea {
width: 100%;
min-width: 200px;
height: 25px;
border: 1px solid $grey-element;
padding: 0 8px;
font-size: 12px;
}
&::after {
position: absolute;
right: 9px;
top: 10px;
content: '';
width: 0;
height: 0;
border-style: solid;
border-width: 6px 3px 0 3px;
border-color: $black transparent transparent transparent;
} }
How can I hide the dropdown box when scrolling outside?
In few words: you need to add event listener when dropdown is open and make ref on your dropdown to avoid click event on your dropdown, but fire it on clicking somewhere else (and remove eventlistener here). Also you can listen for scrolling events. This is implementation example:
import React, {Component} from 'react';
import { CSSTransition } from 'react-transition-group';
class Dropdown extends Component {
constructor(props) {
super(props);
this.setWrapperRef = this.setWrapperRef.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
};
setWrapperRef(node) {
this.wrapperRef = node;
};
handleClickOutside(e) {
e.stopPropagation();
if (this.wrapperRef && !this.wrapperRef.contains(e.target) && this.props.isOpen){
this.props.onClose();
}
};
componentDidUpdate(){
if(this.props.isOpen){
document.addEventListener('mousedown', this.handleClickOutside);
} else {
document.removeEventListener('mousedown', this.handleClickOutside);
}
}
render(){
return (
<div className={"dropdown " + (this.props.isOpen ? "show" : "hide")} ref={this.setWrapperRef}>
<CSSTransition in={this.props.isOpen} timeout={300} classNames="fadeIndown" unmountOnExit={true}>
{this.props.children}
</CSSTransition>
</div>
)
}
}
export default Dropdown;
const toggleDropdown = () => this.setState({ isDropdownOpen: !this.state.isDropdownOpen });
const closeDropdownThen = fn => (...params) => {
this.setState({ isDropdownOpen: false });
return fn(...params);
};
under the render you should define like that constant like above. And when you use
<Dropdown
isOpen={isDropdownOpen}
toggleDropdown={toggleDropdown}
className={s.dropDownContainer}
label="Export"
>
<DropdownItem onClick={closeDropdownThen(this.abcFunction)}>
CSV
</DropdownItem>
this is my dropDown component maybe it helps you. Best regards

Why is the css style not applying to the modal?

For some reason the css style is not applying to the React Modal. Any reason why?
render() {
return (
<div>
<Modal defaultOpen={this.props.isShowing} className="openmodal">
</Modal>
</div>
);
}
CSS File
.openmodal{
text-align: center;
background-color: black;
border-radius: 0;
width: 400px;
position: relative;
}
If you want to use a javascript style you apply it like this;
const openmodal = {
content: {
textAlign: 'center',
backgroundColor: 'black',
borderRadius: '0',
width: '400px',
position: 'relative'
}
}
render() {
return (
<div>
<Modal defaultOpen={this.props.isShowing} style={openmodal}>
</Modal>
</div>
);
}
Try with the inline style.
It should be portalClassName:
<Modal defaultOpen={this.props.isShowing} portalClassName="openmodal">
</Modal>
It comes with its own DOM structure and its own set of classes.
so you have to target those classes in the screenshot

Resources