Collapsing and Expanding Side Panels in React - javascript

I apologize is this is a very simple task this is really my first time transitioning to react.
I have an existing application built with js/css/html that I am attempting to shift over to react. It has a full length horizontal side panel that is open by default, when the window is shrunk beyond a point it collapses with a hamburger icon, and expands again when the window is resized larger. Fairly easily done with media queries in css.
A perfect example is https://purecss.io/layouts/side-menu/ (note the side menu) This is exactly what the current app does.
I'm struggling to do this in react. I can build a side panel that is collapsible (https://reactjsexample.com/react-side-nav-component/) and mofify it for my needs, but I cannot figure out how to set it up so it collapses and expands by itself. I understand I can set it up react to use media queries, however I figured there was likely a more efficient way.
Any advice of good libraries to use, or examples would be greatly appreciated.

You could do something like this:
const Component = props => {
const [windowWidth, setWindowWidth] = useState(0)
useEffect(() => {
window.addEventListener('resize', updateWindowDimensions)
updateWindowDimensions()
return () => window.removeEventListener('resize', updateWindowDimensions)
}, [])
useEffect(() => {
if (windowWidth < 500) {
closeModal()
return
}
openModal()
}, [windowWidth])
updateWindowDimensions() {
setWindowWidth(window.innerWidth)
}
}

Related

How to animate a button when it is visible for the user by scrolling

When you scroll on a page, the page shows an element, I want to be able to specify this element and
do code with it using JS, is this possible? I tried something but it had another problem..
What I tried was,
let section = document.getElementById('out');
window.onscroll = function() {
if (window.scrollY >= 678) {
document.getElementById('out').style.color = "red";
} else {
document.getElementById('out').style.color = "black"
}
}
I didn't use animate here, I just made sure it works, and it did, well almost, because if you zoom in/out it ruins it, I think that's because I got the 678 by going to the button and printing scrollY manually, is there anyway to make that automatic, so it works on any element I need?
I searched a lot and can't seem to find what I need, the solutions need jQuery, I need a solution only with html, css, and javascript.
In the future the solution will be css scroll timelines, but as that feature is at the time of writing experimental and is not supported by major browsers you can use intersection observers.
Quoted from MDN:
The Intersection Observer API lets code register a callback function that is executed whenever an element they wish to monitor enters or exits another element (or the viewport), or when the amount by which the two intersect changes by a requested amount.
To animate a component when it is in or out of view, you can give animated elements a .hidden class in your html markup and create an intersection observer which appends the .shown class to .hidden elements when they are in view.
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => entry.target.classList.toggle(“shown”, entry.isIntersecting))
})
const hiddenElements = document.querySelectorAll(“.hidden”)
hiddenElements.forEach((el) => observer.observe(el))
Then you can just apply transitions under a <selector>.shown css rule.

Scroll to top of a bubble in botframework webchat

Some answers of our chatbot are very long. The webchat scrolls automatically to the bottom so users have to scroll up to get to the top of the bubble and start reading.
I've implemented a custom renderer (react) to wrap the answers into a custom component which simply wraps the answer into a div-tag. I also implemented a simple piece of code to scroll to the top of the bubble.
const MyCustomActivityContainer = ({ children }) => {
const triggerScrollTo = () => {
if (scrollRef && scrollRef.current) {
(scrollRef.current as any).scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
}
const scrollRef: React.RefObject<HTMLDivElement> = React.createRef()
return (
<div ref={ scrollRef } onClick={ triggerScrollTo }>
{ children }
</div>
)
}
export const activityMiddleware = () => next => card => {
if (/* some conditions */) {
return (
<MyCustomActivityContainer>
{ next(card) }
</MyCustomActivityContainer>
);
} else {
return (
{ next(card) }
)
}
};
But this only works if the scrollbar slider is not at its lowest position (there is at least 1 pixel left to scroll down, see here). The problem is the useScrollToBottom hook which always scrolls to bottom automatically if the scrollbar is completely scrolled down.
Is there any way to overwrite the scroll behavior or to temporarily disable the scrollToBottom feature?
As there is no reproducible example I can only guess.
And I'll have to make some guesses on the question too.
Because it's not clear what exactly in not working:
Do you mean that click on the <div> of MyCustomActivityContainer and subsequent call to triggerScrollTo doesn't result into a scroll?
That would be strange, but who knows. In this case I doubt anyone will help you without reproducible example.
Or do you mean that you can scroll the message into view, but if it is already in the view then new messages can result into a scroll while user is still reading a message.
That's so, but it contradicts with you statement that your messages are very long, because that would be the problem with short messages, not with the long ones.
But anyway, you should be able to fix that.
If it works fine with 1 pixel off the lowest position, then just scroll that 1 pixel. You'll need to find the scrollable element. And do scrollable_element.scrollTop -= 1. I tested this approach here. And it worked (there the scrollable element is the grandparent of <p>'s)
Or do you try to scroll automatically at the moment the message arrives? Аnd that is the real issue, but you forgot to mention it, and didn't posted the code that tries to auto-scroll?
In that case you can try to use setTimeout() and defer the scroll by, let's say, 200ms.
This number in based on what I gathered from the source:
BotFramework-WebChat uses react-scroll-to-bottom
In react-scroll-to-bottom there are some timeouts 100ms and 34ms
BotFramework-WebChat doesn't redefine them
There are some heuristics in react-scroll-to-bottom that probably coursing the trouble
https://github.com/compulim/react-scroll-to-bottom/blob/3eb21bc469ee5f5095a431ac584be29a0d2da950/packages/component/src/ScrollToBottom/Composer.js
Currently, there are no reliable way to check if the "scroll" event is trigger due to user gesture, programmatic scrolling, or Chrome-synthesized "scroll" event to compensate size change. Thus, we use our best-effort to guess if it is triggered by user gesture, and disable sticky if it is heading towards the start direction.
And
https://github.com/compulim/react-scroll-to-bottom/blob/f19b14d6db63dcb07ffa45b4433e72284a9d53b6/packages/component/src/ScrollToBottom/Composer.js#L91
For what we observed, #1 is fired about 20ms before #2. There is a chance that this stickyCheckTimeout is being scheduled between 1 and 2. That means, if we just look at #1 to decide if we should scroll, we will always scroll, in oppose to the user's intention.
That's why I think you should use setTimeout()
Since there isn't a reproducible code for me tweak and show you. My suggestion is tweak your code slightly. Chatbot requires constant streaming of data when a new message arrives calculate the height of the div element created for the message. If the div element is greater than the widget height scroll to the top else you can choose to leave it as it is.

Conditionally Rendering Markup (JSX) vs. CSS `display: none` - Which is better practice?

Say I've got a component with identical content, but presented two totally different markup structures depending on the device (desktop viewports or mobile viewports).
In this situation when the viewport is below or above certain width or breakpoint (for this example 768px), I want to show one over the other.
A common situation for something like this might be the Navigation, where at Desktop views you have a simple navigation bar in the header of the page, whilst at Mobile views you have a more complex navigation menu that slides in and out:
import React from 'react';
import './Navigation.scss';
const Navigation = () => {
return (
<div className="navigation">
<div className="mobile-navigation-container">
<MobileNavigation />
</div>
<div className="desktop-navigation-container">
<DesktopNavigation />
</div>
</div>
);
};
Solution 1:
A simple solution to achieve this is to use CSS:
.navigation {
.mobile-navigation-container {
#media (min-width: 768px){
display: none;
}
}
.desktop-navigation-container {
#media (max-width: 767px){
display: none;
}
}
}
However, the issue here is that I still have both views in the DOM, even though one is not visible.
Solution #2:
Alternatively, I can use a resize listener and piece of state in my JSX component to conditionally render the correct component depending on the viewport width I can calculate using window.innerWidth:
import React, { Component } from 'react';
const isClient = typeof window !== 'undefined';
class Navigation extends Component {
state = {
viewportWidth: 0,
}
componentDidMount() {
if (isClient) {
this.updateWindowDimensions();
window.addEventListener('resize', this.updateWindowDimensions);
}
}
componentWillUnmount() {
if (isClient) window.removeEventListener('resize', this.updateWindowDimensions);
}
updateWindowDimensions = () => {
this.setState({ viewportWidth: window.innerWidth });
}
render() {
const { viewportWidth } = this.state;
return (
<div className="navigation">
{viewportWidth <= 768 && (
<div className="mobile-navigation-container">
<MobileNavigation />
</div>
)}
{viewportWidth > 768 && (
<div className="desktop-navigation-container">
<DesktopNavigation />
</div>
)}
</div>
);
}
This solves the issue of having duplicate content on the DOM. Which I'd guess is better for Search Engine Crawlers.
However, this somewhat makes my JSX more complicated, and I have the feeling that the CSS breakpoint is cleaner, smoother implementation in terms of performance, rather than using a JavaScript resize listener (though I can't find solid sources to advise one over the other).
My question is which of these two implementations is better practice and why?
The second approach Solution #2 is very good as compared to Solution #1. Because #1 has unnecessary and unwanted elements in DOM Object which is also confusing for react. Although it is not a good practice in any other languages as well. But in #2 you are not rendering unwanted contents this will improve smooth running of your code and debugging and designing is also easy in this approach.
Definitely the 2nd one even though it involves more lines of code, the overall performance outcome is much better because you don’t have extra pieces of DOM elements laying unnecessarily here and there in the page.
What’s more important is the flexibility provided by solution 2, what if you have to change the underlying markup on different screens in the future? (ex. hide some columns in smaller screens)

Event listeners in the init of a JavaScript component

It is actually the first time I am trying not to do spaguetti code, so it means I am new in this component world.
I have being selected to develop our new top nav bar. And even if it is something really simple... I would like to do a JS component for it.
More or less there is an sketch here: https://codepen.io/tureey/pen/qXQJYv
My problem:
I have something like this now.
function Menu ( view, device, resolutionX, resolutionY) {
this.collapsed = false;
this.view = view;
this.device = device;
this.search = '';
this.resolution = {
x: resolutionX,
y: resolutionY
};
}
// So, this would be nice to have it in a resize eventlistenner once we init our menu
Menu.Prototype.changeVersionbyResolution = function() {
// This should be casted if we resize the windows less than 992px
// It adds/removes classes to the whole menu to display the things totally different
}
// So, this would be nice to have it in a scroll eventlistenner once we init our menu
Menu.Prototype.collapse = function() {
// This should be casted if we scroll down
// It adds/removes classes to the whole menu to display the things totally different
}
var menu = new Menu('ng_live_events', 'xiaomi mi5s', window.innerWidth, window.innerHeight);
I would like to:
The own menu should be able to change himself (so it means he has to know when we scroll down or scroll top, when we resize the window (to change the format at 992px), etc...)
How can I do it?
PS: if you know really good posts where I can find it or examples would be nice.
PS2: I am reading a lot of patron stuff and these things too!
Thanks to everyone for your time!
Victor.

React Native Card Carousel view?

Does anyone know how can we achieve this kind of view in React Native, or is there any available components out there that can help on this issue?
I've seen in F8 2016 app too, been searching on how to achieve the transition and the carousel-like view with horizontal scrolling.
I know that the question is old, but a co-worker and I recently had to create a component that answers this particular need. We ended up open-sourcing it, so it's all yours to try: react-native-snap-carousel.
The plugin is now built on top of FlatList (versions >= 3.0.0), which is great to handle huge numbers of items. It also provides previews (the effect you were after), snapping effect, parallax images, RTL support, and more.
You can take a look at the showcase to get a grasp of what can be achieved with it. Do not hesitate to share your experience with the plugin since we're always trying to improve it.
Edit : two new layouts have been introduced in version 3.6.0 (one with a stack of cards effect and the other with a tinder-like effect). Enjoy!
You can achieve this using ScrollView with paging enabled on iOS and ViewPagerAndroid on Android.
F8 being an open source app,
you can see that's what it's actually using:
https://github.com/fbsamples/f8app/blob/master/js/common/ViewPager.js
This component renders all pages.
If you only want to have the visible and left and right pages rendered to save memory, there's another component built on top of it that does it:
https://github.com/fbsamples/f8app/blob/master/js/common/Carousel.js
There are various other similar implementations available:
https://js.coach/react-native?search=carousel
https://js.coach/react-native?search=swiper
However I'm not recommending https://github.com/leecade/react-native-swiper as I've had several issues with it.
Speaking about the swiper-component claiming the best of the world, it still does not work out of the box (as of November 2018) as described in the official swiper-react-native documentation. The issue and a workaround is described in the swiper issue 444:
The error message (on Android) states console.error: "fontFamily 'Arial' is not a system font and has not been loaded through Exponent.Font.loadAsync.
Zach Dixon provided an elegant quick-fix which I repeat here for everybody's convenience. Simply use the following JSX-snippet inside your render()-function to avoid that a new font is required:
<Swiper style={styles.wrapper} showsButtons={true}
nextButton={<Text>></Text>} prevButton={<Text><</Text>}>
<View style={styles.slide1}><Text style>Slide 1</Text></View>
<View style={styles.slide2}><Text style>Slide 2</Text></View>
<View style={styles.slide3}><Text style>Slide 3</Text></View>
</Swiper>
For those interested in explanations on how to implement carousel with Scroll-View only, I recommend a tutorial on a simple image carousel with ScrollView. The tutorial is straight forward and elaborates on the things one has to take care of, but you cannot use it out of the box within or on top of other View-elements. In particular the snapping does not work to well (on Android).
You can create your own custom carousel. The Carousel end result looks like this-
goToNextPage = () => {
const childlenth = this.getCustomData().length;
selectedIndex = selectedIndex + 1;
this.clearTimer();
if (selectedIndex === childlenth) {
this.scrollRef.current.scrollTo({ offset: 0, animated: false, nofix: true });
selectedIndex = 1;
}
this.scrollRef.current.scrollTo({
animated: true,
x: this.props.childWidth * selectedIndex,
});
this.setUpTimer();
}
// pushing 1st element at last
getCustomData() {
const {data} = this.props;
const finaldata = [];
finaldata.push(...data);
finaldata.push(data[0]);
return finaldata;
}
This is the main logic used behind looped carousel.
Here we are pushing the first item at last in the list again and then when scroll reaches at last position we are making the scrollview to scroll to first position as first and last element are same now and we scroll to first position with animation like this
this.scrollRef.current.scrollTo({ offset: 0, animated: false, nofix: true });
For further reference go through the link provided.
https://goel-mohit56.medium.com/custom-horizontal-auto-scroll-looped-carousel-using-scrollview-42baa5262f95

Categories

Resources