Changing react-leaflet map width with button click - css

I am working on a map with react-leaflet. I placed a button on the map that will ideally open a menu on the left side of the map. The way I want to open up the menu is by changing the width of the map from 100% to 80%. The menu button toggles a boolean, which ideally the map will rerender and resize when the button clicks.
this is the code in my App.js
export default function App() {
const [isMarkerClick, setIsMarkerClick] = React.useState(false);
const [isMenuOn, setIsMenuOn] = React.useState(false);
function toggleMarker() {
setIsMarkerClick(prevClick => !prevClick);
}
function toggleMenu() {
setIsMenuOn(prevMenu => !prevMenu);
}
return (
<div>
<MainMap isMenuOn={isMenuOn} />
<MarkerButton isMarkerClick={isMarkerClick} toggleMarker={toggleMarker} />
<MenuButton toggleMenu={toggleMenu} />
</div>
)
}
this is where the Map Code lives
export default function MainMap(props) {
const isMenuOn = props.isMenuOn;
const isMarkerClick = props.isMarkerClick;
const zoom = 15;
const position = ['39.9526', '-75.1652'];
const [marker, setMarker] = React.useState(["", ""])
return (
<div>
<MapContainer
center={position}
zoom={zoom}
zoomControl={false}
style={{
height: "100vh",
width: isMenuOn ? "80vw" : "100vw"
}}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={marker}></Marker>
{isMarkerClick && <ClickTrack setMarker={setMarker} />}
</MapContainer>
</div>
)
}
as of now, when I click the menu button, the isMenuOn state updates and which then feeds back into the MapContainer and should rerender with a new width but it doesn't. Any Ideas on how to get change the map size with a click of button using react?
example of the menu button
I thought that when clicking the menu button and changing the state of isMenuOn to "true", the mapcontainer would be listening and rerender with using the new width. Although it seems like setting the width through style ={{}}, might not allow for changes?

React Leaflet will only set the width and height when the component mounts and does not update it with the state changes, in order to update the map's width you need to re-render the component fully and to do that set the component's key to something that will change with the isMenuOpen value
here is an example
<MapContainer
center={position}
zoom={zoom}
zoomControl={false}
key={isMenuOn ? "open-state": "closed-state"}
style={{
height: "100vh",
width: isMenuOn ? "80vw" : "100vw"
}}
>

The accepted answer works, but I suspect it would cause a full map reload. You can try keeping the width value static in the MapContainer element, and instead change it the wrapping div like this:
<div style={{ width: isMenuOn ? '80vw' : '100vw' }}>
<MapContainer
center={position}
zoom={zoom}
zoomControl={false}
style={{
height: '100vh',
width: '100%',
}}
/>
</div>
There is a caveat though; the map needs to be informed of the change in size, otherwise it will seem buggy. I suspect that's why the developers don't update the style after the map mount.
You can however add a map child that monitors the isMenuOn prop (or any other prop that changes your layout), and let the map instance know they should invalidate everything about sizing:
function LayoutListener({ isMenuOn }) {
const map = useMap();
useEffect(() => {
map?.invalidateSize();
}, [map, isMenuOn]);
return null;
}
And then just put in the MapContainer children:
<MapContainer>
<LayoutListener isMenuOn={isMenuOn} />
{otherChildren}
</MapContainer>

Related

How to show only one Item at a time in React native's FlatLists?

I would really like to make this design but I don't know how to implement it in code, any suggestions?The issue is:I don't know how to show only one item at a time on the FlatList and how to make the centered item look closer.Lastly,How can I make the scroll go from one item to the other,without beeing sort of in the middle?
I tried looking online but I haven't been able to find what I need, if you found something please let me know.
I think you are looking for a carousel. Please refer to this library.
A list that allows both dragging and pressing a button.
For that i would use a horizontal FlatList. Then, make each item in the list take up 100% of the screen width and use the FlatList's ref param to scroll to the next item in the list when pressing a button and have a useState to keep track of which index the user is at to align button and swiping.
Heres how i would do it.
Get 100% screen width
import { Dimensions } from 'react-native';
const { width } = Dimensions.get('window');
Set up horizontal FlatList
The key here is the params: pagingEnabled and horizontal.
onViewableItemsChanged is called whenever focused item changes.
useCallback is for optimization. import it with useState
// Method passed to FlatList. Displays the data's text.
const renderItem = useCallback(
({ item }) => (
<View style={[styles.card, { width }]}>
<Text style={styles.cardItem}>{item.text}</Text>
</View>
),
[]
);
<FlatList
ref={flatListRef} // Reference to list. Used to scroll to specific items.
horizontal
pagingEnabled // cause snapping to items
data={data}
renderItem={renderItem}
onViewableItemsChanged={onScroll} // Calling with anonymous function here causes issues
viewabilityConfig={{
itemVisiblePercentThreshold: 100,
}}
/>
Button
Using the flatListRef and my useState, to scroll to the next index and keep track of where the user is at.
import React, { useState, useRef, useCallbac } from 'react';
const flatListRef = useRef(null);
const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
const onButtonPress = useCallback(() => {
if (currentSectionIndex === data.length - 1) {
// What you want to do at the end of the list.
} else if (flatListRef.current) {
// Go to the next item
flatListRef.current.scrollToIndex({
index: currentSectionIndex + 1,
});
setCurrentSectionIndex(currentSectionIndex + 1);
}
}, [currentSectionIndex, data.length]);
OnScroll function
We wanted to allow dragging and pressing a button to go move on in the list.
So when the user scrolls, we use this function, which we passed to the FlatLists onViewableItemsChanged
// When scrolling set useState to keep track of where the user is at.
const onScroll = useCallback(({ viewableItems }) => {
if (viewableItems.length === 1) {
setCurrentSectionIndex(viewableItems[0].index);
}
}, []);

Material-UI text field with dynamic rows

I'm trying to have a multiline TextField component that takes all available space depending on a device.
I know fullWidth but is there a way to have something like fullHeight in rows setting, depending on a device that it is displayed on?
You basically want your TextField element to take full height and width of the container.
For width you can simply add fullWidth prop to your TextField element,
<TextField fullwidth/>
For adding required height,
If you have prop Multiline={true} in your TextField element, Material Ui sets numbers of rows dynamically and height is adjusted according to number of row (everytime user hits enter new row is generated), due to this you can not set specific height.
Now to be able to set height manually, you must add prop rows={1}*
<Textfield multiline rows={1} fullwidth />
Now, you should be able to set height with JSS,
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(() => ({
inputMultiline : {
"& .MuiInputBase-input" : {
height : '100vh', //here add height of your container
},
}
}));
Now simply add this to className of your TextField element,
<TextField multiline fullWidth row={1} className={classes.inputMultiline} />
For any doubt refer to,
material ui TextField API here and
material ui styles API here.
(although, no proper information regarding this is available in docs)
A solution is to manupulate Material-UI classes using JSS.
I change the height of the input base and align the text at start vertically.
const useStyles = makeStyles(() => ({
input: {
height: "100%",
"& .MuiInputBase-root": {
height: "100%",
display: "flex",
alignItems: "start"
}
}
}));
Add the input classes to your input and it's work.
All the exemple bellow on this codesandbox.

Can Material-UI TextField width be set to match width of input text?

Is it possible to have Material-UI automatically adjust the width of the TextField element to match the width of the input text?
I am creating a form view/edit page and I'm rendering the data back into the same fields, however there is also a series of parameters which the server sets. It would be nice to render in disabled form elements and have their width automatically fit.
I've played with the width properly of both TextField and the underlying Input with no success. I could potentially count the characters and set a width in JS, but I'd rather a CSS solution.
<TextField
disabled={true}
label={"UUID"}
value={"7be093a5647d41ff8d958928b63d11f5"}
style={{width: "auto"}}
InputProps={{
style: {width: "100%"}
}}
/>
https://codesandbox.io/s/material-demo-forked-c3llv
You could base the width of the input on the length of the text
const FONT_SIZE = 9
const DEFAULT_INPUT_WIDTH = 200
const [textValue, setTextValue] = useState("")
const [inputWidth, setInputWidth] = useState(DEFAULT_INPUT_WIDTH)
useEffect(() => {
if (textValue.length * FONT_SIZE > DEFAULT_INPUT_WIDTH) {
setInputWidth((textValue.length + 1) * FONT_SIZE)
} else {
setInputWidth(DEFAULT_INPUT_WIDTH)
}
}, [textValue])
return (
<div>
<TextField
label={"UUID"}
value={textValue}
onChange={(e) => setTextValue(e.target.value)}
InputProps={{
style: { width: `${inputWidth}px` },
}}
/>
</div>
)
Below is the forked codesandbox
Reference: this answer

How can i scroll bottom on onPress of button in react-native?

I have added the button on simple screen and I want to scroll Bottom whenever I press the button. what code to add to button onPress?
render() {
return (
<ScrollView>
{this.yourComponents()}
<Button>Scroll To Bottom </Button>
</ScrollView>
)
}
You could do this using ref.
render() {
return (
<ScrollView ref={scrollView => this.scrollView = scrollView}>
{this.yourComponents()}
<Button onPress={() => {this.scrollView.scrollToEnd()}}>Scroll To Bottom </Button>
</ScrollView>
)
}
You can also do this using the useRef hook.
const scrollView = useRef();
const scrollView = useRef();
const onPress = () => {
scrollView.current.scrollToEnd();
}
<Button onPress={onPress} />
This question is quite similar to this one but isn't exactly the same, that is why i'll provide an answer.
ScrollView provide a ScrollTo() method that allows you to scroll to a x/y position in your scrollview.
let _scrollViewBottom
<ScrollView
ref='_scrollView'
onContentSizeChange={(contentWidth, contentHeight)=>{
_scrollViewBottom = contentHeight;
}}>
</ScrollView>
then use the ref name of the scroll view like
this.refs._scrollView.scrollTo(_scrollViewBottom);
Additionally, if your goal is to frequently push new data and scroll at the end of your scrollview, you might wanna take a look at react-native-invertible-scroll-view.
InvertibleScrollView is a React Native scroll view that can be inverted so that content is rendered starting from the bottom, and the user must scroll down to reveal more. This is a common design in chat applications
For functional component
<ScrollView
ref={ref => { scrollView = ref }}
onContentSizeChange={() => scrollView.scrollToEnd({ animated: true })}
>
...
</ScrollView>
For class component
<ScrollView
ref={ref => { this.scrollView = ref }}
onContentSizeCange={() => this.scrollView.scrollToEnd({ animated: true })}
>
...
</ScrollView>
let _scrollViewBottom
<ScrollView
ref={scrollViewRef}
onContentSizeChange={(contentWidth, contentHeight)=>{
_scrollViewBottom = contentHeight;
// this make move your scroll
scrollViewRef.current.scrollTo({x:0,y:_scrollViewBottom ,animated:true});
}}>
</ScrollView>
Additionally, if your goal is to frequently push new data and scroll at the end of your scrollview, you might wanna take a look at react-native-invertible-scroll-view.
InvertibleScrollView is a React Native scroll view that can be inverted so that content is rendered starting from the bottom, and the user must scroll down to reveal more. This is a common design in chat applications

React Native - Move Button on Tap

I'm new to React Native (experiences iOS developer in Swift) and I can't seem to find anything online for how to move UI elements in response to touch events. (Or maybe I'm just really bad at searching stack overflow lol).
Eventually, I want to have a textbox in the center of the screen, and when the user begins to type (or taps on the box to start typing), the box will slide to the top of the screen. However I can't even find a simple tutorial for this. I know that I can have a const style that defines style/position aspects of the textbox, and I can create a second style and then on button tap I can change the textbox's style and re-render, but it seems overkill to make an entire second style, where only one attribute is changing.
Can someone provide sample React Native code, where there is a text label and a button on the screen, and when the user taps the button, the label moves from above the button to below the button?
Check out this example of moving an element on touch. I use a <TouchableWithoutFeedback> and onPressIn. When you touch it, it changes the x and y position of it:
https://snack.expo.io/#noitsnack/onpressin-twice
import React, { Component } from 'react';
import { Text, View, TouchableWithoutFeedback, Image, Dimensions } from 'react-native';
import IMAGE_EXPO from './assets/expo.symbol.white.png'
const IMAGE_SIZE = 100;
export default class App extends Component {
state = {
score: 0,
...getRandXY()
}
render() {
const { score, x, y } = this.state;
return (
<View style={{flex:1, backgroundColor:'steelblue' }}>
<Text style={{fontSize:72, textAlign:'center'}}>{score}</Text>
<TouchableWithoutFeedback onPressIn={this.addScore}>
<Image source={IMAGE_EXPO} style={{ width:IMAGE_SIZE, height:IMAGE_SIZE, left:x, top:y, position:'absolute' }} />
</TouchableWithoutFeedback>
</View>
)
}
addScore = e => {
this.setState(({ score }) => ({ score:score+1, ...getRandXY() }));
}
}
function getRandXY() {
return {
x: getRandInt(0, Dimensions.get('window').width - IMAGE_SIZE),
y: getRandInt(0, Dimensions.get('window').height - IMAGE_SIZE)
}
}
function getRandInt(min, max)
{
return Math.floor(Math.random()*(max-min+1)+min);
}
Once you get this movement down you can move to the Animation API to get teh sliding effect.

Resources