React Modals visible for a split-second on page load - css

I am rendering modals in React.
My index.html looks like this:
<div id="root"></div>
<div id="modal"></div>
And all my modals are rendered (through a portal) as a child of .modal.
Each modal element has the following form:
<div class="modal-background open">
<!-- children -->
</div>
Where the class can be modal-background open or modal-background closed. The entire component is:
interface OwnProps {
children: React.ReactNode
isOpen: boolean
onExit: () => void
}
export class Modal extends React.Component<OwnProps, any> {
_exit = () => this.props.onExit();
_renderModal = () => (
<div className={`modal-background ${this.props.isOpen ? "open" : "closed"}`} onClick={this._exit}>
{this.props.children}
</div>
);
render() {
if (this.props.isOpen) {
document.body.className += " no-scroll";
} else {
document.body.classList.remove("no-scroll");
}
let elem = document.querySelector("#modal");
if (elem == null) {
console.log("Could not render modal.");
return null;
}
return ReactDOM.createPortal(this._renderModal(), elem);
}
}
And the CSS looks like:
.modal-background {
/* Other styling - this a dark backdrop for a modal child */
background-color: rgba(0,0,0,0.2);
transition: opacity 150ms ease-out;
&.closed {
opacity: 0;
pointer-events: none;
}
&.open {
pointer-events: all;
opacity: 1;
&:hover {
cursor: pointer;
}
}
}
So my modal is used like <Modal><CustomModalElement/></Modal>.
When I load the page, my modal elements briefly flash, indicating that they are not hidden on load (but a split-second afterwards).
I can fix this by adding display: none and display: inherit into the css, but then I miss the nice transitions.
Is there a better way to do this?

Not sure you need to do anything else inside your index.html file except
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal"></div>
And for your Modal.js, you could try something along these lines:
import React from "react";
import ReactDOM from "react-dom";
const Modal = props => {
return ReactDOM.createPortal(
<div className="ui dimmer modals visible active">
<div className="ui standard modal visible active">
<div className="header">Delete Object</div>
<div className="content">
Are you sure you want to delete this?
</div>
<div className="actions">
<button className="ui primary button">Delete</button>
<button className="ui button">Cancel</button>
</div>
</div>
</div>,
document.querySelector("#modal")
);
};
export default Modal;
and then inside your other component where the user will execute the modal:
import React from "react";
import Modal from "../Modal"; // or wherever your Modal is in the file tree
const ObjectDelete = () => {
return (
<div>
ObjectDelete
<Modal />
</div>
);
};
export default ObjectDelete;
Keep in mind that the example of modal I offer here is not a reusable component.

Related

Hide nav button in react-material-ui-carousel

I've just implemented the react material ui carousel, and it was pretty straightforward, the only thing i didn't catch, is how to hide buttons and show them only on over.
I noticed the props navButtonsAlwaysVisible and set it to false but it isn't enough.
Should i implement my own logic for that, or maybe I'm just missing something?
here's the component code:
import styles from '../../styles/Testimonial.module.scss'
import Image from 'next/image'
import Carousel from 'react-material-ui-carousel'
const Testimonial = _ => {
const items = [
{
imageUrl: "/png/image0.webp",
feedback: "feedback0",
name: "name0",
location: "location0"
},
{
imageUrl: "/png/image1.jpeg",
feedback: "feedback1",
name: "name1",
location: "location1"
}
]
return (
<div id="customers" className={`section ${styles.testimonial}`}>
<h2 className={`title ${styles.title}`}>Clientes Felizes</h2>
<span className={"separator"}> </span>
<Carousel
className={styles.carousel}
autoPlay={true}
stopAutoPlayOnHover={true}
interval={5000}
animation={"slide"}
swipe={true}
navButtonsAlwaysVisible={false}
navButtonsProps={{
style: {
backgroundColor: "#8f34eb",
opacity: 0.4
}
}}
>
{
items.map( (item, i) => <Item key={i} item={item} /> )
}
</Carousel>
</div>
)
}
function Item(props)
{
return (
<article className={styles.testimonial__card}>
<div className={styles.testimonial__photo_container}>
<Image
className={styles.testimonial__photo}
src={props.item.imageUrl}
alt="Testimonial"
width={312}
height={300}
/>
</div>
<p className={styles.testimonial__copy}>{props.item.feedback}</p>
<span className={styles.testimonial__name}>{props.item.name}</span>
<span className={styles.testimonial__city}>{props.item.location}</span>
</article>
)
}
export default Testimonial;
there's a prop called navButtonsAlwaysInvisible
navButtonsAlwaysInvisible={true}
You can try using Custom CSS for your purpose. Based on the current rendered markup,
.jss6 {
opacity: 0;
transition: all ease 1000ms; /* So that it does not disappear quickly */
}
You can define the hover for the parent so that it displays only when the parent container is hovered on:
.jss1.Testimonial_carousel__3rny3:hover .jss6 {
opacity: 1;
}
This is how it works now:

Print MaterialUI Dialog fullscreen

I am new to React and Material-UI and I want to print my current dialog.
The problem is that I cannot find a way to maximize my Dialog for priting (set to fullScreen) without doing it in the Browser, too. So I basically want a smaller Dialog in my Browser and for the Dialog the maximal size.
Here is my basic code in TSX:
import React, { Component} from 'react';
import { Button, Dialog } from '#material/core';
export default class MUITester extends Component {
render(){
return (
<Dialog fullScreen={false}>
<div>
<Button onClick={() => window.print()}>
PRINT
</Button>
</div>
</Dialog>
);
}
And the corresponding css file:
#media print {
.print {
fullScreen=true;
color: blue;
}
}
Can I solve it using css? Or do I have to use React/Material-UI?
I solved it! Change the classes of Dialog:
<Dialog classes={{paperFullScreen: "prePrint printDialog"}} fullScreen>
Here my css:
.prePrint {
height: auto !important;
max-width: 600px !important;
}
/*Print Dialog*/
#media print {
.printDialog {
max-width: 100% !important;
}
}
You can set the width of your dialog like this:
<Dialog fullWidth={true} maxWidth='md'>
<div>
<Button onClick={() => window.print()}>
PRINT
</Button>
</div>
</Dialog>
As given in the Documentation
For printing div, which is inside dialog, use below code, and add css also
import React, { Component} from 'react';
import { Button, Dialog } from '#material/core';
export default class MUITester extends Component {
render(){
return (
<Dialog classes={{paperFullScreen: "prePrint"}} fullScreen>
<div id="DialogPrint">
some text some text , some paragraph and so on
</div>
<div >
<Button onClick={() => window.print()}>
PRINT
</Button>
</div>
</Dialog>
);
}
}
add below code in css
.prePrint {
height: auto !important;
max-width: 600px !important;
}
/*Print Dialog*/
#media print {
body * {
visibility: hidden;
}
#DialogPrint,
#DialogPrint * {
visibility: visible;
}
#DialogPrint {
position: absolute;
left: 0;
top: 0;
}
}
I was looking for a full-screen Dialog on mobile and a simple dialog for desktop and the below example resolved my issue, please take a look if helps.
import useMediaQuery from '#mui/material/useMediaQuery';
function MyComponent() {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));
return <Dialog fullScreen={fullScreen} />;
}
You can check demo on MUI official documentation.
You just need to add fullScreen flag to modal component in order to achieve full screen.
Like below
<Dialog fullScreen open={open} onClose={handleClose} TransitionComponent={Transition}>
And if you don't want to use fullScreen, simply remove that fullScreen flag and don't need to use CSS here.

Vue lazyloading, remove spinner when image loads

I have a vue Nuxt project where I explore lazyloading with lazysizes package.
I created a spinner component (html css only) who should be visible only while the image is loading.
I also created an ImageItem component who includes the spinner component and it looks like this:
< script >
import spinner from '~/components/spinner.vue'
export default {
components: {
spinner,
},
props: {
source: {
type: String,
required: true,
}
},
} <
/script>
<style lang="scss" scoped>.imageItem {
position: relative;
.image {
z-index: 2;
&.lazyload,
&.lazyloading {
opacity: 0;
}
&.lazyloaded {
opacity: 1;
transition: all 1s linear 0.35s;
}
}
}
</style>
<template>
<div class="imageItem">
<spinner />
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
To explain my code, I have props: source where In parent component i pass the image i want to lazyload. Also in the CSS while the image is loading, the image has .lazyloading class and when is loaded .lazyloaded class. Right now when Image is loaded i put it on top of the spinner.
My problem is, when I load the image I want to hide or destroy the spinner element since I think just putting the image on top is not the best way to do it. Can someone give me direction how should I properly hide the spinner when the image is loaded ?
Lazysizes fires an event when loading the image is finished : lazyloaded event, So you can do this :
<template>
<div class="imageItem">
<spinner v-if="lazyloading"/>
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
<script>
import spinner from '~/components/spinner.vue'
export default {
data(){
return {
lazyloading
}
},
mounted(){
document.addEventListener('lazyloaded', (e) => {
this.lazyloading = false;
}
});
}
}
</script>

react wait for another component to finish rendering

I have the following situtation:
2 components A and B.
A is above B in the page layout.
I wish to calculate B height css rule dynamically using it's top.
B's top is affected by A because A is rendered above it. I attached a reference to B and then apply it style by calculating the bounding rect of the ref and calculating the desired height from the top.A problem occurs when component A is taking more time to fully render than component B because A loads images that takes a bit more time to load making B's top to shift a after it was already rendered so the calculating of the height in the function needs to be called again somehow after A was fully rendered but I don't wanna make B coupled to A just because it renders before it, what can I do to solve this issue?
You can solve it using Css flexbox, check code snippet bellow.
(function () {
var loadImgBtn = document.getElementById('load-image-btn');
var loadedImg = document.getElementById('loaded-image');
loadImgBtn.addEventListener('click', function () {
loadedImg.style.display = 'block';
});
})();
.flexbox {
display: flex;
flex-direction: column;
align-items: stretch;
min-height: 350px;
}
#loaded-image {
display: none;
}
#first-block {
background: green;
}
#second-block {
background: red;
flex-grow: 1;
}
<div class="flexbox">
<div id="first-block"> Component A
<img id="loaded-image"
src="https://images.pexels.com/photos/248797/pexels-photo-248797.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500">
</div>
<div id="second-block">Component B</div>
</div>
<button id="load-image-btn">Load Image</button>
If you want to calculate it programmatically you can use ref or forwardRef if you need to pass ref further down something like this
const ComponentA = React.forwardRef((props, ref) => (
<div ref={ref} className="componentA">
{props.children}
</div>
));
const ComponentB = React.forwardRef((props, ref) => (
<div ref={ref} className="componentB">
{props.children}
</div>
));
class App extends React.Component{
constructor(props){
super(props);
this.refA = React.createRef();
this.refB = React.createRef();
}
componentDidMount(){
console.log(this.refA.current.clientHeight,this.refB.current.clientHeight);
}
render(){
return (
<div className="App">
<ComponentA calculatedHeight={componentAHeight} ref={this.refA} >Component A</ComponentA>
<ComponentB calculatedHeight={componentBHeight} ref={this.refB} >Component B</ComponentB>
</div>
);
}
}
You can do it using findDOMNode too (it is depricated in strict mode). react-sizeme might be helpfull as well.

Add a State property to an Inline Style in React

I have a react element that has an inline style like this: (Shortened version)
<div className='progress-bar'
role='progressbar'
style={{width: '30%'}}>
</div>
I want to replace the width with a property from my state, although I'm not quite sure how to do it.
I tried:
<div className='progress-bar'
role='progressbar'
style={{{width: this.state.percentage}}}>
</div>
Is this even possible?
You can do it like this
style={ { width: `${ this.state.percentage }%` } }
Example
yes its possible check below
class App extends React.Component {
constructor(props){
super(props)
this.state = {
width:30; //default
};
}
render(){
//when state changes the width changes
const style = {
width: this.state.width
}
return(
<div>
//when button is clicked the style value of width increases
<button onClick={() => this.setState({width + 1})}></button>
<div className='progress-bar'
role='progressbar'
style={style}>
</div>
</div>
);
}
:-)

Resources