Drag to Change not possible when material-ui slider is styled - reactjs

Once I start styling the Slider-Component from material-ui the Drag-to-Change-Value does only work on Click, but NOT on Drag.
So I can change the value by clicking somewhere on the Slider-Track, but I cannot drag the slider to change value.
I already tried styling it with 'styled-components', yet the sane problem occurs.
...
thumb: {
height: 35,
width: 35,
border: '1px solid var(--grey)',
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)',
backgroundColor: 'var(--light-font)',
},
track: {
backgroundColor: 'var(--primary-color)',
height: 3,
borderBottom: '1px solid grey',
},
trackAfter: {
backgroundColor: 'var(--bg-grey)',
},
})(Slider)
return (
<>
<h6>Weekly Goal: {weeklyGoal[page]}hr</h6>
<StyledSlider
style={{ touchAction: 'none' }}
type="range"
min={0}
max={20}
step={1}
value={weeklyGoal[page]}
onChange={handleGoalChange}
/>
</>
)
}

I am surprised nobody has replied to this question yet since it is probably a common error. Here's what I think is going on:
It seems you (we) were trying to wrap the withStyles HOC within another HOC, for example:
const styled = (component) => props => {
const Foo = withStyles({})(component);
return <Foo {...props} />
}
The mistake is in treating styled as a HOC when it should only return the result of withStyles(), i.e. Foo in the above example. Otherwise it will be instantiating a new Foo everytime the props change, hence not reflecting the expected state changes. This is what the code should be instead:
const styled = (component) => {
const Foo = withStyles({})(component);
return Foo;
}
In your case you should have returned StyledSlider only (not the JSX), then using it and passing props to it where required.

Related

Passing sx prop to a custom component

I have a component that I use as a layout component, and in nearly all cases it works just fine, however there are one or two places where I need to be able to adjust the styles, and I would like to be able to do this by simply passing the exact styles that I need instead of adding some custom prop that will toggle them on or off.
The component looks like this:
const BlockLayout: React.FC<IProps> = ({
children,
id,
forceToBottom,
sxProps, // <--- What I want to add
}) => {
return (
<Box
id={id}
sx={[
{
backgroundColor: (theme) => theme.palette.background.paper,
mt: forceToBottom ? 'auto' : 1,
borderRadius: 0.5,
display: 'flex',
border: '1px solid rgba(0, 0, 0, 0.1)',
flexWrap: 'wrap-reverse',
},
sxProps, //<--- How I could use it?
(theme) => ({
...(theme.palette.mode === 'dark' && {
border: `1px solid ${lighten(
theme.palette.background.paper,
0.15
)}`,
}),
}),
]}
>
{children}
</Box>
)
}
How can this be done, and what type do the props need to have?
In the documentation they just cast the type to const.
You can add them as follows using the spread operator:
},
sxProps && ...sxProps,
(theme) => ({

How can re-render a custom hook when its input value change from undefined to a valid value?

I created a custom hook that get a ref to check the scroll state that is in top of its parent or in bottom of it or even it is scrollable or not. So I pass a ref object to my hook but at first that the ref is null cause that anything does not work in my hook but I wanna when my ref get the right value re-call my hook with this right value but it doesn't work and my custom hook call just one time and its ref value is null
below is my hook :
import { RefObject, useEffect, useState } from "react"
export const useScrollState = <TElement extends HTMLElement>(ref: RefObject<TElement | null>) => {
const [isTop, setIsTop] = useState(true)
const [isBottom, setIsBottom] = useState(false)
const targetRef = ref?.current
const isScrollable: boolean =
(targetRef && targetRef.offsetHeight < targetRef.scrollHeight) ?? false
const scrollHandler = () => {
if (targetRef && Number(targetRef.scrollTop.toString()) === 0) {
setIsTop(true)
} else setIsTop(false)
if (targetRef && targetRef.offsetHeight + targetRef.scrollTop >= targetRef.scrollHeight) {
setIsBottom(true)
} else {
setIsBottom(false)
}
}
useEffect(() => {
targetRef?.addEventListener("scroll", scrollHandler)
return () => {
targetRef?.removeEventListener("scroll", scrollHandler)
}
}, [])
return {
isTop,
isBottom,
isScrollable,
}
}
and there is my use case like below:
const divRef = useRef<HTMLDivElement>(null)
const { isTop, isBottom, isScrollable } = useScrollState(divRef)
return (
<>
<div ref={divRef}>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
<p style={{ border: "dashed 1px red", height: "150px" }}>test</p>
</div>
</>
)
React refs are meant to remember some value that doesn't require a rerender when the value changes. So it is not possible to refresh your hook based on whether the ref value has changed. If you need your component to rerender, you have to use state.
React docs say:
When you want a component to “remember” some information, but you don’t want that information to trigger new renders, you can use a ref.
Source: https://beta.reactjs.org/learn/referencing-values-with-refs
UPDATE: As per my understanding, you have ref as null so when you try to attach an event listener, it doesn't work. To address this, you can use callback ref. Basically, you pass a useCallback() function as ref into the DOM node. This way, you will be notified when ref updates even if the DOM node is created later:
const divRef = useCallback(node => {
// set ref in a state variable inside your custom hook
// basically you need a new useState call in your custom hook
}, [])
<div ref={divRef}>
...
</div>
You can read more about it here: https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
targetRef is available after the very first render, in this useEffect callback:
useEffect(() => {
targetRef?.addEventListener("scroll", scrollHandler)
return () => {
targetRef?.removeEventListener("scroll", scrollHandler)
}
}, [])
But you don't need to pass isTop or isBottom as dependecies, instead pass an empty array for useEffect to only run after the first render.

How to make an MUI 5 checkbox & label change color on hover?

I'd like to have a checkbox with a label in a wrapper. When the wrapper is hovered everything should change color. See the image:
Here is my code:
const styles = {formControlLabel:{
color:'#d7dae0',
border: '1px solid white',
span: {
color: '#d7dae0',
'&:hover':{border: '1px solid red'},
}
},
}}
export function MyCheckbox(){
return(
<FormControlLabel
control={<Checkbox />}
label={'test'}
sx={styles.formControlLabel}
/>
)
}
I've tried many different things, but this is the closest I've come. I can't seem to put a '&:hover' on the formControlLabel styles at the top level - it has to be imbedded in another element. Why is that?
Sandbox: https://codesandbox.io/s/magical-feynman-lzy1e2?file=/src/App.tsx
You need to change your :hover to your parent and set borderColor in the parent and having span inside the :hover parent, it will change to red at the same time
const styles = {
formControlLabel: {
border: "1px solid",
p: "8px",
m: "20px",
"&:hover": {
borderColor: "red",
span: {
color: "red"
},
}
}
};

is it possible to animate a strikethrough with React Spring?

I'm new to React Spring and I initially tried this
const strikeProps = useSpring({
textDecoration: "line-through",
from: { textDecoration: "none" },
});
But it's not working. I think there should be a way simulate the CSS solution for this.
The problem here is, that the original CSS solution is uses pseudo element for emulating the strike trough. We can only add react-spring properties for normal html elements. So the most compact way is to create a separate strike through component for this problem. For example:
const StrikeTroughtText = ({ children, weight = 1 }) => {
const props = useSpring({
from: { width: "0%" },
to: { width: "100%" }
});
return (
<div style={{ position: "relative", display: "inline-block" }}>
{children}
<animated.div
style={{
position: "absolute",
top: "50%",
left: 0,
width: props.width,
height: `${weight}px`,
background: "black"
}}
/>
</div>
);
};
We basically animate the width of the absolutely positioned div containing a black line over the text.
You can use it like a div component:
<StrikeTroughtText>text</StrikeTroughtText>
For bigger font size the default 1 px line weight is not enough, so I added a weight property also.
Here is my example: https://codesandbox.io/s/react-strike-trought-text-component-with-react-spring-animation-86cfd?file=/src/App.js

What is the idiomatic way to do styling in Material-UI?

There are a few ways to do styling in Material-UI.
Use makeStyles:
import Box from '#material-ui/core/Box'
const useStyles = makeStyles(theme => ({
testStyled: (props: isBoolean) => ({
width: props ? '1px' : '2px',
})
}))
<Box className={useStyles(true).root}></Box>
Use MuiBoxProps:
import Box, { MuiBoxProps } from '#material-ui/core/Box'
let isTrue = true
let props: MuiBoxProps = {
style: {
marginBottom: isTrue ? '1px' : '2px'
}
}
<Box {...props}></Box>
In the second way, I can put a special input for each individual component. For example:
let props: MuiBoxProps = {
mb: '1px'
}
Type inference can be used to see compilation errors.
However, this method is not applicable to makeStyles because its return type is always CSSProperties.
In the first case, however, I can style them internally by adding props.
So, in conclusion:
Type inference is impossible for a specific component. Can apply props in batch.
Type inference is possible for a specific component. Can't apply props in batch.
What is the official recommendation?
Actually, doesn't matter which way you are choosing because both the have in official documents. the matter is that in which method or way you are comfortable with.
But Yes, I think the best way is withSyles which is also I use in my professional development code.
Here is the Example:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const styles = {
root: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
},
};
function HigherOrderComponent(props) {
const { classes } = props;
return <Button className={classes.root}>Higher-order component</Button>;
}
HigherOrderComponent.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(HigherOrderComponent);

Resources