I have the following modal component:
export default function LoginModal(props) {
const { showLogin, hideLogin } = props
if (!showLogin) return null
return (
<div class='overlay'>
<div class='modal'>
<article class='mw5 center bg-white br3 pa3 pa4-ns mv3 ba b--black-10'>
<div class='tc'>
<h1 class='f4'>Firstname Lastname</h1>
<hr class='mw3 bb bw1 b--black-10' />
</div>
<p class='lh-copy measure center f6 black-70'>
test test test test
</p>
</article>
</div>
</div>
)
}
which I am attempting to conditionally render using a state property stored in my redux store. However, when I place it as follows:
<article class='pv6 center ph3 ph5-ns tc br2 bg-washed-green dark-green mb5'>
PAGE CONTENT HERE
</article>
<LoginModal />
It appears below the rest of the screen content rather than above everything else as I had hoped. I am using the following css to try to get this effect, but it doesn't seem to be working:
.overlay {
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: 'rgba(0,0,0,0.3)';
padding: 50;
z-index: 300;
}
.modal {
background-color: '#fff';
border-radius: 5;
max-width: 500;
min-height: 300;
margin: '0 auto';
padding: 30;
}
I need the modal to appear as a login card in the center of the screen with the background dimmed, as is common on many websites. I would also rather not use a UI component library to achieve. Thanks for the help!
I think you are missing a position: fixed in your .overlay styles.
Also, you might want to consider using Portals to render the modal outside the DOM hierarchy of your App, that is, outside the <div id="app"></div> or <div id="root"></div> we commonly use.
To do that, your index.html or equivalent file would have something like this:
<div id="app"></div>
<div id="modal"></div>
And then you need to update your LoginModal like this:
export default function LoginModal(props) {
const { showLogin, hideLogin } = props;
if (!showLogin) return null;
return ReactDOM.createPortal((
<div class='overlay'>
<div class='modal'>
<article class='mw5 center bg-white br3 pa3 pa4-ns mv3 ba b--black-10'>
<div class='tc'>
<h1 class='f4'>Firstname Lastname</h1>
<hr class='mw3 bb bw1 b--black-10' />
</div>
<p class='lh-copy measure center f6 black-70'>
test test test test
</p>
</article>
</div>
</div>
), document.getElementById('modal'));
}
Note that if you try to reuse the code in this example to create a generic Modal component, you would only be able to show one at a time, as they would be rendering inside the same #modal element.
In the Portals documentation you can see an example that creates a new element dynamically so that you can have multiple modals at the same time.
Related
In my react app I have a hidden banner that I want to show, when the length of the array reaches to 5. But it looks like that I am trying to get an element before it is rendered. I get the error about getting a style of undefined element.
This function must change css of the banner element and make it visible.
showBanner() {
let banner = document.getElementsByClassName('overlay')[0]
banner.style.cssText = "visibility: visible;opacity: 1;"
}
I want to render my popup component only if the condition is met.
render() {
if (this.props.awarded) {
if (this.props.awarded.length === 5) {
this.showBanner()
return (
<>
<h1 id="awardLabel">5 movies</h1>
<div id="movieList">
{this.props.awarded.map((movie) => {
return (
<div className="awardHolder" key={movie.imdbID}>
<div className="awardImgHolder" >
<img src={movie.Poster} alt={movie.Title}></img>
</div>
<div className="awardMovieInfo">
<p>{movie.Title}</p>
<p>year {movie.Year}</p>
</div>
<div className="withdrawButton" onClick={(e) => this.deleteMovie(e, movie)}> WITHDRAW </div>
</div>
)
})}
</div>
<Popup />
</>
)
} else { ...
This is my banner structure.
<div id="popup1" className="overlay">
<div className="popup">
<h2>Here i am</h2>
<a className="close" href="#">×</a>
<div className="content">
<p>Congratulations. You've nominated 5 movies.</p>
<button onClick={this.closeBanner}>Try again</button>
</div>
</div>
</div>
This is my css for the banner element.
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
}
How can I dynamically change element's styles using conditions to render that element?
You're trying to access your Popup component before it gets created. In other words, this.showBanner() is called before <Popup /> is rendered.
One solution is to move your popup to a higher-level component
This might be a good use case for React Context, which will allow you to have some global state that your components can tap into without having to pass the banner state through multiple components as props.
If you are going to do this, you might consider not manually updating the styling with querySelectors; instead, you can have React either render or not render the component based on your global banner state.
Your application will be wrapped in <BannerContext.Provider> tags, and then the component that needs to render or not render the banner can use <BannerContext.Consumer> tags to check the current banner state. You can also store a toggle function in the BannerContext so that other parts of the application (and the banner itself) can toggle the BannerContext as needed.
I'm trying to use the slot API in this example:
<tabs-s>
<tab-c>
<tab-c>
</tabs>
where tabs-s is the component that wraps other components.Inside it I'm using the tag to render its dom but if I want the assigned nodes I also get the whitespaces (text nodes).
Is there a manner to avoid getting the text nodes when calling assignedNodes() method? This was not happening in Polymer 1.x
Thanks
Let say you want to create a featured section to present new items
the section needs to have some basic information and change colors.
The element will take the title, count and class from his parent
<featured-section class="blue">
<span slot="count">3</span>
<h1 slot="title">The title of the element go here</h1>
</featured-section>
Inside the element featured-section
<dom-module id="featured-section">
<template>
<section>
<div class="vertical-section-container">
<div class="circle-container">
<div class="circle">
<slot name="count"></slot>
</div>
</div>
<slot name="title"></slot>
<feature-box></feature-box>
<feature-grid></feature-grid>
</div>
</section>
</template>
But who is in charge of the class detail? The element itself featured-section
<custom-style>
<style include="shared-styles">
:host {
display: block;
background-color: var(--my-section-color);
}
:host(.blue) {
--my-section-color: #03A9F4;
}
:host(.green) {
--my-section-color: #8BC34A;
}
:host(.pink) {
--my-section-color: #FF6EB6;
}
::slotted(h1) {
color: #fff;
padding-bottom: 1.5em;
line-height: 48px;
}
</style>
</custom-style>
I am new to programming so I apologise if my code presentation is not very good or my explanation not very clear. But, what I am trying to achieve is an auto-scroll feature as the content inside the <span> tag expands, so as you can see the function will print <br> and eventually, my <span> will require scrolling, when that happens, I would like to make it scroll automatically to the bottom of the <span> until the function finishes.
<pre><span class="inner-pre" id=code style="height:500px; display: block; overflow: auto; font-size: 16px"></span></pre>
<script>
function print()
{
for (i = 0; i < n; i++)
{
document.getElementById("code").innerHTML += "<br>";
} // for
} // end-function
</script>
I have looked at similar Stack Overflow questions, and I cannot find a solution to what I am trying to achieve. I have tried the following solutions:
document.getElementById('divID').scrollIntoView();
$(divname / .class / #id).focus();
div = document.getElementById('#your_div');
div.scrollTo(0,div.scrollHeight);
But neither worked for me, though, it may be that I might've implemented it wrong.
My HTML code:
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="panel panel-danger">
<div class="panel-heading"><h3 align="center">Pseudo Code</h3></div>
<div class="panel-body" style="height:600px;">
<pre><span class="inner-pre" id=code style="height:500px; display: block; overflow: auto; font-size: 16px">code</span></pre>
</div>
</div>
</div>
</div>
</div>
On ollynural.github.io, in the portfolio page i'm trying to simulate a pop-up div giving more information on the project you clicked on. To go back off the pop-up, I've added an ng-click so when you click on the main portfolio-pop-up container, the pop-up is removed.
Is it possible to only have the parts of the portfolio-pop-up div that are exposed (not on the photo nor the description white box) removing the main div once clicked? So you can click freely on the picture and the white box
<div class="portfolio-pop-up container" ng-click="losePortfolioFocus()">
<div class="row">
<div class="col-xs-12">
<img class="portfolio-image portfolio-image-popup" src="{{portfolioImageClass}}">
</div>
<div class="col-xs-12 pop-up-container">
<div class="pop-up-row">
<div class="col-xs-9" style="background: red">
<h1>
{{portfolioTitle}}
</h1>
<p>
{{portfolioDescription}}
</p>
</div>
<div class="col-xs-3" style="background: cyan">
Click me
<div ng-repeat="tech in portfolioTech">
{{tech}}
</div>
</div>
</div>
</div>
</div>
</div>
JS
$scope.losePortfolioFocus= function() {
angular.element('.portfolio-pop-up').css("display", "none");
}
CSS
.portfolio-pop-up {
display: none;
position: absolute;
width: 100%;
height: 100%;
/* color with alpha transparency */
background-color: rgba(0, 0, 0, 0.70);
top: 0;
left: 0;
bottom: 0;
right: 0;
}
Any help would be appreciated, can post more css or code if needed
You can stop the propagation of the click event on the element that wraps the pop-up's content like this:
<div class="portfolio-pop-up container" ng-click="losePortfolioFocus()">
<div class="row" ng-click="$event.stopPropagation()">
...
</div>
</div>
This way the clicks inside the popup will not trigger the losePortfolioFocus() handler.
I suggest you crawl up the event chain from you event' target and check whether your pop-up-container is in there. This way, you'll have a way to distinguish click in the pop-up or out of it.
In a nutshell, what I want to achieve using the Angular-masonry directive (http://passy.github.io/angular-masonry/) is something like this: http://codepen.io/desandro/pen/htsui
I have tried a number of different methods, but the bigger div just spreads out over the top of the next div along.
This is the HTML:
<div class="row" ng-controller="DemoCtrl">
<div class="span12">
<div masonry >
<div class="masonry-brick" ng-repeat="brick in bricks">
<div ng-class="brick.cssClass" ng-click="changeBrick($index)">
<img ng-src="{{brick.src}}" alt="A masonry brick" >
<span>Here is a load of text to see what fits in here and such </span>
</div>
</div>
</div>
</div>
</div>
This is a snippet of the angular code:
app.controller('DemoCtrl', function ($scope, $timeout,$element) {
..
$scope.changeBrick= function changeBrick(i) {
$scope.bricks[i].cssClass = 'big';
$element.masonry();
$element.masonry('layout');
}
}
The styles are just this for testing:
.small{
width: 200px;
background-color: lime;
}
.big{
width: 400px;
background-color: red;
}
Ate the moment, the div just spreads out to right over the top of the next div along - I really want masonry to do its thing and rearrange the other divs.
Try broadcasting the masonry.reload message to get angular-masonry to refresh the layout. I ran into a semi-similar problem and this seems to work: https://stackoverflow.com/a/27967782/3191599
function refresh() {
common.$timeout(function () { $scope.$broadcast('masonry.reload'); }, 100);
}