Merge branch 'modal'

This commit is contained in:
fiatjaf 2018-09-23 21:54:43 +00:00
commit 9eb1580afa
15 changed files with 191 additions and 171 deletions

45
formspree/js/Modal.js Normal file
View File

@ -0,0 +1,45 @@
/** @format */
const React = require('react')
const cs = require('class-set')
export default class Modal extends React.Component {
constructor(props) {
super(props)
this.close = this.close.bind(this)
}
render() {
return (
<>
<div
className={cs({'modal-overlay': true, open: this.props.opened})}
onClick={this.close}
/>
<div
className={cs({modal: true, react: true, target: this.props.opened})}
>
<div className="container">
{this.props.opened ? (
<>
<div className="x">
<h4>{this.props.title}</h4>
<a href="#" onClick={this.close}>
&times;
</a>
</div>
{this.props.children}
</>
) : null}
</div>
</div>
</>
)
}
close(e) {
e.preventDefault()
this.props.onClose(e)
}
}

View File

@ -3,7 +3,7 @@
const React = require('react')
const createPortal = require('react-dom').createPortal
module.exports = class Portal extends React.Component {
export default class Portal extends React.Component {
render() {
return createPortal(
this.props.children,

View File

@ -7,12 +7,14 @@ const React = require('react')
const toastr = window.toastr
const fetch = window.fetch
const modals = require('../modals')
import Modal from '../Modal'
module.exports = class CreateForm extends React.Component {
export default class CreateForm extends React.Component {
constructor(props) {
super(props)
this.openModal = this.openModal.bind(this)
this.closeModal = this.closeModal.bind(this)
this.setEmail = this.setEmail.bind(this)
this.setURL = this.setURL.bind(this)
this.setSitewide = this.setSitewide.bind(this)
@ -21,6 +23,8 @@ module.exports = class CreateForm extends React.Component {
this.checkSitewide = this.checkSitewide.bind(this)
this.state = {
modalOpened: false,
url: '',
email: '',
sitewide: false,
@ -31,10 +35,6 @@ module.exports = class CreateForm extends React.Component {
}
}
componentDidMount() {
modals()
}
render() {
let {
email,
@ -48,16 +48,16 @@ module.exports = class CreateForm extends React.Component {
return (
<div className="col-1-1">
<div className="create-form">
<a href="#create-form" className="button">
<a href="#" onClick={this.openModal} className="button">
Create a form
</a>
<div className="modal" id="create-form" aria-hidden="true">
<Modal
opened={this.state.modalOpened}
onClose={this.closeModal}
title="Create form"
>
<div className="container">
<div className="x">
<h4>Create form</h4>
<a href="#">&times;</a>
</div>
<form onSubmit={this.create}>
<div className="col-1-1">
<h4>Send email to:</h4>
@ -149,7 +149,7 @@ module.exports = class CreateForm extends React.Component {
</div>
</form>
</div>
</div>
</Modal>
</div>
</div>
)
@ -263,4 +263,14 @@ module.exports = class CreateForm extends React.Component {
toastr.error('Failed to create form, see the console for more details.')
}
}
openModal(e) {
e.preventDefault()
this.setState({modalOpened: true})
}
closeModal(e) {
e.preventDefault()
this.setState({modalOpened: false})
}
}

View File

@ -3,10 +3,10 @@
const React = require('react')
const {BrowserRouter: Router, Route} = require('react-router-dom')
const FormList = require('./FormList')
const FormPage = require('./FormPage')
import FormList from './FormList'
import FormPage from './FormPage'
module.exports = class Dashboard extends React.Component {
export default class Dashboard extends React.Component {
render() {
return (
<Router>

View File

@ -6,10 +6,10 @@ const React = require('react')
const cs = require('class-set')
const {Link} = require('react-router-dom')
const CreateForm = require('./CreateForm')
const Portal = require('../Portal')
import CreateForm from './CreateForm'
import Portal from '../Portal'
module.exports = class FormList extends React.Component {
export default class FormList extends React.Component {
constructor(props) {
super(props)

View File

@ -2,9 +2,7 @@
const React = require('react') // eslint-disable-line no-unused-vars
module.exports = FormDescription
function FormDescription({prefix, form}) {
export default function FormDescription({prefix, form}) {
return (
<h2 className="form-description">
{prefix}{' '}

View File

@ -3,12 +3,11 @@
const React = require('react')
const CodeMirror = require('react-codemirror2')
const cs = require('class-set')
const {Link} = require('react-router-dom')
require('codemirror/mode/xml/xml')
require('codemirror/mode/javascript/javascript')
const {Link} = require('react-router-dom')
module.exports = class FormIntegration extends React.Component {
export default class FormIntegration extends React.Component {
constructor(props) {
super(props)

View File

@ -4,9 +4,9 @@ const toastr = window.toastr
const fetch = window.fetch
const React = require('react')
const SettingsSwitch = require('./SettingsSwitch')
import SettingsSwitch from './SettingsSwitch'
module.exports = class FormSettings extends React.Component {
export default class FormSettings extends React.Component {
constructor(props) {
super(props)
@ -27,18 +27,16 @@ module.exports = class FormSettings extends React.Component {
return (
<>
<div className="container" id="settings">
<SettingsSwitch
title="Form Enabled"
<SettingsSwitch
title="Form Enabled"
fieldName="disabled"
description="You can disable this form to cause it to stop receiving new
submissions temporarily or permanently."
onChangeFn={() => this.update}
checkedFn={() =>
'disabled' in tmp
? !tmp.disabled
: !form.disabled
'disabled' in tmp ? !tmp.disabled : !form.disabled
}
></SettingsSwitch>
/>
<SettingsSwitch
title="reCAPTCHA"
fieldName="captcha_disabled"
@ -50,7 +48,7 @@ module.exports = class FormSettings extends React.Component {
? !tmp.captcha_disabled
: !form.captcha_disabled
}
></SettingsSwitch>
/>
<SettingsSwitch
title="Email Notifications"
fieldName="disable_email"
@ -58,11 +56,9 @@ module.exports = class FormSettings extends React.Component {
download the submissions from the dashboard."
onChangeFn={() => this.update}
checkedFn={() =>
'disable_email' in tmp
? !tmp.disable_email
: !form.disable_email
'disable_email' in tmp ? !tmp.disable_email : !form.disable_email
}
></SettingsSwitch>
/>
<SettingsSwitch
title="Submission Archive"
fieldName="disable_storage"
@ -74,8 +70,8 @@ module.exports = class FormSettings extends React.Component {
? !tmp.disable_storage
: !form.disable_storage
}
></SettingsSwitch>
/>
<div className="row">
<div className={this.state.deleting ? 'col-1-2' : 'col-5-6'}>
<h4>
@ -92,8 +88,8 @@ module.exports = class FormSettings extends React.Component {
</span>
) : (
<span>
Deleting the form will erase all traces of this form on
our databases, including all the submissions.
Deleting the form will erase all traces of this form on our
databases, including all the submissions.
</span>
)}
</p>

View File

@ -2,34 +2,36 @@
const React = require('react') // eslint-disable-line no-unused-vars
module.exports = SettingsSwitch
function SettingsSwitch({title, fieldName, description, checkedFn, onChangeFn}) {
export default function SettingsSwitch({
title,
fieldName,
description,
checkedFn,
onChangeFn
}) {
return (
<>
<div className="row">
<div className="col-1-1">
<h4>{title}</h4>
<div className="row">
<div className="col-1-1">
<h4>{title}</h4>
</div>
<div className="switch-row">
<label className="switch">
<input
type="checkbox"
onChange={onChangeFn()}
checked={checkedFn()}
name={fieldName}
/>
<span className="slider" />
</label>
</div>
</div>
<div className="switch-row">
<label className="switch">
<input
type="checkbox"
onChange={onChangeFn()}
checked={checkedFn()}
name={fieldName}
/>
<span className="slider" />
</label>
<div className="row">
<div className="col-1-1">
<p className="description">{description}</p>
</div>
</div>
</div>
<div className="row">
<div className="col-1-1">
<p className="description">
{description}
</p>
</div>
</div>
</>
)
}

View File

@ -4,7 +4,7 @@ const toastr = window.toastr
const fetch = window.fetch
const React = require('react')
module.exports = class FormSubmissions extends React.Component {
export default class FormSubmissions extends React.Component {
constructor(props) {
super(props)

View File

@ -6,15 +6,16 @@ const cs = require('class-set')
const qs = require('query-string')
const React = require('react')
const CodeMirror = require('react-codemirror2')
const Modal = require('react-modal')
require('codemirror/mode/xml/xml')
require('codemirror/mode/css/css')
import Modal from '../../Modal'
const MODAL_REVERT = 'revert'
const MODAL_PREVIEW = 'preview'
const MODAL_SYNTAX = 'syntax'
module.exports = class FormSettings extends React.Component {
export default class FormSettings extends React.Component {
constructor(props) {
super(props)
@ -186,14 +187,12 @@ module.exports = class FormSettings extends React.Component {
</div>
</div>
<Modal
contentLabel="Revert changes"
isOpen={this.state.modal === MODAL_REVERT}
onRequestClose={this.closeModal}
className="dummy"
overlayClassName="dummy"
title="Revert changes"
opened={this.state.modal === MODAL_REVERT}
onClose={this.closeModal}
>
<div>
<div className="container">
<div>
<h2>Are you sure?</h2>
<p>
Reverting will discard the changes you've made to your email
@ -207,11 +206,9 @@ module.exports = class FormSettings extends React.Component {
</div>
</Modal>
<Modal
contentLabel="Preview"
isOpen={this.state.modal === MODAL_PREVIEW}
onRequestClose={this.closeModal}
className="dummy"
overlayClassName="dummy"
title="Preview"
opened={this.state.modal === MODAL_PREVIEW}
onClose={this.closeModal}
>
<div id="whitelabel-preview-modal">
<iframe
@ -232,22 +229,22 @@ module.exports = class FormSettings extends React.Component {
</div>
</Modal>
<Modal
contentLabel="Email Syntax"
isOpen={this.state.modal === MODAL_SYNTAX}
onRequestClose={this.closeModal}
className="dummy"
overlayClassName="dummy"
title="Email Syntax"
opened={this.state.modal === MODAL_SYNTAX}
onClose={this.closeModal}
>
<div>
<div>
<h2>Email Syntax</h2>
<p>
the email body can contain simple HTML that's valid in an email.
No <span className="code">&lt;script&gt;</span> or{' '}
<span className="code">&lt;style&gt;</span> tags can be
<span className="code">&lt;style&gt;</span> tags can be{' '}
included. For a list of recommended HTML tags see{' '}
<a href="" target="_blank">
the ContantContact guide to HTML in email
<a
href="https://explore.reallygoodemails.com/new-to-email-coding-heres-where-to-start-2494422f0bd4"
target="_blank"
>
this guide to HTML in email
</a>
.
</p>
@ -273,9 +270,9 @@ module.exports = class FormSettings extends React.Component {
{{/ _fields }} Closes the _fields block.
`.trim()}
</pre>
</div>
<div className="container right">
<button onClick={this.closeModal}>OK</button>
<div className="container right">
<button onClick={this.closeModal}>OK</button>
</div>
</div>
</div>
</Modal>

View File

@ -5,14 +5,14 @@ const fetch = window.fetch
const React = require('react')
const {Route, Link, NavLink, Redirect} = require('react-router-dom')
const Portal = require('../../Portal')
const Integration = require('./Integration')
const Submissions = require('./Submissions')
const Settings = require('./Settings')
const Whitelabel = require('./Whitelabel')
const FormDescription = require('./FormDescription')
import Portal from '../../Portal'
import Integration from './Integration'
import Submissions from './Submissions'
import Settings from './Settings'
import Whitelabel from './Whitelabel'
import FormDescription from './FormDescription'
module.exports = class FormPage extends React.Component {
export default class FormPage extends React.Component {
constructor(props) {
super(props)
@ -37,9 +37,9 @@ module.exports = class FormPage extends React.Component {
</Portal>
<Portal to="#header .center">
<h1>Form Details</h1>
{this.state.form &&
{this.state.form && (
<FormDescription prefix="For " form={this.state.form} />
}
)}
</Portal>
<Route
exact

View File

@ -2,13 +2,11 @@
const render = require('react-dom').render
const React = require('react') // eslint-disable-line no-unused-vars
const Modal = require('react-modal')
const Dashboard = require('./Dashboard')
import Dashboard from './Dashboard'
if (document.querySelector('body.forms.dashboard')) {
let el = document.querySelector('.container.block')
Modal.setAppElement(el)
document.querySelector('.menu .item:nth-child(2)').innerHTML = ''
render(<Dashboard />, el)

View File

@ -29,8 +29,11 @@
margin: 0 !important;
}
.invoice {
button + button {
margin-left: 11px;
}
.invoice {
.middle > * {
vertical-align: middle;
}
@ -340,9 +343,13 @@
.create-form {
margin: 3em 0;
form {
clear: both;
h4 {
margin: 0;
padding: 0;
margin-top: 13px;
float: left;
}
form input:not([type="checkbox"]):not([type="radio"]) {
margin-top: 0;
clear: both;
@ -360,18 +367,11 @@
text-align: right;
}
.modal:not(.js):target > *,
.modal.target > * {
top: 20%;
}
}
.modal[id^="form-"] {
textarea {
font-size: 15px;
}
}
.modal {
& > * {
background: #fefefe;
@ -387,16 +387,19 @@
transition: transform 0.5s ease-out;
padding: 0.3em 1.3em 1.3em 1.3em;
.x a {
float: right;
padding: 10px;
font-size: 1.7em;
}
h4 {
margin: 0;
padding: 0;
margin-top: 13px;
float: left;
.x {
a {
float: right;
padding: 10px;
font-size: 1.7em;
}
h4 {
margin: 0;
padding: 0;
margin-top: 13px;
float: left;
}
& + * { clear: both; }
}
}
@ -458,7 +461,6 @@
color: #2A735B;
}
}
&:not(.js):target > *,
&.target > * {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
@ -466,27 +468,29 @@
top: 10%;
}
&:before {
content: "";
display: none;
background: rgba(0, 0, 0, 0.6);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
}
&:not(.js):target:before,
&.target:before {
display: block;
}
.red {
color: $red;
}
}
.modal:not(.react):before,
.modal-overlay {
content: "";
display: none;
background: rgba(0, 0, 0, 0.6);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
}
.modal.target:not(.react):before,
.modal-overlay.open {
display: block;
}
/* icon morphing */
.fa-unlock:hover::before {
content: "\f023";
@ -534,34 +538,6 @@
}
}
.ReactModal__Overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.5);
display: flex;
justify-content: center;
.ReactModal__Content {
z-index: 11;
position: absolute;
top: 80px;
background: #fefefe;
border: #333333 solid 1px;
border-radius: 5px;
padding: 1.4em 1.4em 0.5em 1.4em;
max-width: 900px;
transform: translate(0, -500%);
transition: transform 0.6s ease-out;
}
&.ReactModal__Overlay--after-open .ReactModal__Content {
transform: translate(0, 0);
}
}
.CodeMirror {
z-index: 0;
padding: 10px;

View File

@ -7,7 +7,6 @@
"react": "^16.4.2",
"react-codemirror2": "^5.1.0",
"react-dom": "^16.4.2",
"react-modal": "^3.5.1",
"react-router-dom": "^4.3.1",
"url": "^0.11.0",
"valid-url": "^1.0.9"