How execute this two OnScrolls? - javascript

I'm following this tutorial https://medium.com/appandflow/react-native-collapsible-navbar-e51a049b560a to create a navbar collapsed. Work's fine, but there's a problem, in this tutorial the autor set the props onscroll of the FlatList:
onScroll={Animated.event( [{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }], { useNativeDriver: true },)}
But i already using this onScroll Props for:
onScroll={event => this.handleScroll(event)}
My handle scroll will verify if is the end of the scroll and load more feed:
handleScroll (event) {
var endOfPage = event.nativeEvent.layoutMeasurement.height + event.nativeEvent.contentOffset.y >=
event.nativeEvent.contentSize.height;
if (endOfPage) {
this.getSections()
}
}
I cant running my function and the autor command in the same time. Is there a way to execute this two OnScroll?
My componenet AnimatedFlatList (The first OnScroll will not work because the other will override the last):
<AnimatedFlatList
contentContainerStyle={[collapse.contentContainer, {paddingTop: this.props.navigation.state.params.category == '' ? 166.5 : 96.5}]}
onMomentumScrollBegin={this._onMomentumScrollBegin}
onMomentumScrollEnd={this._onMomentumScrollEnd}
onScroll={event => this.handleScroll(event)}
onScrollEndDrag={this._onScrollEndDrag}
data={this.state.sections}
renderItem={this._renderSectionItem}
extraData={this.state}
keyExtractor={(item, index) => index.toString()}
removeClippedSubviews
onScroll={Animated.event( [{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }], { useNativeDriver: true },)}
/>

Based off of this answer:
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }],
{
useNativeDriver: true,
listener: event => this.handleScroll(event)
});
}}

Related

ToolTip does not disappear on scroll

I have a button on the site and a ToolTip to it, which describes the action of the button.
But there is one bug that I can not solve (and I'm already starting to doubt if there is a solution to this problem).
Description of the problem: when the user hovers over the icon, a tooltip appears - everything works fine here. But if at this moment the table is scrolling, then the tooltip flies out of bounds. It's hard to describe, take a look
Pay attention to how the tooltip (if the cursor is hovered over) flies up or down when scrolling.
Tell me how to solve this problem?
<div>
<Tooltip
title="Delete"
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: '#a3a3a3',
'& .MuiTooltip-arrow': {
color: '#a3a3a3',
},
},
},
}}
PopperProps={{
modifiers: [
{
name: "offset",
options: {
offset: [0, -8],
},
},
],
}}>
<DeleteForeverIcon/>
</Tooltip>
</div>
Instruction: hover over any cell from the first column, wait for the tooltip to appear. Then scroll the wheel up or down and see how the tooltip goes outside the table
P.s. Please note that this question has already been answered. And in principle this solution is working. But I had a lot of problems when adding this solution to my real code. Probably a simple solution for me here would be to simply cancel the scrolling when you hover over the button. Tell me how this can be done (but keep in mind that position: fixed is not suitable in this case)
My approach is different, where each tooltip maintains its own state. It is using IntersectionObserver to determine if the ToolTip component is viewable. When the component is no longer viewable, it will hide the Popper (the tooltip popup) by setting the CSS to display: 'none' via the sx prop on PopperProps.
Codesandbox Example: Here
Here is the modified file FileDownloadButton.jsx:
import React from "react";
import FileDownloadIcon from "#mui/icons-material/FileDownload";
import { ButtonGroup, Tooltip } from "#mui/material";
export default function FileDownloadButton() {
const tipRef = React.useRef(null);
const [inView, setInView] = React.useState(false);
const cb = (entries) => {
const [entry] = entries;
entry.isIntersecting ? setInView(true) : setInView(false);
};
React.useEffect(() => {
const options = {
root: null,
rootMargin: "0px"
};
const ref = tipRef.current;
const observer = new IntersectionObserver(cb, options);
if (ref) observer.observe(ref);
return () => {
if (ref) observer.unobserve(ref);
};
}, [tipRef]);
return (
<ButtonGroup>
<div>
<Tooltip
ref={tipRef}
title="Download record "
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: "#a3a3a3",
"& .MuiTooltip-arrow": {
color: "#a3a3a3"
}
}
}
}}
PopperProps={{
sx: { display: inView ? "block" : "none" },
modifiers: [
{
name: "offset",
options: {
offset: [0, -8]
}
}
]
}}
>
<FileDownloadIcon />
</Tooltip>
</div>
</ButtonGroup>
);
}
Changes for reference
Change 1
export default function FileDownloadButton() {
const tipRef = React.useRef(null);
const [inView, setInView] = React.useState(false);
const cb = (entries) => {
const [entry] = entries;
entry.isIntersecting ? setInView(true) : setInView(false);
};
React.useEffect(() => {
const options = {
root: null,
rootMargin: "0px"
};
const ref = tipRef.current;
const observer = new IntersectionObserver(cb, options);
if (ref) observer.observe(ref);
return () => {
if (ref) observer.unobserve(ref);
};
}, [tipRef]);
Change 2
PopperProps={{
sx: { display: inView ? "block" : "none" },
Update 1
Original poster wants toggle
Codesandbox example
import React, { useState } from "react";
import FileDownloadIcon from "#mui/icons-material/FileDownload";
import { ButtonGroup, IconButton, Tooltip } from "#mui/material";
import VisibilityOffIcon from "#mui/icons-material/VisibilityOff";
import VisibilityIcon from "#mui/icons-material/Visibility";
export default function FileDownloadButton() {
const [click, setClick] = useState(true);
const tipRef = React.useRef(null);
const [inView, setInView] = React.useState(false);
const cb = (entries) => {
const [entry] = entries;
entry.isIntersecting ? setInView(true) : setInView(false);
};
React.useEffect(() => {
const options = {
root: null,
rootMargin: "0px"
};
const ref = tipRef.current;
const observer = new IntersectionObserver(cb, options);
if (ref) observer.observe(ref);
return () => {
if (ref) observer.unobserve(ref);
};
}, [tipRef]);
return (
<ButtonGroup>
<div>
<Tooltip
ref={tipRef}
title={click ? "Show item" : "Hide Item"}
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: "#a3a3a3",
"& .MuiTooltip-arrow": {
color: "#a3a3a3"
}
}
}
}}
PopperProps={{
sx: { display: inView ? "block" : "none" },
modifiers: [
{
name: "offset",
options: {
offset: [0, -8]
}
}
]
}}
>
<IconButton onClick={() => setClick(!click)}>
{click ? <VisibilityOffIcon /> : <VisibilityIcon />}
</IconButton>
</Tooltip>
</div>
</ButtonGroup>
);
}
I think this is browser specific issue. When I checked the given url( https://codesandbox.io/s/silly-grass-1lb3qw) in firefox browser it was working fine(but not in the chrome). Later figured that out hover while scrolling on element will work differently in the chrome compare to other browsers since latest versions.
I made following changes to make it work in chrome. Basically whenever we hover any item then the material tooltip is being added to the document. So what I did was I have attached an scroll event and if there is any material tooltip element is present I just simply removed it.
DeviceTable.jsx
export default function DevicesTable() {
const tableRef = useRef();
function removeElementsByClass(className){
const elements = document.getElementsByClassName(className);
while(elements.length > 0){
elements[0].remove();
}
}
useEffect(() => {
if (tableRef.current) {
tableRef.current.addEventListener("scroll", (e) => {
// CLASS NAME OF THE TOOLTIP ATTACHED TO THE DOM. THERE ARE MULTIPLE CLASSES BUT I FOUND FOLLOWING CLASSNAME TO BE UNIQUE. PLEASE CROSS CHECK FROM YOUR END AS WELL.
//YOU CAN CHECK THIS BY PASSING open={true} attribute on <Tooltip> AND INSPECT DOM
removeElementsByClass("css-yk351k-MuiTooltip-tooltip")
});
}
return () => {
if(tableRef.current) {
tableRef.current.removeEventListener("scroll", ()=>{});
}
}
}, []);
return (
<TableContainer className="TableContainerGridStyle">
<Table className="TableStyle">
<DevicesTableHeader />
// CHANGED LINE
<TableBody ref={tableRef} className="TableBodyStyle">
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
</TableBody>
</Table>
</TableContainer>
);
}
Apart from the above I think you can use another alternatives like followCursor, setting the position relative attribute to the table cell(TableCellStyle) or body. But these don't solve the problem fully.
As you are passing Table component as props children to the StateLabel component so in order to display/render we need to update StateLabel component to use props.children
export default function StateLabel({children}) {
return <div>{children}</div>;
}
Div hover not working when scrolling in chrome

Passing div style as a function argument Javascript/ React

I'm building an application using React, framer-motion and react-intersection-observer.
Inside animation.js I have a function which is imported inside App.js and used as a component in App.js.
I want to apply an aspect ratio to some divs using style as a parameter, but it doesn't work.
<FadeAnimation name={'project-image--image'} style={'--ratio':16/9} />
Failed to set an indexed property on 'CSSStyleDeclaration': Indexed property setter is not supported.
I have other divs with this property and they are displayed correctly
<div className='project-image--image' style={{'--ratio':1/2}}/>
Animation.js
export const container = {
hidden: { opacity: 0, y: 5 },
visible: { opacity: 1, y: 0 }
}
function FadeAnimation({ children, name, delay, duration, style }) {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start("visible");
}
}, [controls, inView]);
return (
<motion.div className={`${name}`} style={{`${style}`}}
ref={ref}
animate={controls}
initial="hidden"
transition={{ duration: duration, delay: delay }}
variants={{
visible: { opacity: 1, y: 0 },
hidden: { opacity: 0, y: 5 }
}}
>
{children}
</motion.div>
);
}
Have you tried:
<FadeAnimation name={'project-image--image'} style={{'--ratio':16/9}} />
(Adding another curly brace)
And then, in the FadeAnimationComponent using it as
<motion.div className={`${name}`} style={style} {/*...*/}/>

How to call a click event on react geo chart?

I have basic geo chart from react geocharts
<Chart
width={calculateMapHeight()}
height={'575px'}
chartType="GeoChart"
data={user.details}
getSelection={(e) => console.log('test')}
select={console.log('test')}
enableRegionInteractivity={true}
regionClick={(e) => console.log('test')}
onClick={(e) => console.log('test')}
mapsApiKey="apikey"
rootProps={{ 'data-testid': '1' }}
options={{
backgroundColor: 'transparent',
defaultColor: red,
animation: {
startup: true,
duration: 2500,
},
}}
/>
but I can't work out what I need to do to call an event when a user click on a country? I've tried to log stuff from all the methods above but nothing is working
also as a side note, it only seems to show the country on hover when that country has been passed into my map. is it possible to turn it on for all countries?
Define an array of chartEvents. In your case use select as eventName. Use chartEvents prop and supply the chartEvents array to it.
The callback receives the selected array using which you can figure out the index of your chart data array. Upon country selection, simply use your orignial whole data and find the selected country.
Use ready event and make an api call and fetch all countries and put them in a state and use it as data to chart. This way, you can dynamically have all countries are populated in the chart
Working demo with sample data - codesandbox
const options = {
title: "Population of Largest U.S. Cities",
chartArea: { width: "30%" },
hAxis: {
title: "Total Population",
minValue: 0
},
vAxis: {
title: "City"
}
};
export default function App() {
const [allCountries, setAllCountries] = useState([["Country"]]);
const chartEvents = [
{
eventName: "select",
callback({ chartWrapper }) {
const selectedId = chartWrapper.getChart().getSelection();
if (selectedId.length) {
// console.log("Selected Country", data[selectedId[0].row + 1]);
console.log("Selected Country", allCountries[selectedId[0].row + 1]);
} else {
console.log("No Country to show ");
}
}
},
{
eventName: "ready",
callback: ({ chartWrapper, google }) => {
fetch("https://restcountries.eu/rest/v2/all").then(res =>
res.json().then(res => {
const allCountries = res.map(c => [c.name]);
console.log("allCountries", allCountries);
setAllCountries(prev => [...prev, ...allCountries]);
})
);
}
}
];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Chart
// width={calculateMapHeight()}
height={"575px"}
width={"575px"}
chartType="GeoChart"
// data={user.details}
chartEvents={chartEvents}
// data={data}
data={allCountries}
getSelection={e => console.log("test")}
// select={() => console.log("test")}
enableRegionInteractivity={true}
regionClick={e => console.log("test")}
onClick={e => console.log("test")}
mapsApiKey="apikey"
rootProps={{ "data-testid": "1" }}
options={{
backgroundColor: "transparent",
defaultColor: "red",
animation: {
startup: true,
duration: 2500
}
}}
/>
</div>
);
}
There is an example of handling select event.
With your code:
const chartEvents = [
{
eventName: "select",
callback({ chartWrapper }) {
console.log("Selected ", chartWrapper.getChart().getSelection());
}
}
];
<Chart
width={calculateMapHeight()}
height={'575px'}
chartType="GeoChart"
data={user.details}
chartEvents={chartEvents}
enableRegionInteractivity={true}
mapsApiKey="apikey"
rootProps={{ 'data-testid': '1' }}
options={{
backgroundColor: 'transparent',
defaultColor: red,
animation: {
startup: true,
duration: 2500,
},
}}
/>
NOTE: if you use some old version < 3.0 then chartEvents prop isn't available, instead you can use events prop.

Persistent Victory Charts VictoryVoronoiContainer tooltips on click

I'm implementing a combination VictoryLine and VictoryScatter chart with Victory-Charts using VictoryVoronoiContainer for displaying values at the mouse hover location. I need to make these hover details persist on click at multiple locations.
Example of just hover data: https://formidable.com/open-source/victory/docs/victory-voronoi-container/
Specifically, if a user clicks while a given popup is active, using VoronoiDimension='x' in this case, the hover popup details remain visible at their X coordinate. In a perfect world, any number of these would be visible.
Using events (https://formidable.com/open-source/victory/guides/events/). I can sort of fake it on a scatter point click (https://jsfiddle.net/kn6v9357/3/) but with the voronoidimension hover it's difficult to see the points and you have to be awfully precise with the click. Plus, when there's overlap you only trigger a click on the top layer, so overlapping data isn't shown on what persists.
Any suggestions or ideas?
Code for VictoryScatter events in from the jsFiddle (note this doesn't do what I want) with just a scatter component to keep it simple:
<VictoryChart
containerComponent = { <VictoryVoronoiContainer
voronoiDimension="x"
//voronoiBlacklist={['Scatter0','Scatter1','Scatter2',]}
labels={
(d) => {
return (
`${d.market}: ${d.metric1}`
)
}
}
/> }
>
<VictoryAxis
style = {{tickLabels: {fontSize: 8,angle: -15}}}
label = 'Date'
/>
<VictoryAxis dependentAxis
label = {this.state.metric === 'metric1' ? 'Metric 1' : 'Metric 2'}
style = {{tickLabels: {fontSize: 8}, axisLabel: {padding: 40}}}
axisLabelComponent = { <VictoryLabel style = {{fontSize: 12}} dx = {20} /> }
/>
{ this.viewablePlaces.map((place, i) =>
<VictoryGroup
animate = {{easing: "cubic",duration: 500,onLoad: {duration: 0}}}
key = {place + String(i) + 'Group'}
data = {this.get_data(place)}
x = {(d) => moment(d.date).format('MMM YY')}
y = {this.state.metric === 'metric1' ? 'metric1' : 'metric2'}
>
<VictoryScatter
key = {place + String(i) + 'Scatter'}
name = {"Scatter"+i}
style = {{
data: {fill: "#455A64",cursor: "pointer"},
labels: {fontSize: 12,padding: 2}
}}
size = {2.5}
labels={(d) => d.market + ': ' + String(d.metric2)}
labelComponent = {
<VictoryTooltip
orientation = {"top"}
pointerLength = {5}
pointerWidth = {3}
cornerRadius = {3}
/>
}
events={[
{
target: "data",
eventHandlers: {
onMouseOver: (props) => {
return [
{
target: "labels",
mutation: {active: true}
}
];
},
onMouseOut: (props) => {
return [
{
target: "labels",
mutation: (props) => props.name === 'clicked' ? {name: 'clicked', active: true} : null
}
];
},
onClick: (props) => {
return [
{
target: "data",
mutation: (props) => props.style.fill === 'orange' ?
null : {style: Object.assign({}, props.style, {fill: 'orange'}), size: 3.5}
},
{
target: "labels",
mutation: (props) => props.name === 'clicked' ?
null : {name: 'clicked', active: true}
}
];
}
}
}
]}
/>
</VictoryGroup>
)}
</VictoryChart>
Edit:
Here's another example with persistent points, but I need to find a way to also make the labels persist.
https://codesandbox.io/s/oq1w9xj8q6

react-native-navigation navigator is undefined custom button

I am using React Native Navigation by Wix (https://github.com/wix/react-native-navigation)
I am using Redux with my app also.
I am trying to add a custom button to my top bar, so I can trigger opening and closing the drawer.
I am adding a drawer to the tab as follows :
Navigation.startTabBasedApp({
tabs: [
{
label: 'Home',
screen: 'swiftyApp.Home',
icon: icons.homeOutline,
selectedIcon: icons.home,
title: 'Home',
navigatorStyle,
navigatorButtons: {
leftButtons: [
{
id: 'custom-button',
component: 'CustomButton',
passProps: {
text: 'Hi!'
}
}
]
}
}
],
drawer: {
left: {
screen: 'swiftyApp.Drawer',
passProps: {}
},
style: {
drawerShadow: false,
contentOverlayColor: 'rgba(0,0,0,0.25)',
leftDrawerWidth: 75,
rightDrawerWidth: 25
},
type: 'MMDrawer',
animationType: 'slide',
disableOpenGesture: false
},
appStyle: {
orientation: 'portrait',
hideBackButtonTitle: true
}
});
});
My Custom Button component looks like
const CustomButton = props => {
console.log(props);
const { text, navigator } = props;
return (
<TouchableOpacity
style={[styles.button, { backgroundColor: 'tomato' }]}
onPress={() =>
navigator.toggleDrawer({
side: 'left',
animated: true
})
}
>
<View style={styles.button}>
<Text style={{ color: 'white' }}>{text}</Text>
</View>
</TouchableOpacity>
);
};
The button displays and the styles are applied as expected. However when clicking the button an exception is thrown that the onPress fails as navigator.toggleDrawer is undefined, on checking the output of the navigator props being passed in, I can see in the log:
2017-11-25 13:33:48.703 [info][tid:com.facebook.react.JavaScript] '************', { testID: 'CustomButton',
navigator: undefined,
passPropsKey: 'customButtonComponent3',
rootTag: 21,
text: 'Hi!' }
Navigator is indeed undefined. I cannot for the life of my word out why.
How do I pass navigator into something such a custom button for the navbar, so I can toggle a drawer open or trigger a modal?
Custom buttons aren't associated with a navigator. You'll need to set the button in the screens' constructor and pass the navigator in props.
constructor(props) {
super(props);
this.props.navigator.setButtons(this.navigatorButtons(this.props.navigator));
}
navigatorButtons = (navigator) => {
return {
leftButtons: [
{
id: 'custom-button',
component: 'CustomButton',
passProps: {
text: 'Hi!',
navigator
}
}
]
};
}
Don't forget that custom left button isn't supported on Android.

Categories

Resources