diff --git a/README.md b/README.md
index 8ad77ef..17e90a8 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ npm install --save react-eureka
```jsx
import React, { Component } from 'react';
-import { EurekaForm } from 'react-eureka';
+import { EurekaForm, Question } from 'react-eureka';
class EurekaDemo extends Component {
constructor(props) {
@@ -27,7 +27,7 @@ class EurekaDemo extends Component {
formSubmitted: false
};
}
-
+
_onSubmit = () => this.setState({...this.state, formSubmitted: true})
render() {
@@ -43,13 +43,13 @@ class EurekaDemo extends Component {
What's your name
-
+
Hello {values.name} , and your email?
-
+
-
+
Phone Number?
-
+
}
@@ -84,7 +84,7 @@ there are 2 APIs you can use (you can actually use both, but we don't recomend i
in the questions API you pass your questions as a JSON object.
in the React children API you pass the components you want to display as your questions.
-## questions API
+## questions array API
```js
const questions = [{
@@ -102,22 +102,63 @@ const questions = [{
```
-## React children API
-*Note:* **The type prop sets both the HTML form type and the key in the values object**
+## React Component API
+*Note:* **The type prop sets both the HTML form type and the key in the
+values object**
+each child you give to EurekaForm will be treated as a `question`, the
+easiest way is to use the `Question` helper we provide.
+
+### Question Helper
```jsx
-
+
What's your name
-
-
+
+
Hello {values.name} , and your email?
-
-
+
+
Phone Number?
-
+
```
+### Full API
+In order to implement your own Question Helper you just need to call
+`onChange` when your value changes, if you are going to use an ` `
+tag, please pass down the `type` property to make sure HTML5 validation
+still works. You should also make sure to gracefully handle the `children`
+prop, as it'll be usual to pass down the question component there.
+
+```jsx
+const InputQuestion = ({ onChange, type, children }) => (
+
+ {children}
+
+
+)
+
+const ListQuestion = ({ options, onChange, children }) => (
+
+ {children}
+ { options.map(opt, i => (
+ onChange(opt)}>{opt}
+ )}
+
+)
+
+const MyForm = ({values = {}}) => (
+
+
+ What's your name ?
+
+
+ Hello {values.name} , and your country ?
+
+
+)
+```
+
## Credits
The implementation of the component is based on the work of
[Mary Lou from Tympanus](https://tympanus.net/Development/MinimalForm/)
diff --git a/example/README.md b/example/README.md
new file mode 100644
index 0000000..e06a486
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,9 @@
+# eureka-form simple example
+
+this example shows how to use the `Question` and `EurekaForm` components
+exported from the main package.
+
+## runing
+```sh
+yarn && yarn start
+```
diff --git a/example/node_modules/eureka-form b/example/node_modules/eureka-form
new file mode 120000
index 0000000..8fdb6a2
--- /dev/null
+++ b/example/node_modules/eureka-form
@@ -0,0 +1 @@
+../../build
\ No newline at end of file
diff --git a/example/package.json b/example/package.json
new file mode 100644
index 0000000..2768e16
--- /dev/null
+++ b/example/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "example",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "react": "^16.4.2",
+ "react-dom": "^16.4.2",
+ "react-scripts": "1.1.5"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
\ No newline at end of file
diff --git a/example/public/index.html b/example/public/index.html
new file mode 100644
index 0000000..ed0ebaf
--- /dev/null
+++ b/example/public/index.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+ React App
+
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
+
diff --git a/example/src/index.js b/example/src/index.js
new file mode 100644
index 0000000..dfdeae6
--- /dev/null
+++ b/example/src/index.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { EurekaForm, Question, Number } from '../../build';
+
+class InputQuestion extends Question {
+ render() {
+ const { onChange, type, children } = this.props
+ return (
+
+ {children}
+
+
+ )
+ }
+}
+
+class ListQuestion extends Question {
+ render() {
+ const { options, onChange, children } = this.props
+ return (
+
+ {children}
+ { options.map((opt, i) => (
+ onChange(opt)}>{opt}
+ ))}
+
+ )
+ }
+}
+
+const doStuff = (values) => console.log('submitted', values)
+
+const MyForm = ({values = {}}) => (
+
+
+ What's your name ?
+
+
+ Hello {values.name} , and your country ?
+
+
+)
+
+ReactDOM.render( , document.getElementById('root'));
diff --git a/src/index.js b/src/index.js
index 8b2be33..af61d97 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,6 +4,22 @@ import './style.css';
import ICONS_ARROW from 'react-icons/lib/md/arrow-forward';
+const Digit = ({children, ...props}) => (
+
+
+ {children}
+
+
+)
+
+const Number = ({value}) => (
+
+ {value + 1}
+ {value}
+ {value - 1}
+
+)
+
const checkTransitionsSupport = () => {
const b = document.body || document.documentElement;
const s = b.style;
@@ -37,13 +53,90 @@ const randomID = () => {
return id;
};
-class EurekaForm extends React.Component {
+class Question extends React.PureComponent {
constructor(props) {
- super(props);
+ super(props)
+
+ this.inputRef = null
+ }
+
+ _validateHTML5(input) {
+ if (input.setCustomValidity && input.checkValidity) {
+ // clear any previous error messages
+ input.setCustomValidity('');
+
+ // checks input against the validation constraint
+ if (!input.checkValidity()) {
+ // Optionally, set a custom HTML5 valiation message
+ // comment or remove this line to use the browser default message
+ //input.setCustomValidity('Whoops, that\'s not an email address!');
+
+ // prevent the question from changing
+ return input.validationMessage;
+ }
+ }
+ return true
+ }
+
+ validate() {
+ // XXX(xaiki): here the correct way would be to use ReactDOM to find
+ // `this` so we don't have to pass the node around, but that would
+ // introduce a new dependency, so we have this weird API where we let
+ // the user shoot his foot by providing us the (wrong?) ref
+ return this._validateHTML5(this.inputRef)
+ }
+ focus() {
+ this.inputRef && this.inputRef.focus()
+ }
+
+ render () {
+ const {
+ children = [],
+ type = "text",
+ key,
+ focus,
+ } = this.props
+
+ let { onChange } = this.props
+
+ if (! onChange) {
+ console.error('warning you didnt pass an `onChange` handler')
+ onChange = function () {}
+ }
+
+ return (
+
+
+
+ {children}
+
+
+
+ this.inputRef = e}
+ id={key} name={key}
+ onChange={onChange}
+ type={type}
+ disabled={!focus}/>
+
+ )
+ }
+}
+
+class EurekaForm extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ const { questions = [], children } = props
+ const stateQuestions = questions.map((question, i) => (
+
+ {question.title}
+
+ )).concat(children)
+
+ this.questionRefs = {}
this.state = {
+ questions: stateQuestions,
current: 0,
- questions: [],
values: {},
wasSubmitted: false
};
@@ -53,7 +146,6 @@ class EurekaForm extends React.Component {
const childQuestions = React.Children.map(this.props.children, (child, i) => ({
type: child.props.type || `eureka-question-${i}`
}))
- const questions = [].slice.call(this.formRef.querySelectorAll( 'ol.questions > li' ));
const values = (this.props.questions || [])
.concat(childQuestions)
.reduce((acc, cur) => Object.assign({}, acc, {
@@ -61,11 +153,8 @@ class EurekaForm extends React.Component {
}), {})
this.setState({
...this.state,
- questions,
values,
- // XXX: this actually doesn't catch duplicate keys,
- // i.e. it actually counts the *awswers* you can get.
- questionsCount: Object.keys(values).length,
+ questionsCount: this.state.questions.length
}, callback);
}
@@ -73,8 +162,6 @@ class EurekaForm extends React.Component {
this._updateQuestions(() => {
// show first question
this.props.onUpdate(this.state)
- const firstQuestion = this.state.questions[0];
- firstQuestion.classList.add('current');
// next question control
this.ctrlNext = this.formRef.querySelector('button.next');
@@ -92,26 +179,9 @@ class EurekaForm extends React.Component {
// // question number status
this.questionStatus = this.formRef.querySelector('span.number');
-
+
// // give the questions status an id
this.questionStatus.id = this.questionStatus.id || randomID();
-
- // associate "x / y" with the input via aria-describedby
- for (var i = this.state.questions.length - 1; i >= 0; i--) {
- const formElement = this.state.questions[i].querySelector('input, textarea, select');
- formElement.setAttribute('aria-describedby', this.questionStatus.id);
- };
-
- // // current question placeholder
- this.currentNum = this.questionStatus.querySelector('span.number-current');
- this.currentNum.innerHTML = Number(this.state.current + 1);
-
- // // total questions placeholder
- this.totalQuestionNum = this.questionStatus.querySelector('span.number-total');
- this.totalQuestionNum.innerHTML = this.state.questionsCount;
-
- // // error message
- this.error = this.formRef.querySelector('span.error-message');
// checks for HTML5 Form Validation support
// a cleaner solution might be to add form validation to the custom Modernizr script
@@ -119,107 +189,40 @@ class EurekaForm extends React.Component {
// init events
this._initEvents();
+ this.questionRefs[this.state.current].focus()
});
}
_initEvents() {
- // first input
- const firstElInput = this.state.questions[this.state.current].querySelector('input, textarea, select');
-
- // focus
- const onFocusStartFn = () => {
- firstElInput.removeEventListener('focus', onFocusStartFn);
-
- this.ctrlNext.classList.add('show');
- };
-
- // show the next question control first time the input gets focused
- firstElInput.addEventListener('focus', onFocusStartFn);
-
- if (this.props.autoFocus) {
- firstElInput.focus();
- }
-
- // show next question
- this.ctrlNext.addEventListener('click', ev => {
+ // show next question
+ this.ctrlNext.addEventListener('click', ev => {
ev.preventDefault();
- this._nextQuestion();
- });
+ this._nextQuestion();
+ });
- // pressing enter will jump to next question
- this.formRef.addEventListener('keydown', ev => {
+ // pressing enter will jump to next question
+ this.formRef.addEventListener('keydown', ev => {
const keyCode = ev.keyCode || ev.which;
-
- // enter
- if(keyCode === 13) {
+
+ // enter
+ if(keyCode === 13) {
ev.preventDefault();
-
+
this._nextQuestion();
- }
- });
+ }
+ });
}
- _nextQuestion() {
- if(!this._validate()) {
- return false;
- }
-
- // checks HTML5 validation
- if (this.supportsHTML5Forms) {
- const input = this.state.questions[this.state.current].querySelector('input, textarea, select');
- // clear any previous error messages
- input.setCustomValidity('');
-
- // checks input against the validation constraint
- if (!input.checkValidity()) {
- // Optionally, set a custom HTML5 valiation message
- // comment or remove this line to use the browser default message
- //input.setCustomValidity('Whoops, that\'s not an email address!');
-
- // display the HTML5 error message
- this._showError(input.validationMessage);
-
- // prevent the question from changing
- return false;
- }
- }
-
- // check if form is filled
- if (this.state.current === this.state.questionsCount - 1) {
- this.isFilled = true;
- }
-
- // clear any previous error messages
- this._clearError();
-
- // current question
- const currentQuestion = this.state.questions[this.state.current];
- currentQuestion.querySelector('input, textarea, select').blur();
- this._setValue(currentQuestion)
-
- this.setState({
- ...this.state,
- // increment current question iterator
- current: ++this.state.current
- }, () => {
+ _nextQuestionFinish() {
+ {
// update progress bar
this._progress();
-
- let nextQuestion;
+ this.props.onUpdate(this.state)
if(!this.isFilled) {
- // change the current question number/status
- this._updateQuestionNumber();
-
// add class "show-next" to form element (start animations)
this.formRef.classList.add('show-next');
-
- // remove class "current" from current question and add it to the next one
- // current question
- nextQuestion = this.state.questions[this.state.current];
- currentQuestion.classList.remove('current');
- nextQuestion.classList.add('current');
}
// after animation ends, remove class "show-next" from form element and change current question placeholder
@@ -231,17 +234,10 @@ class EurekaForm extends React.Component {
if(self.isFilled) {
this._submit();
- }
-
- else {
+ } else {
this.formRef.classList.remove('show-next');
-
- this.currentNum.innerHTML = this.nextQuestionNum.innerHTML;
- this.questionStatus.removeChild(this.nextQuestionNum);
-
- // force the focus on the next input
- nextQuestion.querySelector('input, textarea, select').focus();
}
+ this.questionRefs[this.state.current].focus()
};
// onEndTransitionFn();
@@ -252,80 +248,82 @@ class EurekaForm extends React.Component {
else {
onEndTransitionFn();
}
- });
+ }
+ }
+
+ _nextQuestion() {
+ const component = this.questionRefs[this.state.current]
+ if (! component.validate) {
+ console.error("Warning, component", component, "doesn't support validation, implement your validate() method")
+ } else {
+ const validationResult = component.validate()
+ if (validationResult !== true) {
+ return this._showError(validationResult);
+ }
+ }
+
+ // check if form is filled
+ if (this.state.current === this.state.questionsCount - 1) {
+ this.isFilled = true;
}
- // updates the progress bar by setting its width
- _progress() {
- const currentProgress = this.state.current * ( 100 / this.state.questionsCount );
- this.progress.style.width = currentProgress + '%';
-
- // update the progressbar's aria-valuenow attribute
- this.progress.setAttribute('aria-valuenow', currentProgress);
+ // clear any previous error messages
+ this._clearError();
+
+ this.setState(state => ({
+ ...state,
+ // increment current question iterator
+ current: ++state.current
+ }), () => this._nextQuestionFinish());
}
-
- _validate() {
- if (!this.state.questions[this.state.current]) {
- return false;
- }
- // current question´s input
- const input = this.state.questions[this.state.current].querySelector('input, textarea, select').value;
- if (input === '') {
- this._showError('EMPTYSTR');
-
- return false;
- }
+ // updates the progress bar by setting its width
+ _progress() {
+ const currentProgress = this.state.current * ( 100 / this.state.questionsCount );
+ this.progress.style.width = currentProgress + '%';
- return true;
+ // update the progressbar's aria-valuenow attribute
+ this.progress.setAttribute('aria-valuenow', currentProgress);
}
- _updateQuestionNumber() {
- // first, create next question number placeholder
- this.nextQuestionNum = document.createElement('span');
- this.nextQuestionNum.className = 'number-next';
- this.nextQuestionNum.innerHTML = Number(this.state.current + 1);
-
- // insert it in the DOM
- this.questionStatus.appendChild(this.nextQuestionNum);
- }
-
_showError(err) {
- let message = '';
- switch(err) {
- case 'EMPTYSTR':
- message = 'Please fill the field before continuing';
- break;
- case 'INVALIDEMAIL':
- message = 'Please fill a valid email address';
- break;
- // ...
- default:
- message = err;
+ let message = '';
+ switch(err) {
+ case 'EMPTYSTR':
+ message = 'Please fill the field before continuing';
+ break;
+ case 'INVALIDEMAIL':
+ message = 'Please fill a valid email address';
+ break;
+ // ...
+ default:
+ message = err;
};
-
- this.error.innerHTML = message;
-
- this.error.classList.add('show');
+
+ this.setState({
+ error: message
+ })
}
-
+
_clearError() {
- this.error.classList.remove('show');
+ this.setState({
+ error: null
+ })
}
-
- _setValue(question) {
- const questionInput = question.querySelector('input, textarea, select');
- questionInput.setAttribute("disabled", true);
- const key = questionInput.getAttribute("id")
- const newState = {
- ...this.state,
- values: {
- ...this.state.values,
- [key]: questionInput.value
+
+ _change(key) {
+ return function (value) {
+ const { questions, current } = this.state
+
+ const newState = {
+ ...this.state,
+ values: {
+ ...this.state.values,
+ [key]: value
+ }
}
+ this.setState(newState)
}
- this.setState(newState)
- this.props.onUpdate(newState)
}
_submit() {
@@ -341,70 +339,59 @@ class EurekaForm extends React.Component {
this.props.onSubmit(this.formRef, this.state.values);
});
}
- }
+ }
render() {
- let customClass = "";
-
- if (this.props.className) {
- customClass = this.props.className + " ";
- }
-
- return (
-
+ )
}
}
@@ -412,4 +399,4 @@ EurekaForm.defaultProps = {
onUpdate: function () {}
}
-module.exports = { EurekaForm };
+module.exports = { EurekaForm, Question, Number };
diff --git a/src/style.css b/src/style.css
index 2257604..878a608 100644
--- a/src/style.css
+++ b/src/style.css
@@ -1,91 +1,130 @@
.simform {
- position: relative;
- text-align: left;
- font-size: 2.5em;
+ position: relative;
+ text-align: left;
+ font-size: 2.5em;
}
.simform .submit {
- display: none;
+ display: none;
+}
+
+.rollingNumber {
+ display: inline-block;
+ padding: 0;
+ margin: 0;
+}
+
+.rollingNumber li {
+ display: block;
+ bottom: 0;
+ right: -4px;
}
/* Question list style */
.simform ol {
- margin: 0;
- padding: 0;
- list-style: none;
- position: relative;
- -webkit-transition: height 0.4s;
- transition: height 0.4s;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ position: relative;
+ -webkit-transition: height 0.4s;
+ transition: height 0.4s;
}
.simform ol:before {
- content: '';
- background-color: rgba(0,0,0,0.1);
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- height: 2.35em;
+ content: '';
+ background-color: rgba(0,0,0,0.1);
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 2.35em;
}
.questions li {
- z-index: 100;
- position: relative;
- visibility: hidden;
- height: 0;
- -webkit-transition: visibility 0s 0.4s, height 0s 0.4s;
- transition: visibility 0s 0.4s, height 0s 0.4s;
+ z-index: 100;
+ position: relative;
+ height: 0;
+ visibility: hidden;
}
.questions li.current,
.no-js .questions li {
- visibility: visible;
- height: auto;
- -webkit-transition: none;
- transition: none;
+ visibility: visible;
+ height: auto;
+ -webkit-transition: none;
+ transition: none;
}
/* Labels */
.questions li > span {
- display: block;
+ display: block;
overflow: hidden;
margin-bottom: 0.8em;
}
-.questions li > span label {
- display: block;
- -webkit-transition: -webkit-transform 0.4s;
- transition: transform 0.4s;
- -webkit-transform: translateY(-100%);
- transform: translateY(-100%);
+.questions li label {
+ display: block;
+ visibility: visible;
+ opacity: 0;
+ -webkit-transform: translateY(-100%);
+ transform: translateY(-100%);
+}
+
+.questions li.current label,
+.no-js .questions li label {
+ opacity: 1;
+ -webkit-transition: none;
+ transition: none;
+ -webkit-transform: translateY(-100%);
+ transform: translateY(-100%);
}
-.questions li.current > span label,
-.no-js .questions li > span label {
- -webkit-transition: none;
- transition: none;
- -webkit-transform: translateY(0);
- transform: translateY(0);
+.questions li.prev label {
+ background: green;
+ -webkit-animation: moveUpFromCenter 4s both;
+ animation: moveUpFromCenter 4s both;
}
-.show-next .questions li.current > span label {
- -webkit-animation: moveUpFromDown 0.4s both;
- animation: moveUpFromDown 0.4s both;
+.questions li.current label {
+ background: red;
+ -webkit-animation: moveUpFromDown 4s both;
+ animation: moveUpFromDown 4s both;
+}
+
+.questions li.next label {
+ background: blue;
+}
+
+.questions li.current:first-child label {
+ -webkit-animation: none;
+ animation: none;
+ transform: translateY(0);
}
@-webkit-keyframes moveUpFromDown {
- from { -webkit-transform: translateY(100%); }
- to { -webkit-transform: translateY(0); }
+ from { -webkit-transform: translateY(100%); opacity: 0; }
+ to { -webkit-transform: translateY(0); opacity: 1; }
}
@keyframes moveUpFromDown {
- from { -webkit-transform: translateY(100%); transform: translateY(100%); }
- to { -webkit-transform: translateY(0); transform: translateY(0); }
+ from { -webkit-transform: translateY(100%); transform: translateY(100%); opacity: 0; }
+ to { -webkit-transform: translateY(0); transform: translateY(0); opacity: 1; }
}
+@-webkit-keyframes moveUpFromCenter {
+ from { -webkit-transform: translateY(0); opacity: 1; }
+ to { -webkit-transform: translateY(-100%); opacity: 0; }
+}
+
+@keyframes moveUpFromCenter {
+ from { -webkit-transform: translateY(0); transform: translateY(0); opacity: 1; }
+ to { -webkit-transform: translateY(-100%); transform: translateY(-100%); opacity: 0; }
+}
+
+
/* Input field */
.questions input {
- display: block;
+ display: block;
margin: 0.3em 0 0 0;
padding: 0.2em 1em 0.5em 0.7em;
width: calc(100% - 2em);
@@ -102,180 +141,156 @@
.questions .current input,
.no-js .questions input {
- opacity: 1;
+ opacity: 1;
}
.questions input:focus,
.simform button:focus {
- outline: none;
+ outline: none;
}
/* Next question button */
-.next {
- position: absolute;
+button.next {
+ position: absolute;
right: 0.6em;
bottom: 1.2em;
- display: block;
- padding: 0;
- border: none;
- background: none;
- color: rgba(0,0,0,0.4);
- text-align: center;
+ display: block;
+ padding: 0;
+ border: none;
+ background: none;
+ color: rgba(0,0,0,0.4);
+ text-align: center;
opacity: 0;
font-size: 30px;
- z-index: 100;
- cursor: pointer;
- -webkit-transition: -webkit-transform 0.3s, opacity 0.3s;
- transition: transform 0.3s, opacity 0.3s;
- -webkit-transform: translateX(-20%);
- transform: translateX(-20%);
- pointer-events: none;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ z-index: 100;
+ cursor: pointer;
+ -webkit-transition: -webkit-transform 0.3s, opacity 0.3s;
+ transition: transform 0.3s, opacity 0.3s;
+ -webkit-transform: translateX(-20%);
+ transform: translateX(-20%);
+ pointer-events: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
-.next:hover {
- color: rgba(0,0,0,0.5);
+button.next:hover {
+ color: rgba(0,0,0,0.5);
}
-.next.show {
- opacity: 1;
- -webkit-transform: translateX(0);
- transform: translateX(0);
- pointer-events: auto;
+button.next.show {
+ opacity: 1;
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ pointer-events: auto;
}
/* Progress bar */
.simform .progress {
- width: 0%;
- height: 0.15em;
- background: rgba(0,0,0,0.3);
- -webkit-transition: width 0.4s ease-in-out;
- transition: width 0.4s ease-in-out;
+ width: 0%;
+ height: 0.15em;
+ background: rgba(0,0,0,0.3);
+ -webkit-transition: width 0.4s ease-in-out;
+ transition: width 0.4s ease-in-out;
}
.simform .progress::before {
- position: absolute;
- top: auto;
- width: 100%;
- height: inherit;
- background: rgba(0,0,0,0.05);
- content: '';
+ position: absolute;
+ top: auto;
+ width: 100%;
+ height: inherit;
+ background: rgba(0,0,0,0.05);
+ content: '';
}
/* Number indicator */
.simform .number {
- position: absolute;
- right: 0;
- overflow: hidden;
- margin: 0.4em 0;
- width: 3em;
- font-weight: 700;
- font-size: 0.4em;
+ position: absolute;
+ right: 0;
+ overflow: hidden;
+ margin: 0.4em 0;
+ width: 3em;
+ font-weight: 700;
+ font-size: 0.4em;
}
.simform .number:after {
- position: absolute;
- left: 50%;
- content: '/';
- opacity: 0.4;
- -webkit-transform: translateX(-50%);
- transform: translateX(-50%);
+ position: absolute;
+ left: 50%;
+ content: '/';
+ opacity: 0.4;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
}
.simform .number span {
- float: right;
- width: 40%;
- text-align: center;
+ float: right;
+ width: 40%;
+ text-align: center;
}
.simform .number .number-current {
- float: left;
+ float: left;
}
.simform .number-next {
- position: absolute;
- left: 0;
+ position: absolute;
+ left: 0;
}
.simform.show-next .number-current {
- -webkit-transition: -webkit-transform 0.4s;
- transition: transform 0.4s;
- -webkit-transform: translateY(-100%);
- transform: translateY(-100%);
+ -webkit-transition: -webkit-transform 0.4s;
+ transition: transform 0.4s;
+ -webkit-transform: translateY(-100%);
+ transform: translateY(-100%);
}
.simform.show-next .number-next {
- -webkit-animation: moveUpFromDown 0.4s both;
- animation: moveUpFromDown 0.4s both;
+ -webkit-animation: moveUpFromDown 0.4s both;
+ animation: moveUpFromDown 0.4s both;
}
/* Error and final message */
-.simform .error-message,
-.simform .final-message {
- position: absolute;
- visibility: hidden;
- opacity: 0;
- -webkit-transition: opacity 0.4s;
- transition: opacity 0.4s;
+.simform .error-message{
+ position: absolute;
+ -webkit-transition: opacity 0.4s;
+ transition: opacity 0.4s;
}
.simform .error-message {
- padding: 0.4em 3.5em 0 0;
- width: 100%;
- color: rgba(0,0,0,0.7);
- font-style: italic;
- font-size: 0.4em;
-}
-
-.final-message {
- top: 50%;
- left: 0;
- padding: 0.5em;
- width: 100%;
- text-align: center;
- -webkit-transform: translateY(-50%);
- transform: translateY(-50%);
-}
-
-.error-message.show,
-.final-message.show {
- visibility: visible;
- opacity: 1;
-}
-
-.final-message.show {
- -webkit-transition-delay: 0.5s;
- transition-delay: 0.5s;
+ padding: 0.4em 3.5em 0 0;
+ width: 100%;
+ color: rgba(0,0,0,0.7);
+ font-style: italic;
+ font-size: 0.4em;
}
/* Final hiding of form / showing message */
.simform-inner.hide {
- visibility: hidden;
- opacity: 0;
- -webkit-transition: opacity 0.3s, visibility 0s 0.3s;
- transition: opacity 0.3s, visibility 0s 0.3s;
+ visibility: hidden;
+ opacity: 0;
+ -webkit-transition: opacity 0.3s, visibility 0s 0.3s;
+ transition: opacity 0.3s, visibility 0s 0.3s;
}
/* No JS Fallback */
.no-js .simform {
- font-size: 1.75em;
+ font-size: 1.75em;
}
.no-js .questions li {
- padding: 0 0 2em;
+ padding: 0 0 2em;
}
.no-js .simform .submit {
- display: block;
- float: right;
- padding: 10px 20px;
- border: none;
- background: rgba(0,0,0,0.3);
- color: rgba(0,0,0,0.4);
+ display: block;
+ float: right;
+ padding: 10px 20px;
+ border: none;
+ background: rgba(0,0,0,0.3);
+ color: rgba(0,0,0,0.4);
}
.no-js .simform .controls {
- display: none;
+ display: none;
}
/* Remove IE clear cross */
@@ -285,13 +300,13 @@ input[type=text]::-ms-clear {
/* Adjust form for smaller screens */
@media screen and (max-width: 44.75em) {
- .simform {
- font-size: 1.8em;
- }
+ .simform {
+ font-size: 1.8em;
+ }
}
@media screen and (max-width: 33.5625em) {
- .simform {
- font-size: 1.2em;
- }
-}
\ No newline at end of file
+ .simform {
+ font-size: 1.2em;
+ }
+}