diff --git a/demo/Page/Page.config.js b/demo/Page/Page.config.js
index 8c824e9700..a7cfcb141b 100644
--- a/demo/Page/Page.config.js
+++ b/demo/Page/Page.config.js
@@ -8,14 +8,28 @@ export default {
{
name: 'title',
label: 'Page Title',
- type: 'string',
- maxLength: 100
+ type: 'input',
+ maxLength: 100,
+ required: true
},
{
name: 'content',
label: 'Content',
type: 'textarea',
- height: 100
+ height: 100,
+ required: true
+ },
+ {
+ name: 'slides',
+ label: 'Slides',
+ type: 'repeater',
+ fields: [
+ {
+ name: 'content',
+ type: 'textarea',
+ label: 'Content'
+ }
+ ]
},
{
label: 'Meta Information',
@@ -23,14 +37,14 @@ export default {
fields: [
{
name: 'metaTitle',
- type: 'string',
+ type: 'input',
maxLength: 100,
label: 'Meta Title',
width: 50
},
{
name: 'metaKeywords',
- type: 'string',
+ type: 'input',
maxLength: 100,
label: 'Meta Keywords',
width: 50
diff --git a/demo/Page/components/Edit/index.js b/demo/Page/components/Edit/index.js
index dafb1d92f5..503992710c 100644
--- a/demo/Page/components/Edit/index.js
+++ b/demo/Page/components/Edit/index.js
@@ -7,11 +7,13 @@ import {
Button,
Form,
Input,
+ HiddenInput,
Textarea,
Group,
- FormSubmit
+ FormSubmit,
+ Repeater
} from 'payload/components';
-import { toKebabCase } from 'payload/utils';
+import { toKebabCase, convertArrayToObject } from 'payload/utils';
class Edit extends Component {
@@ -35,6 +37,7 @@ class Edit extends Component {
render() {
const initialData = this.props.data ? this.props.data : {};
+ const fields = convertArrayToObject(this.props.collection.fields, 'name');
return (
@@ -48,13 +51,45 @@ class Edit extends Component {
Save
} />
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components.js b/src/components.js
index 55da5be291..dc29f44c04 100644
--- a/src/components.js
+++ b/src/components.js
@@ -10,8 +10,12 @@ export { default as withEditData } from './components/data/edit';
// Field Types
export { default as fieldType } from './components/field-types/fieldType';
export { default as Group } from './components/field-types/Group';
+export { default as Repeater } from './components/field-types/Repeater';
export { default as Input } from './components/field-types/Input';
+export { default as Email } from './components/field-types/Email';
+export { default as Password } from './components/field-types/Password';
export { default as Textarea } from './components/field-types/Textarea';
+export { default as HiddenInput } from './components/field-types/HiddenInput';
// Forms
export { default as Form, FormContext } from './components/forms/Form';
@@ -29,6 +33,7 @@ export { default as Sticky } from './components/layout/Sticky';
export { default as Sidebar } from './components/layout/Sidebar';
export { default as ContentBlock } from './components/layout/ContentBlock';
export { default as Table } from './components/layout/Table';
+export { default as Section } from './components/layout/Section';
// Modules
export { default as Status } from './components/modules/Status';
diff --git a/src/components/field-types/Email/index.js b/src/components/field-types/Email/index.js
new file mode 100644
index 0000000000..f004cde8f9
--- /dev/null
+++ b/src/components/field-types/Email/index.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import { fieldType } from 'payload/components';
+
+import './index.scss';
+
+const error = 'Please enter a valid email';
+
+const validate = value => /\S+@\S+\.\S+/.test(value);
+
+const Email = props => {
+ return (
+
+ {props.error}
+ {props.label}
+
+
+ );
+}
+
+export default fieldType(Email, 'email', validate, error);
diff --git a/src/components/field-types/Email/index.scss b/src/components/field-types/Email/index.scss
new file mode 100644
index 0000000000..851f5f3195
--- /dev/null
+++ b/src/components/field-types/Email/index.scss
@@ -0,0 +1,16 @@
+@import '~payload/scss/styles';
+
+.field-type.email {
+ margin-bottom: rem(.5);
+ position: relative;
+
+ input {
+ @include formInput;
+ }
+
+ &.error {
+ input {
+ background-color: lighten($error, 20%);
+ }
+ }
+}
diff --git a/src/components/field-types/Group/index.js b/src/components/field-types/Group/index.js
index 1495cfe8c3..7c2be3070f 100644
--- a/src/components/field-types/Group/index.js
+++ b/src/components/field-types/Group/index.js
@@ -1,19 +1,11 @@
import React from 'react';
-
-import './index.scss';
+import { Section } from 'payload/components';
const Group = props => {
return (
-
- {props.heading &&
-
- }
-
- {props.children}
-
-
+
);
};
diff --git a/src/components/field-types/HiddenInput/index.js b/src/components/field-types/HiddenInput/index.js
new file mode 100644
index 0000000000..295d5b31dd
--- /dev/null
+++ b/src/components/field-types/HiddenInput/index.js
@@ -0,0 +1,7 @@
+import React from 'react';
+import { fieldType } from 'payload/components';
+
+const HiddenInput = props => ;
+
+export default fieldType(HiddenInput, 'hiddenInput');
diff --git a/src/components/field-types/Input/index.js b/src/components/field-types/Input/index.js
index 7c3fbfa74f..87fab4e11e 100644
--- a/src/components/field-types/Input/index.js
+++ b/src/components/field-types/Input/index.js
@@ -3,32 +3,9 @@ import { fieldType } from 'payload/components';
import './index.scss';
-const errors = {
- text: 'Please fill in the field',
- email: 'Please enter a valid email',
- password: 'Please enter a password'
-};
+const error = 'Please fill in the field';
-const validate = (value, type) => {
- let emailTest = /\S+@\S+\.\S+/;
-
- switch (type) {
- case 'text':
- return value.length > 0;
-
- case 'password':
- return value.length > 0;
-
- case 'email':
- return emailTest.test(value);
-
- case 'hidden':
- return true;
-
- default:
- return false;
- }
-}
+const validate = value => value.length > 0;
const Input = props => {
return (
@@ -43,11 +20,11 @@ const Input = props => {
onChange={props.onChange}
disabled={props.disabled}
placeholder={props.placeholder}
- type={props.type}
+ type="text"
id={props.id ? props.id : props.name}
name={props.name} />
);
}
-export default fieldType(Input, 'input', validate, errors);
+export default fieldType(Input, 'input', validate, error);
diff --git a/src/components/field-types/Password/index.js b/src/components/field-types/Password/index.js
new file mode 100644
index 0000000000..ae925cf295
--- /dev/null
+++ b/src/components/field-types/Password/index.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { fieldType } from 'payload/components';
+
+import './index.scss';
+
+const errors = {
+ password: 'Please enter a password',
+ confirm: 'Please ensure that both passwords match'
+};
+
+const validate = value => value.length > 0;
+
+const Password = props => {
+ return (
+
+ {props.error}
+ {props.label}
+
+
+ );
+}
+
+export default fieldType(Password, 'password', validate, errors);
diff --git a/src/components/field-types/Password/index.scss b/src/components/field-types/Password/index.scss
new file mode 100644
index 0000000000..83be0858b9
--- /dev/null
+++ b/src/components/field-types/Password/index.scss
@@ -0,0 +1,16 @@
+@import '~payload/scss/styles';
+
+.field-type.password {
+ margin-bottom: rem(.5);
+ position: relative;
+
+ input {
+ @include formInput;
+ }
+
+ &.error {
+ input {
+ background-color: lighten($error, 20%);
+ }
+ }
+}
diff --git a/src/components/field-types/Repeater/index.js b/src/components/field-types/Repeater/index.js
new file mode 100644
index 0000000000..c7dbb2154f
--- /dev/null
+++ b/src/components/field-types/Repeater/index.js
@@ -0,0 +1,19 @@
+import React, { Component } from 'react';
+import { Section } from 'payload/components';
+
+class Repeater extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
+
+ )
+ }
+}
+
+export default Repeater;
diff --git a/src/components/field-types/fieldType/index.js b/src/components/field-types/fieldType/index.js
index 7340c4dbd8..b8066e54d3 100644
--- a/src/components/field-types/fieldType/index.js
+++ b/src/components/field-types/fieldType/index.js
@@ -3,7 +3,7 @@ import { FormContext, Tooltip } from 'payload/components';
import './index.scss';
-const fieldType = (PassedComponent, slug, validate, errors) => {
+const fieldType = (PassedComponent, type, validate, errors) => {
class FieldType extends Component {
@@ -19,7 +19,7 @@ const fieldType = (PassedComponent, slug, validate, errors) => {
this.props.context.setValue({
name: this.props.name,
value: value,
- valid: this.props.required
+ valid: this.props.required && validate
? validate(value, this.props.type)
: true
});
@@ -52,7 +52,7 @@ const fieldType = (PassedComponent, slug, validate, errors) => {
const showError = valid === false && this.props.context.submitted;
- let className = `field-type ${slug}${showError ? ' error' : ''}`;
+ let className = `field-type ${type}${showError ? ' error' : ''}`;
let value = this.props.context.fields[this.props.name] ? this.props.context.fields[this.props.name].value : '';
@@ -92,9 +92,9 @@ const fieldType = (PassedComponent, slug, validate, errors) => {
if (props.showError) {
return (
- {props.type && errors[props.type]}
+ {props.error && errors[props.error]}
- {!props.type && errors}
+ {!props.error && errors}
)
}
diff --git a/src/components/forms/Form/index.scss b/src/components/forms/Form/index.scss
index 3c67b96b62..41a4f82e29 100644
--- a/src/components/forms/Form/index.scss
+++ b/src/components/forms/Form/index.scss
@@ -6,4 +6,8 @@ form {
> * {
width: 100%;
}
+
+ > .btn {
+ @include gutter;
+ }
}
diff --git a/src/components/forms/Submit/index.js b/src/components/forms/Submit/index.js
index 4e87db51a4..f50fb5c695 100644
--- a/src/components/forms/Submit/index.js
+++ b/src/components/forms/Submit/index.js
@@ -1,17 +1,21 @@
import React, { Component } from 'react';
import { FormContext, Button } from 'payload/components';
+import './index.scss';
+
class FormSubmit extends Component {
render() {
return (
-
+
+
+
);
}
}
-export default props => {
+const ContextFormSubmit = props => {
return (
{context => }
@@ -19,3 +23,5 @@ export default props => {
);
};
+export default ContextFormSubmit;
+
diff --git a/src/components/forms/Submit/index.scss b/src/components/forms/Submit/index.scss
new file mode 100644
index 0000000000..fc066fcf98
--- /dev/null
+++ b/src/components/forms/Submit/index.scss
@@ -0,0 +1,9 @@
+@import '~payload/scss/styles';
+
+form > .form-submit {
+ @include gutter;
+
+ .btn {
+ width: 100%;
+ }
+}
diff --git a/src/components/layout/Section/index.js b/src/components/layout/Section/index.js
new file mode 100644
index 0000000000..62163b9b9e
--- /dev/null
+++ b/src/components/layout/Section/index.js
@@ -0,0 +1,20 @@
+import React from 'react';
+
+import './index.scss';
+
+const Section = props => {
+ return (
+
+ {props.heading &&
+
+ }
+
+ {props.children}
+
+
+ )
+}
+
+export default Section;
diff --git a/src/components/field-types/Group/index.scss b/src/components/layout/Section/index.scss
similarity index 93%
rename from src/components/field-types/Group/index.scss
rename to src/components/layout/Section/index.scss
index 5e0e50a282..f2e9137b84 100644
--- a/src/components/field-types/Group/index.scss
+++ b/src/components/layout/Section/index.scss
@@ -1,6 +1,6 @@
@import '~payload/scss/styles';
-.field-group {
+section.section {
@extend %shadow;
margin: rem(1) rem(.5);
diff --git a/src/components/modules/Filter/index.js b/src/components/modules/Filter/index.js
index e6a9f2a83e..12e6463c9a 100644
--- a/src/components/modules/Filter/index.js
+++ b/src/components/modules/Filter/index.js
@@ -5,7 +5,7 @@ class Filter extends Component {
render() {
return (
);
}
diff --git a/src/components/modules/StickyAction/index.scss b/src/components/modules/StickyAction/index.scss
index fe75f1ae47..9294ebb5a7 100644
--- a/src/components/modules/StickyAction/index.scss
+++ b/src/components/modules/StickyAction/index.scss
@@ -13,6 +13,7 @@
.controls {
flex-shrink: 0;
+ display: flex;
.btn {
margin-top: 0;
diff --git a/src/components/views/CreateUser/index.js b/src/components/views/CreateUser/index.js
index 09d671d4bd..f60fc0a76c 100644
--- a/src/components/views/CreateUser/index.js
+++ b/src/components/views/CreateUser/index.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { ContentBlock, Form, Input, FormSubmit, SetStepNav } from 'payload/components';
+import { ContentBlock, Form, Email, Password, FormSubmit, SetStepNav } from 'payload/components';
import './index.scss';
@@ -16,8 +16,8 @@ const CreateUser = () => {
diff --git a/src/components/views/Login/index.js b/src/components/views/Login/index.js
index 722745257c..09514499b8 100644
--- a/src/components/views/Login/index.js
+++ b/src/components/views/Login/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
-import { ContentBlock, Form, Input, FormSubmit } from 'payload/components';
+import { ContentBlock, Form, Email, Password, FormSubmit } from 'payload/components';
import './index.scss';
@@ -21,8 +21,8 @@ const Login = props => {
To Dashboard
diff --git a/src/lib/helpers/convertData.js b/src/lib/helpers/convertData.js
new file mode 100644
index 0000000000..0237cf79b4
--- /dev/null
+++ b/src/lib/helpers/convertData.js
@@ -0,0 +1,22 @@
+export const convertArrayToObject = (arr, key) => {
+ return arr.reduce((obj, item) => {
+ if (key) {
+ obj[item[key]] = item;
+ return obj;
+ }
+
+ obj[item] = {};
+ return obj;
+ }, {});
+}
+
+export const convertObjectToArray = arr => {
+ return Object.values(arr);
+}
+
+export const convertArrayToHash = (arr, key) => {
+ return arr.reduce((obj, item, i) => {
+ obj[item[key]] = i;
+ return obj;
+ }, {});
+}
diff --git a/src/utils.js b/src/utils.js
index 53e1b589ac..5f3b8e6b20 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,2 +1,3 @@
export { default as toKebabCase } from './lib/helpers/toKebabCase';
export { default as getPropSubset } from './lib/helpers/getPropSubset';
+export { convertArrayToHash, convertArrayToObject, convertObjectToArray } from './lib/helpers/convertData';