Rotate an icon when containing Button is clicked - javascript

I have a Material-UI button whose markup looks like this:
<Button
disableFocusRipple={true}
disableRipple={true}
color="inherit"
onClick={openBlogMenu}
className={classes.blogButtonStyle}
>
<LibraryBooksIcon />
Blog
{!blogMenu && <KeyboardArrowDownIcon />}
{blogMenu && <KeyboardArrowUpIcon />}
</Button>
<BlogDropDown pageURL={pageURL} />
Here, the openBlogMenu changes the blogMenu Redux state which toggles BlogDropDown into view. As apparent, I'm also reading this state to toggle between the two arrow icons, KeyboardArrowDownIcon and KeyboardArrowUpIcon.
So far, this setup works smooth. However, I'm keen on doing away with KeyboardArrowUpIcon altogether and instead, making KeyboardArrowDownIcon rotate 180 degrees upon click.
I know it's as easy as just adding one of the available pseudo-classes, e.g., :active or :focus to the icon element. But then it'll only rotate if the icon itself is clicked rather than the entire button.
I also understand one could just dynamically add a transform attribute (transform: rotate(180deg);) to the icon's CSS in the click handler, but that wouldn't animate the transition.
Any tips?
P.S.: For illustration of what I'm looking for, check out menu option More on https://www.flipkart.com/

You can use CSS to only flip the icon, not the button. Here is the solution that works perfectly fine for me:
const useStyles = makeStyles (theme => ({
open : {
transform: "scaleX(1)",
},
close: {
transform: "scaleX(-1)",
},
}));
export default function Test() {
const classes = useStyles();
const drawerToggle = () => { setOpen(!open) };
const [open, setOpen] = React.useState(false);
return (
<IconButton aria-label="open drawer" onClick={drawerToggle} edge="start">
<MenuOpenRoundedIcon className={clsx(!open && classes.close, open && classes.open)}/>
</IconButton>
)
}
Use clsx (or any equivalent conditional class selector) to assign when the icon needs to be flipped based on the state of the button. Here, I'm using a state hook, open, and toggle its boolean value on click. Later, I used clsx to decide, based on the value of open, which class I assign to the icon, i.e., which direction I need to flip it.
If you want to flip vertically, you can use scaleY instead.
Here is the code I wrote in the code sandbox with both Button and IconButton examples (vertical and horizontal): https://codesandbox.io/s/festive-maxwell-3kcge?file=/src/App.js

Related

React native Modal bypasses expo navigation bar setting

In my App.js i have set :
import * as NavigationBar from "expo-navigation-bar";
...In my component
useEffect(() => {
if (android) {
NavigationBar.setBackgroundColorAsync("transparent");
}
}, []);
which sets my navigation bars transparent in all screens,but when a modal is visible :
<Modal
animationType="none"
transparent
visible={isVisible}
presentationStyle="overFullScreen"
hardwareAccelerated
>
...navigation bar becomes white,even when i try to set it also within my modal component as well,any known solutions for this ?
I had this problem in Android as well. What I believe is happening is the react-native modal is simply taking the default android:navigationBarColor in the styles.xml and so every time it pops up, it overwrites the current navigation bar color until it gets dismissed.
The flag statusBarTranslucent did not work for me.
I was able to fix this by navigating to /res/values/styles.xml in your app or src folder
It really helps if you change your view style to "Android"
then
Then in the AppTheme style I added navigationBarColor to be transparent.
It looks like this
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColor">#android:color/black</item>
<item name="android:navigationBarColor">#android:color/transparent</item> // <----This is what I added
</style>
Then in my react native code, I used react-native-navigation-bar-color. I believe this should work with expo-navigation-bar since the main issue is derived from react native modal trying to overlay the default navBarColor value.
import changeNavigationBarColor from "react-native-navigation-bar-color";
const setDarkMode = (isDarkMode: boolean) => {
// logic to change my app's themeProvider to dark mode
changeNavigationBarColor(isDarkMode ? "black" : "white"); // Then I change the navigation bar color.
};
Hopefully this helps someone!

Menu positioned incorrectly after hovering a MenuItem element (Material-UI)

When hovering a MenuItem, the positon of the Menu container should not change.
[x] The issue is present in the latest release. Please not that if you downgrade MUI to v4.5.0
It behaves as expected.
Current Behavior 😯
When hovering a MenuItem, the positon of the Menu container changes.
Expected Behavior 🤔
When hovering a MenuItem, the positon of the Menu container should not change.
Steps to Reproduce 🕹
Steps:
Open https://codesandbox.io/s/magical-ritchie-9pxh2 (MUI 4.9.5)
Hover the second ListItem, notice a text appears.
Click the first MenuItem after the Menu appears.
Notice: If you downgrade MaterialUI to 4.5.0 It behaves as expected.
Here it works : https://codesandbox.io/s/purple-mountain-rzvbd (MUI version 4.5.0)
This is most-likely due to this PR: https://github.com/mui-org/material-ui/pull/19046.
Use visibility to hide the element which is the anchor (to make it stay in the DOM) - this will ensure your menu will not move to a wrong position.
<ListItemIcon
style={{ visibility: hoveredItem ? "visible" : "hidden" }}
onClick={e => {
e.stopPropagation();
onClickSideText(e);
}}
>
Or you can hide/show the element based on whether Menu is visible or not. In this case you have to hive the hoveredItem when you close the menu.
const onMenuItemLeave = e => {
if (!anchorElelement.anchorEl) {
e.stopPropagation();
setHoveredItem(null);
}
};
const closeItemMenu = () => {
setAnchorElelement({});
setHoveredItem(null);
};

Debounce jsx element's rendering in React

I am wondering if it is possible to debounce a jsx element's rendering. I have an animation of a panel expanding that has some content in it. If the panel is empty (just the background) the animation is smooth and works as expected. The animation and associated components are all from Material-UI. The issue arises when the content is in the panel already so the animation (width expansion) just skips out to the width of the content making the animation look choppy. Here is a similar example to what I am referring to. This code is my code I am using in my panel and they work the same in terms of the expansion. Only difference is the content in this example is just lorem ipsum so the animation appears to work fine. Is it possible to debounce the <CardContent /> component's rendering like this?
{ open &&
(
_.debounce(e => {
return (
<CardContent>
{/*content in here*/}
</CardContent>
)
}, 300)
)
}
or something similar (this doesn't work) so the panel will be fully expanded through the animation before the content is rendered?
I'd like to add a comment however my reputation isn't above 50 yet so unfortunately I can't :p.
JS Animation
To answer your question, if you are using JavaScript animations like GSAP or AnimeJS you can have a state flag called. isAnimating = null. Then when your animation starts set the flag to true and when it's finished set it to false. Then in your render() method write a conditional render like this.state.isAnimating === false && <CardContent />.
CSS Animation
If you are using CSS animation you can use the transitionend event to wait for the animation to end. Therefore you have to create a ref on the animating DOM element to which you can attach the event.
Does that help you a bit? Please let me know if you have any other questions.
Here is a solution using react spring library
I added a state variable in order to display or not the card content
const [contentVisible, setContentVisible] = React.useState(false);
I created a spring to handle the width transition, I set contentVisible to true as soon as the width approaches the 300
const { width } = useSpring({
width: open ? 300 : 120,
onFrame: ({ width }) => setContentVisible(width > 299.8)
});
Finally, I created an animated card component
const AnimatedCard = animated(Card);
...
<AnimatedCard classes={{ root: classes.card }} style={{ width }}>

How to start react-native onTouchDown anination

I am using Expo and the latest version of React-Native and I want to provide a subtle tactile feedback for Components like a View or Button.
I currently use animatable to start a pulse animation on the onPress() event but the animation only fires once the finger is released.
I want a subtle size reduction whilst press then a smooth tween back when released - that would feel elegant and not annoying to me.
Can this be done? I thought Animation or Animatable would have easily supported this but I can’t find any similar examples.
You could make your own touchable using the Gesture Responder System
Basically you'll use the props onStartShouldSetResponder, onResponderGrant, and onResponderRelease passed to an Animated.View.
class MyTouchable extends React.PureComponent {
render(){
<Animated.View
style={{ your styles, and animation values }}
onStartShouldSetResponder={() => true}
onResponderGrant={({ nativeEvent }) => {
//run your animations here
}}
onResponderRelease={({ nativeEvent }) => {
//animate back to zero, call your onPress function
//passed via props
}}
/>
}
}

React Native slide out panel and scrollview

I am developing an app with react native. I have this UI element which is similar to that of Maps in iOS, in which you slide a panel from the bottom and inside it, there is a scrollable list.
For the slide-out panel, I am using a component called rn-sliding-up-panel. It has several props as event listeners. For example
<SlidingUpPanel
allowDragging={/*Boolean*/}
onDragStart={()=>{} /*When it is about to be dragged*/}
onDrag={()=>{} /*When it is being dragged*/}
onDragEnd={()={} /*When the user is no longer touching the screen*/}
></SlidingUpPanel>
Inside it, I have a <ScrollView> containing a <List> from react-native-elements. As far as I know, it has only one vent listener, being:
<ScrollView onScroll={()=>{}}></ScrollView>
My issue is that scrolling on the list actually causes the panel to close (it closes by sliding down). I found a work-around by adding a state, and modfiying it onScroll:
state = {
dragPanel: true,
}
/*===========================================*/
<SlidingUpPanel allowDragging={this.state.dragPanel}>
<ScrollView onScroll={()={ this.setState({dragPanel: false}) }}></ScrollView>
</SlidingUpPanel>
However, I cannot find a way to restore the dragging, and it doesn't fire up as efficiently.
TL;DR
Is there an eficient way to implement a ScrollView inside a SlidingUpPanel without the events of each overlapping? Maybe using something similar to function(e){e.preventDefault();}?
To properly disable / restore outer scroll dragging, do
_onGrant() {
this.setState({ dragPanel: false });
return true;
}
_onRelease() {
this.setState({ dragPanel: true });
}
constructor(props) {
super(props);
this._onGrant = this._onGrant.bind(this);
this._onRelease = this._onRelease.bind(this);
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: this._onGrant,
onPanResponderRelease: this._onRelease,
onPanResponderTerminate: this._onRelease,
});
}
render() {
<SlidingUpPanel allowDragging={this.state.dragPanel}>
<ScrollView
{...this._panResponder.panHandlers}
/>
</SlidingUpPanel>
}
From what I had been searching for a long time, preventDefault() is a pure web-javascript thing, I think there are no preventDefault in react-native.
From document section Handling Touches, react-native just use javascript to simulate Objc (iOS) & Java (Android) events.
Set the minimumDistanceThreshold property to something around 50. Maybe 30 for small screens and 50-60 for bigger ones. Do it like so:
<SlidingUpPanel minimumDistanceThreshold={isSmallScreen ? 30 : 50}>
<ScrollView style={{flex: 1}}>
</ScrollView>
</SlidingUpPanel>
It might be late for an answer but use your scroll view as absolute positioned and position it accordingly.
Think of the scroll view as a pop-up dialog that appears in front of the backdrop behind it. Upon clicking the backdrop, the pop-up dismisses. Apply similar logic to the issue by letting scroll view in front of the slide up panel.

Categories