mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-26 17:16:11 -05:00
Refactor modal components
1. Split out base modal stylings from iframe modal stylings 2. Move iframe-specific stylings into a separate copmonent 3. Move width/height iframe stylings to scss 4. Change global stylings for modals to match #980 5. Update styles to use trello style guide
This commit is contained in:
parent
fa1b6c6c00
commit
c9de1d73d9
18 changed files with 280 additions and 149 deletions
|
@ -72,7 +72,7 @@
|
|||
"react": "15.1.0",
|
||||
"react-dom": "15.0.1",
|
||||
"react-intl": "2.1.2",
|
||||
"react-modal": "1.3.0",
|
||||
"react-modal": "1.5.2",
|
||||
"react-onclickoutside": "4.1.1",
|
||||
"react-redux": "4.4.5",
|
||||
"react-responsive": "1.1.4",
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
var connect = require('react-redux').connect;
|
||||
var omit = require('lodash.omit');
|
||||
var React = require('react');
|
||||
|
||||
var sessionActions = require('../../redux/session.js');
|
||||
|
||||
var Modal = require('../modal/modal.jsx');
|
||||
var IframeModal = require('../modal/iframe/modal.jsx');
|
||||
var Registration = require('../registration/registration.jsx');
|
||||
|
||||
require('./intro.scss');
|
||||
|
||||
Modal.setAppElement(document.getElementById('view'));
|
||||
|
||||
var Intro = React.createClass({
|
||||
type: 'Intro',
|
||||
getDefaultProps: function () {
|
||||
|
@ -52,11 +49,6 @@ var Intro = React.createClass({
|
|||
this.closeRegistration();
|
||||
},
|
||||
render: function () {
|
||||
var frameProps = {
|
||||
width: 570,
|
||||
height: 357,
|
||||
padding: 15
|
||||
};
|
||||
return (
|
||||
<div className="intro">
|
||||
<div className="content">
|
||||
|
@ -130,15 +122,12 @@ var Intro = React.createClass({
|
|||
<img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
|
||||
alt="Intro Video" />
|
||||
</div>
|
||||
<Modal
|
||||
className="video-modal"
|
||||
isOpen={this.state.videoOpen}
|
||||
onRequestClose={this.closeVideo}
|
||||
style={{content:frameProps}}>
|
||||
<iframe
|
||||
src="//player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
|
||||
{...omit(frameProps, 'padding')} />
|
||||
</Modal>
|
||||
<IframeModal
|
||||
className="mod-intro-video"
|
||||
isOpen={this.state.videoOpen}
|
||||
onRequestClose={this.closeVideo}
|
||||
src="//player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -241,3 +241,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content.mod-intro-video {
|
||||
padding: 15px;
|
||||
width: 35.625rem;
|
||||
}
|
||||
|
||||
.modal-content-iframe.mod-intro-video {
|
||||
width: 35.625rem;
|
||||
min-height: 22.3125rem;
|
||||
}
|
||||
|
|
58
src/components/modal/base/modal.jsx
Normal file
58
src/components/modal/base/modal.jsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
var classNames = require('classnames');
|
||||
var omit = require('lodash.omit');
|
||||
var React = require('react');
|
||||
var ReactModal = require('react-modal');
|
||||
|
||||
require('./modal.scss');
|
||||
|
||||
/**
|
||||
* Container for pop up windows (See: registration window)
|
||||
*/
|
||||
var Modal = React.createClass({
|
||||
type: 'Modal',
|
||||
statics: {
|
||||
setAppElement: ReactModal.setAppElement
|
||||
},
|
||||
propTypes: {
|
||||
className: React.PropTypes.string,
|
||||
overlayClassName: React.PropTypes.string
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
className: '',
|
||||
overlayClassName: ''
|
||||
};
|
||||
},
|
||||
requestClose: function () {
|
||||
return this.refs.modal.portal.requestClose();
|
||||
},
|
||||
render: function () {
|
||||
var modalClasses = classNames(
|
||||
'modal-content',
|
||||
this.props.className
|
||||
);
|
||||
var overlayClasses = classNames(
|
||||
'modal-overlay',
|
||||
this.props.overlayClassName
|
||||
);
|
||||
return (
|
||||
<ReactModal
|
||||
ref="modal"
|
||||
className={modalClasses}
|
||||
overlayClassName={overlayClasses}
|
||||
{...omit(this.props, ['className', 'overlayClassName'])}
|
||||
>
|
||||
<div className="modal-content-close" onClick={this.requestClose}>
|
||||
<img
|
||||
className="modal-content-close-img"
|
||||
src="/svgs/modal/close-x.svg"
|
||||
alt="close-icon"
|
||||
/>
|
||||
</div>
|
||||
{this.props.children}
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Modal;
|
61
src/components/modal/base/modal.scss
Normal file
61
src/components/modal/base/modal.scss
Normal file
|
@ -0,0 +1,61 @@
|
|||
@import "../../../colors";
|
||||
@import "../../../frameless";
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
margin: 3.75rem auto;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0 0 1px $active-gray;
|
||||
background-color: $ui-white;
|
||||
padding: 0;
|
||||
width: 48.75rem;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
background-color: transparentize($ui-blue, .3);
|
||||
}
|
||||
|
||||
.ReactModal__Content:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
$modal-close-size: 1rem;
|
||||
.modal-content-close {
|
||||
position: absolute;
|
||||
top: $modal-close-size / 2;
|
||||
right: $modal-close-size / 2;
|
||||
border-radius: $modal-close-size;
|
||||
background-color: $active-dark-gray;
|
||||
cursor: pointer;
|
||||
width: $modal-close-size * 2;
|
||||
height: $modal-close-size * 2;
|
||||
text-align: center;
|
||||
line-height: $modal-close-size * 2;
|
||||
}
|
||||
|
||||
.modal-content-close-img {
|
||||
padding-top: $modal-close-size / 2;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $desktop - 1) {
|
||||
.modal-content {
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin-top: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.modal-content-close {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
47
src/components/modal/iframe/modal.jsx
Normal file
47
src/components/modal/iframe/modal.jsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
var classNames = require('classnames');
|
||||
var React = require('react');
|
||||
|
||||
var Modal = require('../base/modal.jsx');
|
||||
|
||||
require('./modal.scss');
|
||||
|
||||
Modal.setAppElement(document.getElementById('view'));
|
||||
|
||||
var IframeModal = React.createClass({
|
||||
propTypes: {
|
||||
componentKey: React.PropTypes.string,
|
||||
isOpen: React.PropTypes.bool,
|
||||
onRequestClose: React.PropTypes.func,
|
||||
className: React.PropTypes.string,
|
||||
componentRef: React.PropTypes.string,
|
||||
src: React.PropTypes.string
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
className: '',
|
||||
iframeClassName: ''
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
var iframeClasses = classNames(
|
||||
'modal-content-iframe',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
key={this.props.componentKey}
|
||||
isOpen={this.props.isOpen}
|
||||
onRequestClose={this.props.onRequestClose}
|
||||
className={this.props.className}
|
||||
>
|
||||
<iframe
|
||||
ref={this.props.componentRef}
|
||||
src={this.props.src}
|
||||
className={iframeClasses}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = IframeModal;
|
5
src/components/modal/iframe/modal.scss
Normal file
5
src/components/modal/iframe/modal.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
@import "../../../frameless";
|
||||
|
||||
.modal-content-iframe {
|
||||
border: 0;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
var clone = require('lodash.clone');
|
||||
var defaultsDeep = require('lodash.defaultsdeep');
|
||||
var React = require('react');
|
||||
var ReactModal = require('react-modal');
|
||||
|
||||
require('./modal.scss');
|
||||
|
||||
var defaultStyle = {
|
||||
overlay: {
|
||||
zIndex: 100,
|
||||
backgroundColor: 'rgba(0, 0, 0, .75)'
|
||||
},
|
||||
content: {
|
||||
position: 'absolute',
|
||||
overflow: 'visible',
|
||||
borderRadius: '6px',
|
||||
width: 500,
|
||||
height: 250,
|
||||
padding: 0,
|
||||
top: '50%',
|
||||
right: 'auto',
|
||||
bottom: 'auto',
|
||||
left: '50%',
|
||||
marginTop: -125,
|
||||
marginLeft: -250
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for pop up windows (See: registration window)
|
||||
*/
|
||||
var Modal = React.createClass({
|
||||
type: 'Modal',
|
||||
statics: {
|
||||
setAppElement: ReactModal.setAppElement
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
style: defaultStyle
|
||||
};
|
||||
},
|
||||
calculateStyle: function () {
|
||||
var style = clone(this.props.style, true);
|
||||
defaultsDeep(style, defaultStyle);
|
||||
style.content.marginTop = (style.content.height + style.content.padding*2) / -2;
|
||||
style.content.marginLeft = (style.content.width + style.content.padding*2) / -2;
|
||||
return style;
|
||||
},
|
||||
requestClose: function () {
|
||||
return this.refs.modal.portal.requestClose();
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<ReactModal ref="modal"
|
||||
{...this.props}
|
||||
style={this.calculateStyle()}>
|
||||
<div className="modal-close" onClick={this.requestClose}></div>
|
||||
{this.props.children}
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Modal;
|
|
@ -1,34 +0,0 @@
|
|||
@import "../../colors";
|
||||
|
||||
.ReactModal__Content {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
$modal-close-size: 20px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin-top: -$modal-close-size / 2;
|
||||
margin-right: -$modal-close-size / 2;
|
||||
border: 2px solid $ui-border;
|
||||
border-radius: $modal-close-size / 2;
|
||||
background-color: $active-dark-gray;
|
||||
cursor: pointer;
|
||||
width: $modal-close-size;
|
||||
height: $modal-close-size;
|
||||
text-align: center;
|
||||
line-height: $modal-close-size;
|
||||
color: $type-white;
|
||||
font-size: $modal-close-size;
|
||||
|
||||
&:before {
|
||||
content: "x";
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
var React = require('react');
|
||||
|
||||
var NavigationBox = require('../container/navigation.jsx');
|
||||
var NavigationBox = require('../base/navigation.jsx');
|
||||
|
||||
require('./navigation.scss');
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ var Form = require('../../forms/form.jsx');
|
|||
var Input = require('../../forms/input.jsx');
|
||||
var log = require('../../../lib/log.js');
|
||||
var Login = require('../../login/login.jsx');
|
||||
var Modal = require('../../modal/modal.jsx');
|
||||
var NavigationBox = require('../container/navigation.jsx');
|
||||
var Modal = require('../../modal/base/modal.jsx');
|
||||
var NavigationBox = require('../base/navigation.jsx');
|
||||
var Registration = require('../../registration/registration.jsx');
|
||||
|
||||
require('./navigation.scss');
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
var React = require('react');
|
||||
var Modal = require('../modal/modal.jsx');
|
||||
var IframeModal = require('../modal/iframe/modal.jsx');
|
||||
|
||||
require('./registration.scss');
|
||||
|
||||
Modal.setAppElement(document.getElementById('view'));
|
||||
|
||||
var Registration = React.createClass({
|
||||
propTypes: {
|
||||
isOpen: React.PropTypes.bool,
|
||||
|
@ -36,18 +34,14 @@ var Registration = React.createClass({
|
|||
this.toggleMessageListener(false);
|
||||
},
|
||||
render: function () {
|
||||
var frameProps = {
|
||||
width: 610,
|
||||
height: 438
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
isOpen={this.props.isOpen}
|
||||
onRequestClose={this.props.onRequestClose}
|
||||
className="registration"
|
||||
style={{content:frameProps}}>
|
||||
<iframe ref="registrationIframe" src="/accounts/standalone-registration/" {...frameProps} />
|
||||
</Modal>
|
||||
<IframeModal
|
||||
isOpen={this.props.isOpen}
|
||||
onRequestClose={this.props.onRequestClose}
|
||||
className="mod-registration"
|
||||
componentRef="registrationIframe"
|
||||
src="/accounts/standalone-registration/"
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
.registration {
|
||||
@import "../../frameless";
|
||||
|
||||
.modal-content.mod-registration {
|
||||
width: 38.125rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-content-iframe.mod-registration {
|
||||
width: 38.125rem;
|
||||
min-height: 27.375rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
.modal-content.mod-registration {
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.modal-content-iframe.mod-registration {
|
||||
height: 27.375rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
var connect = require('react-redux').connect;
|
||||
var injectIntl = require('react-intl').injectIntl;
|
||||
var omit = require('lodash.omit');
|
||||
var React = require('react');
|
||||
|
||||
var api = require('../../lib/api');
|
||||
|
@ -16,7 +15,7 @@ var Box = require('../../components/box/box.jsx');
|
|||
var Button = require('../../components/forms/button.jsx');
|
||||
var Carousel = require('../../components/carousel/carousel.jsx');
|
||||
var Intro = require('../../components/intro/intro.jsx');
|
||||
var Modal = require('../../components/modal/modal.jsx');
|
||||
var IframeModal = require('../../components/modal/iframe/modal.jsx');
|
||||
var News = require('../../components/news/news.jsx');
|
||||
var Page = require('../../components/page/www/page.jsx');
|
||||
var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
|
||||
|
@ -308,7 +307,6 @@ var Splash = injectIntl(React.createClass({
|
|||
},
|
||||
render: function () {
|
||||
var featured = this.renderHomepageRows();
|
||||
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
|
||||
var homepageCacheState = this.getHomepageRefreshStatus();
|
||||
|
||||
var formatHTMLMessage = this.props.intl.formatHTMLMessage;
|
||||
|
@ -344,21 +342,23 @@ var Splash = injectIntl(React.createClass({
|
|||
return (
|
||||
<div className="splash">
|
||||
{this.shouldShowEmailConfirmation() ? [
|
||||
<DropdownBanner key="confirmedEmail"
|
||||
className="warning"
|
||||
onRequestDismiss={this.handleDismiss.bind(this, 'confirmed_email')}>
|
||||
<DropdownBanner
|
||||
key="confirmedEmail"
|
||||
className="warning"
|
||||
onRequestDismiss={this.handleDismiss.bind(this, 'confirmed_email')}
|
||||
>
|
||||
<a href="#" onClick={this.showEmailConfirmationModal}>Confirm your email</a>
|
||||
{' '}to enable sharing.{' '}
|
||||
<a href="/info/faq/#accounts">Having trouble?</a>
|
||||
</DropdownBanner>,
|
||||
<Modal key="emailConfirmationModal"
|
||||
isOpen={this.state.emailConfirmationModalOpen}
|
||||
onRequestClose={this.hideEmailConfirmationModal}
|
||||
style={{content: emailConfirmationStyle}}>
|
||||
<iframe ref="emailConfirmationiFrame"
|
||||
src="/accounts/email_resend_standalone/"
|
||||
{...omit(emailConfirmationStyle, 'padding')} />
|
||||
</Modal>
|
||||
<IframeModal
|
||||
componentKey="emailConfirmationModal"
|
||||
isOpen={this.state.emailConfirmationModalOpen}
|
||||
onRequestClose={this.hideEmailConfirmationModal}
|
||||
className="mod-confirmation"
|
||||
componentRef="emailConfirmationiFrame"
|
||||
src="/accounts/email_resend_standalone/"
|
||||
/>
|
||||
] : []}
|
||||
{this.props.permissions.educator ? [
|
||||
<TeacherBanner key="teacherbanner" messages={messages} />
|
||||
|
|
|
@ -42,6 +42,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.modal-content.mod-confirmation {
|
||||
width: 31.25rem;
|
||||
}
|
||||
|
||||
.modal-content-iframe.mod-confirmation {
|
||||
border-radius: 1rem;
|
||||
width: 31.25rem;
|
||||
min-height: 20.625rem;
|
||||
}
|
||||
|
||||
//4 columns
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
.splash {
|
||||
|
@ -54,6 +64,15 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content.mod-confirmation {
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.modal-content-iframe.mod-confirmation {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
//6 columns
|
||||
|
@ -68,6 +87,16 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content.mod-confirmation {
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.modal-content-iframe.mod-confirmation {
|
||||
border-radius: 0;
|
||||
width: $tablet - 1;
|
||||
}
|
||||
}
|
||||
|
||||
//6 columns
|
||||
|
|
17
static/svgs/modal/close-x.svg
Normal file
17
static/svgs/modal/close-x.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M12.5,10.3c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4L8,10.1l-2.3,2.3
|
||||
c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1L5.9,8L3.5,5.7C3,5.1,3,4.1,3.5,3.5
|
||||
C4.1,3,5.1,3,5.7,3.5L8,5.9l2.3-2.3c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L10.1,8L12.5,10.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 801 B |
Loading…
Reference in a new issue