Apply styles only during animation in framer-motion - reactjs

I have a collapsible panels animated with framer motion. When the panel switches from collapsed to open the overflow is visible. This breaks the layout somewhat as the content of the panel body then overlays the content below. However, I need the overflow to properly display a dropdown list inside the panels.
I came up with the following idea.
Basically I want to apply "overflow:hidden" to the StyledPanelBody when the framer animation is being executed. This would prevent the ugly effect with the overlay. However, I couldn't find any way to do that from the documentation. There is just a "transitionEnd" property.
const PanelBody = ({children, isFramed, isOpen}) => {
const currentVariant = isOpen ? 'open' : 'closed'
const loadFeatures = () =>
import('./framerMotionFeatures.js').then(res => res.default)
return (
<LazyMotion features={loadFeatures}>
<StyledPanelBody
initial={currentVariant}
animate={currentVariant}
variants={{
closed: {height: 0},
open: {height: 'auto'}
}}
transition={{ease: 'easeInOut', duration: 0.3}}
isFramed={isFramed}
isOpen={isOpen}
>
<div>
{children}
</div>
</StyledPanelBody>
</LazyMotion>
)
}

You can try this as a workaround:
variants={{
closed: {
height: 0,
overflow: 'hidden',
},
open: {
height: 'auto',
transitionEnd: {
overflow: 'unset',
}
}
}}

Related

Making signaturePad component take up full screen

Within one of my components a user can click a button that pulls up a signaturePad they can sign on. This is working, however, I'd like the signaturePad to take up the full screen, whereas now it appears between the footer and header. How can I accomplish this? I played around with using { flex: 1 } on the View of the SignaturePad, but this had no visible effect.
Here is the relevant code from the parent component. If a certain state is true, the signaturePad displays:
{this.state.signaturePanelIsVisible && (
<SignaturePanel
session={this.props?.session}
signaturePanelIsVisible={this.state.signaturePanelIsVisible}
/>
)}
And the signaturePad component code looks like this:
const SignaturePanel = (props) => {
const dispatch = useDispatch();
return (
<SignaturePanel
actionStyle={{
...styles.texts.mediumText,
color: styles.colors.primary,
textDecorationLine: 'underline',
}}
onCancel={async () => {
props.hideSignaturePanel();
}}
onSave={async (base64) => {
const base64Result = base64.base64DataUrl.substr(base64.base64DataUrl.indexOf(',') + 1);
dispatch(
await updateSignature({
...props.signature,
guid: props.session.guid,
value: base64Result,
lastUpdate: Date.serverTime(),
})
);
}}
/>
);
};
export default SignaturePanel;
And the styling applied looks like this:
container: {
width: '100%',
height: '100%',
zIndex: 98,
position: 'absolute',
backgroundColor: '#fff',
},
You'll need to add position: 'absolute' to the style of your modal. Setting the height and width to 100% as well sometimes works, depending on how your app is set up. This will either cover the screen with the component, or make the component appear under the header and overflow the bottom of the screen. To fix the latter issue, add top: -<height of header>. This way the component will move up and cover the header.
<Modal style={styles.modal}>
<View style={styles.signaturePad}>
</View>
</Modal>
modal: {
position: 'absolute',
height: '100%',
width: '100%',
top: -n, //(where n = height of your header)
}
signaturePad: {
height: '100%',
width: '100%',
}
Depending on how your project is set up, you may not need the top value. These are general settings that you should usually put on a modal component. It's considered best practice to have a modal cover the whole screen, then add the component you want to display as a subcomponent.

framer-motion in nextjs won't animate a margin of -100vh to 0vh but animates 0vh to 100vh just fine

I am trying to animate a navbar to open and close. I have a larger container that animates by changing height, and I want to slide down this inner container while it animates its height. However, the animation only works one way, so when the navbar is closing it animates as if it's sliding up but when it's opening there is no animation.
const innerVariants = {
open: {
marginTop: "0",
},
closed: {
marginTop: "-100vh",
},
};
<motion.div
initial={false}
variants={innerVariants}
animate={isOpen ? "open" : "closed"}
transition={{ duration: 0.5 }}
style={{ height: "100vh" }}
>
</motion.div>
Error Message:
You are trying to animate marginTop from "-100vh" to "0vh". -100vh is not an animatable value - to enable this animation set -100vh to a value animatable to 0vh via the `style` property.
It's hard to tell without seeing more code but you may need to use the window height as a number instead of 100vh as a string.
Something like this:
const windowHeight = window.innerHeight;
const innerVariants = {
open: {
marginTop: 0,
transition: {
duration: 0.5
}
},
closed: {
marginTop: -windowHeight,
transition: {
duration: 0.5
}
}
};
here's a working code sandbox: https://codesandbox.io/s/stackoverflow-framer-motion-in-nextjs-wont-animate-a-margin-of-100vh-to-0vh-but-animates-0vh-eiduvq?file=/src/App.js

react-spring transition is not showing when first time rendering

I am using React-spring for first time. I am trying to use transition hook on a side drawer on my page by toggling a button.
But when I am clicking on that button there is no animation as that side drawer opens instantly, but if I click second time then side drawer is closing with animation.
And also if I click that button before that drawer removed from DOM then slide from left animation is there. I can't figure it out where is the problem. Help me please. Thanks.
Here is my code:
import React, { useState } from "react";
import { useTransition, animated, config } from "react-spring";
const Transform = (props) => {
const myStyle = {
position: "fixed",
left: 0,
top: 0,
zIndex: 100,
backgroundColor: "black",
};
const [drawerIsOpen, setDrawerState] = useState(false);
const closeDrawerHandler = () => {
setDrawerState((v) => !v);
};
const transition = useTransition(drawerIsOpen, {
form: { transform: "translateX(-100%)", opacity: 0 },
enter: { transform: "translateX(0%)", opacity: 1 },
leave: { transform: "translateX(-100%)", opacity: 0 },
config: { duration: 2000 },
// config: config.molasses,
// openDrawerHandler: () => setDrawerState(true),
});
return (
<>
{transition((style, item) =>
item ? (
<animated.aside
className='bg-white h-100 w-70 shadow'
style={{ ...style, ...myStyle }}
onClick={closeDrawerHandler}
>
<nav className='h-100'>
<h2>It's a Side Drawer</h2>
</nav>
</animated.aside>
) : (
""
)
)}
<div className='d-flex justify-content-end'>
<button className='btn btn-primary ' onClick={closeDrawerHandler}>
Toggle Btn
</button>
</div>
</>
);
};
export default Transform;
Image of transition problem
I made spelling mistake. Silly one, I spelt from as form . That's why it was happening.

Is it possible to show an Ant Design Tooltip conditionally only when the content has ellipsis?

I'm using the Ant Design component <Paragraph> to show a text with a variable size, and I use the ellipsis option to show "..." when that text exceeds the length of its container.
I also added a <Tooltip> to that <Paragraph> to show the entire text when it is collapsed:
<Tooltip title="This text is displayed, so I don't want to show a tooltip.">
<Paragraph ellipsis>
This text is displayed, so I don't want to show a tooltip.
</Paragraph>
</Tooltip>
Unfortunately, I can't find a way to know when the ellipsis appears on the paragraph, so my Tooltip is always shown.
Is it possible to show an Ant Design Tooltip only when the content has been affected by the ellipsis option?
Ant Design v4.2.0 made that possible by adding an "onEllipsis" function to the "ellipsis" object.
TooltipParagraph API
Working example
const TooltipParagraph: React.FC<ParagraphProps> = ({
children,
ellipsis,
...props
}) => {
const [truncated, setTruncated] = useState(false);
return (
<Tooltip title={truncated ? children : undefined}>
<Paragraph
{...props}
ellipsis={{ ...ellipsis, onEllipsis: setTruncated }}
>
{/* NOTE: Fragment is necessary to avoid showing the title */}
<>{children}</>
</Paragraph>
</Tooltip>
);
};
I do not know about antdegin, But I faced this problem once and I was able to resolve it.
To know if an text tag has overflown this check will be truthy:
element.offsetWidth < element.scrollWidth.
I tried to think about a generic way to solve this issue and ended up with this component (this is not production ready!)
function SmartText({
TypographyComponent = "h1",
TypographyComponentProps = {},
TooltipComponent = ({ children }) => children, // just for demo purpose, it will do nothing basically if not passed
ToolTipComponentProps = {},
children = ""
}) {
const textComponentRef = React.useRef(null);
const [tooltipTitle, setTooltipTitle] = React.useState("");
React.useEffect(() => {
if (
textComponentRef.current &&
textComponentRef.current.offsetWidth <
textComponentRef.current.scrollWidth
) {
setTooltipTitle(textComponentRef.current.innerText);
}
}, [children]);
return (
<TooltipComponent {...ToolTipComponentProps} title={tooltipTitle}>
<TypographyComponent {...TypographyComponentProps} ref={textComponentRef}>
{children}
</TypographyComponent>
</TooltipComponent>
);
}
The TypographyComponent in your case is the Paragraph, to use this method, the TypographyComponent has to forward the ref or to support it.
I used the SmartText with #material-ui tooltip like this:
<SmartText
TypographyComponentProps={{
style: {
whiteSpace: "nowrap",
maxWidth: 500,
textOverflow: "ellipsis",
overflow: "hidden",
textAlign: "left"
}
}}
TooltipComponent={Tooltip}
ToolTipComponentProps={{
placement: "right"
}}
>
Tooltip is needed because this text is long
</SmartText>
<SmartText
TypographyComponentProps={{
style: {
whiteSpace: "nowrap",
maxWidth: 300,
textOverflow: "ellipsis",
overflow: "hidden",
textAlign: "left"
}
}}
TooltipComponent={Tooltip}
ToolTipComponentProps={{
placement: "right"
}}
>
No tooltip needed
</SmartText>
Finally, here is working codesandbox demo

How to remove greyed out color with clicks on material-ui chips?

Trying to figure something out with the material-ui chips. When you click them, they hold a grey color until you click again somewhere else. I want to be able to click and have my active class and click again and have my inactive class. I can't figure out why there is a grey step in the middle.
<div className={classes.root}>
{this.props.chipData.map(data => {
return (
<Chip
key={data.key}
clickable={false}
clickableColorSecondary="white"
label={data.label}
onClick={this.props.toggleChipProperty(data.key)}
className={(data.showing ? classes.active : classes.inactive )}
/>
);
})}
</div>
CSS:
active: {
backgroundColor:"#4054B2",
color:"white",
height:"20px"
},
inactive: {
backgroundColor:"white",
color:"#575b6e",
border:"1px solid #8080806e",
height:"20px"
}
});
This image shows the grey part. You click, it shows grey on the button, then it finally shows the right color once you click off. I find this to be not intuitive. I want to click and have it simply toggle.
enter image description here
I had the same problem with Select Component of Material-UI.
I guess that you can do the same with Chip Component,
just change the MuiIconButton to MuiChip (and maybe select to root also):
Override MaterialUI Component Focus
Let me know if that works for you.
I had the same problem with Select component. I tried with the solution of Konstantinos and I end up solving this problem with 2 steps:
#1 Create a new theme
const theme1 = createMuiTheme({
overrides: {
MuiSelect: {
select: {
'&:focus': {
backgroundColor: 'none'
}
}
}
}
});
#2 Wrap component with theme provider
<MuiThemeProvider theme={theme1}>
<Select...
</MuiThemeProvider>
This is how I solved it and it worked for me. I used styled() Just set the
"&:focus": {
backgroundColor: bgColor
}
Component example:
const AutocompleteChipSemiIncluded = styled(Chip, {
shouldForwardProp: (prop) => prop !== 'bgColor',
})(({ bgColor, theme }) => ({
...(bgColor && {
maxWidth: '180px',
padding: "15px 5px",
backgroundColor: colors.primary.contrastText,
border: `1px dashed ${bgColor}`,
color: bgColor,
'& .MuiChip-deleteIcon': {
color: bgColor,
},
"&:focus": {
backgroundColor: bgColor
}
}),
}));

Resources