I am having some issues with a mobile modal I have been working on that uses react-select.
The selectors shown below are inside of a div with a fixed height and overflow-y: scroll. Whenever I selected an option for the select 'Choose observer', the entire modal will jump down in the viewport (shown in the last picture) for a split second, then return to normal. This will be quite jarring for the end user and it's an issue I would like to get fixed.
This only happens in certain screen orientations. On ipad it is in landscape mode and on iphone it is both orientations. This leads me to believe it is due to the height of the container but theres not much I can do about that as there is the header and navigation tabs.
I have tried several solutions including:
menuPosition="fixed"
blurInputOnSelect={false}
menuShouldScrollIntoView={false}
<Selector
ignoreAccents={false}
menuPortalTarget={menuPortalTarget}
components={{ DropdownIndicator }}
className={className}
options={options}
formatGroupLabel={CustomGroup}
isClearable={isClearable}
isDisabled={disabled}
styles={selectStyles}
placeholder={placeholder}
isSearchable={true}
getOptionValue={getOptionValue}
getOptionLabel={getOptionLabel}
onChange={onSelectedChange}
defaultValue={initialSelected}
controlShouldRenderValue={false}
filterOption={customFilter}
onCreateOption={onCreateOption}
menuPlacement={menuPlacement}
openMenuOnFocus={autoFocus}
autoFocus={!isMobileOrTabletDevice}
menuIsOpen
ref={(e) => (selectRef.current = e)}
id={id}
formatOptionLabel={formatOptionLabel}
isMulti={isMulti}
hideSelectedOptions
/>
const selectStyles = {
control: (provided) => ({
...provided,
margin: 8,
...props.controlStyle,
}),
menu: () => ({ borderTopRightRadius: 0, borderTopLeftRadius: 0 }),
menuList: (provided) => ({
...provided,
maxHeight: '185px',
}),
menuPortal: (base) => ({ ...base, zIndex: 9999 }),
};
If someone could point my in the right direction on how to fix this behavior that would be greatly appreciated. Thank you in advance.
Edit: So i've done some more investigating, and I believe the crux of the issue is that when selecting react select wants to scroll what you just selected into view. With the way I have my modal setup it is a fullscreen modal that overlays on top of a page that is larger than the viewport. When the modal opens I turn overflow hidden and height 100% to the body, but when selecting it still scrolls the page behind the modal.
menuShouldScrollIntoView ={false} adding this props into react-select solved my problem.
Related
so the styling in the css={{....}} property gets applied in my browser, but not in Safari on my iPhone. How do i add styling for iOs Safari, and also how can i make it, so that the scrollbar doesn't disappear and is always visible?
The only style which seems to get applied on iPhone is the webkit-scrollbar-track witdh. (i tested by setting the width to 100px)
I already tried this and this, in order to make the scrollbar always visible, but with no success. Below is the code and images of the scroll component on iPhone and Google Chrome on my Mac.
I also tried using both sx={{....}} css={{....}} while testing. But no difference.
<MenuList
variant={"outline"}
bgColor={"#000000"}
rounded={10}
borderColor={"blue.200"}
>
<Box
css={{
"&::-webkit-scrollbar": {
width: "4px",
},
"&::-webkit-scrollbar-track": {
width: "6px",
},
"&::-webkit-scrollbar-thumb": {
background: "#8ccef0",
borderRadius: "24px",
},
}}
overflowX="auto"
maxHeight="200px"
>
{coinsList
.map(({ label, path }) => (
<MenuItem
pt={1}
pb={1}
key={path}
as={Link}
to={path}
color={dropDownValue === label ? undefined : "blue.200"}
>
{label}
</MenuItem>
))}
</Box>
</MenuList>
Menu in Google Chrome on my Mac. Handlebar always visible and styled(blue color):
Menu in Safari on my iPhone. Handlebar only visible while scrolling and not styled(grey color, thinner, only track-width style gets applied):
[
[EDIT] While looking on the images after posting the question i noticed, that the whole menu is grey on mobile image. This is due to sending the image to my Mac using Telegram. It is actually the same blue color as the Chrome image, except for handlebar (just for clarification.)
I'm trying to customize the cursor pointer so I used a hook to get the mouse position and then I placed the component in absolute in that coordinates.
Here a working code.
There is a flag (USE_PNG) that you can toggle to test if to use a React component or a png (I would prefer the first idea but I'm interested also to the second one if it has some advantages).
USE_PNG = false -> use a React component
As you can see, the code is very simple and it works enough but it has some problems:
when the mouse is on the left side of the window, the left half of the cursor is cut off, but when is on the right then it's not and the horizontal bar appears
it seems not so fluid. Are there some tricks I can use to optimize the code?
USE_PNG = true -> use a png
I tried also to use a png (simpler maybe) but I can't see the cursor anymore
What's the problem?
I use a ref instead of a state and performance have improved.
The first problem remains: the horizontal and vertical scroll when the cursor is on the right or bottom side of the window
I don't think simply hiding the scrollbars is an optimal solution because the window size has changed and the user can scroll.
I think we need to find a cleaner solution.
Edit - Overflowing body (AKA third problem)
if you'll add this to your body tag it should solve it:
margin: 0;
height: 100%;
overflow: hidden
edit - Regarding your second problem
to prevent scroll bars to appear, you can use overflow-y: hidden; (to disable on the x-axis just change the overflow-y to overflow-x, overflow: hidden; for both)
BUT if you would like to enable scrolling but just hide the scrollbar, use the following code:
/* hide scrollbar but allow scrolling */
body {
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
overflow-y: scroll;
}
body::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
}
here is a gif of a working example on my browser:
https://imgur.com/a/wOV7car
It doesn't get cut off for me on the right side (see image below). It sounds like the second problem happens because your cursor gets re-rendered every time you move it, and that's a ton of work for your site!
you should remove the style attributes from the Cursor component and adjust the code inside your event listener for a mousemove event.
it will look like this:
onMouseMove = {e => {
const cursor = document.querySelector('.cursor')
cursor.style.top = ׳${e.pageY}px׳
cursor.style.left = ׳${e.pageX}px׳
}}
Flickering:
#01:
Simply introduce a transition style on the Cursor component, eg transition: "all 50ms". It makes the position change much more smoother and cancels the flickering.
#02:
However as Guy mentioned above, handling the cursor's position in a state means a lot of re-rendering for your component which makes you app slower in the end.
I'd recommend making a ref for the cursor and update it directly from an event listener. With that change you can even remove the useMouse hook:
const App = () => {
const containerNodeRef = useRef(null);
const cursorRef = useRef(null)
const updateMouse = (e) => {
// you can directly access the mouse's position in `e`
// you don't even need the useMouse hook
cursorRef.current.style.top = `${e.y - SIZE / 2}px`
cursorRef.current.style.left = `${e.x - SIZE / 2}px`
}
useEffect(() => {
window.addEventListener('mousemove', updateMouse)
return () => {
window.removeEventListener('mousemove', updateMouse)
}
})
return (
<div
ref={containerNodeRef}
style={{
width: window.innerWidth,
height: window.innerHeight,
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
cursor: 'none'
}}
>
<Cursor ref={cursorRef} />
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const Cursor = forwardRef(({ size = SIZE }, ref) => (
<div
ref={ref}
style={{
position: "absolute",
width: size,
height: size,
backgroundColor: "black",
color: "white",
fontWeight: "bold",
display: "flex",
justifyContent: "center",
alignItems: "center"
}}>
Hello
</div>
)
)
Cut off issue:
Since you're moving around an actual div as your cursor, when it reaches the border of your document, it stretches it to make the div fit -> this is why the document doesn't fit into your window anymore thus the scrollbars are rendered.
You can fix it via css:
body { overflow: hidden }
+1:
I'm not sure how your cursor has to look like in the end, but if it'd be an image, there is an other possible solution. Add this css rule to your container, which loads an image as a cursor and then the browser takes care about the rendering automatically:
<div
ref={containerNodeRef}
style={{
// ...
cursor: 'url("url"), auto'
}}
>
If you'd use this solution then the whole Cursor component and position calculation wouldn't be needed anymore. However the downside is, that it only works with images.
There are already 2 good answers, but I'll add this one too, because other answers are overcomplicating the solution
Flickering
it doesn't matter if you use ref or state, you should just extract
your cursor to separate Component, that way your App component will not rerender
Scrollbars
as other anwers mentioned, using body { overflow: hidden; } will solve this problem, but partially. Your cursor is trying to go beyond page size, hence page is showing scrollbars, adding limitation for cursor position will solve this: cursor.y = Math.max(0, Math.min(currentPositionY, page.width)) (pseudo-code) now cursor.y will not exceed 0 or page.width
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);
};
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.
I'm trying to display a list of rows in a React Native ListView, but it only shows the entries that fit in a single screen, ie, I can't scroll down to see more rows.
Here are the styles:
styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 60
},
rowContainer: {
flexDirection: 'row',
justifyContent: 'space-around'
}
})
ListView:
return (
<View style={styles.container}>
{this.getHeader()}
<ListView
dataSource = {this.state.dataSource}
renderRow = {this.renderRow.bind(this)}/>
</View>
)
Row:
return (
<View style={styles.rowContainer}>
<Text>{text}</Text>
</View>
)
What am I doing wrong?
I had the same issue and found that Listview with wrapper View outside will make ListView not scrollable.
The workaround will be removing wrapper View:
return (
<ListView
dataSource = {this.state.dataSource}
renderRow = {this.renderRow.bind(this)}/>
)
I'll post this here in spite of the OP already having found a solution b/c my ListView wouldn't scroll either and it wasn't b/c I was trying to scroll rather than using the mouse to simulate swiping. I am currently using React-Native v0.1.7.
It turned out that the reason my ListView wouldn't scroll & wouldn't reveal the other rows which weren't being initially rendered on screen was that it didn't have a fixed height. Giving it a fixed height solved this issue. Figuring out how to determine what value to use for the height wasn't a simple matter though.
In addition to the ListView on the page there was also a header at the top of the page which had a fixed height. I wanted that ListView to take up the remaining height. Here, either my ability to use the React-Native limited implementation of FlexBox failed me or the implementation did. However, I was unable to get the ListView to fill up the remainder of the space and be able to scroll properly.
What I did was to require the Dimensions package const Dimensions = require('Dimensions'); and then set a variable for the device window's dimensions const windowDims = Dimensions.get('window'); Once I had that done I was able to determine the height of the remaining space and apply that inline to the style of the ListView: <ListView style={{height: windowDims.height - 125}} ... /> where 125 was the height of the header.
What worked for me was to follow this response: https://stackoverflow.com/a/31500751/1164011
Basically, I had
<View>
<ListView/>
</View>
And what was happening was that the <View> component was getting a height of 0. So I needed to update it to:
<View style={{ flex: 1 }}>
<ListView/>
</View>
That way <View> gets its height based on the content.
Wrap listview and other contents in scroll view.. That solved my scroll issue.
ListView contains an inner ScrollView so it should work as is. On the simulator your scroll by clicking and dragging the mouse.
Why don't you show the full code, maybe some screenshots?