Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 15 additions & 43 deletions docs/src/components/DemoPage.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Panel, FormControl, Grid, Row, Col } from 'react-bootstrap';
import store from '../state/store';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';
import {
IField,
IFieldContext,
IFormError,
FieldOptionEditor,
FieldSelector,
FormBuilder,
FormInput,
FormDisplay,
} from 'react-dynamic-formbuilder';

import FormBuilder from './FormBuilder';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The consumer must create a Higher Order Component (HOC) from the original FormBuilder and pass in store reference. This means creating separate component file for their HOC

import FieldSelector from './FieldSelector';
import FieldOptionEditor from './FieldOptionEditor';

import './DemoPage.css';

import { FieldRegistry } from './constants';

interface IState {
fields: IField[],
editingField: IField,
editingContext: IFieldContext;
value: any,
error: IFormError,
}
Expand All @@ -31,37 +31,22 @@ class DemoPage extends React.Component<void, IState> {

constructor() {
super()
this.onChangeFields = this.onChangeFields.bind(this);
this.onFieldEditing = this.onFieldEditing.bind(this);
this.onDeleteField = this.onDeleteField.bind(this);
this.onFieldOptionChanged = this.onFieldOptionChanged.bind(this);
this.onValueChanged = this.onValueChanged.bind(this);
this.onBeforeAddField = this.onBeforeAddField.bind(this);
this.onValueChanged = this.onValueChanged.bind(this);
this.getStoreState = this.getStoreState.bind(this);
this.state = {
fields: [],
editingField: null,
editingContext: null,
fields: store.getFields(),
value: {},
error: null,
};
}

private onFieldEditing(field: IField, fieldContext: IFieldContext, done: (field: IField) => void) {
this.setState({ editingField: field, editingContext: fieldContext } as IState);
this.fieldEdited = done;
}

private onDeleteField(fields: IField[]) {
this.setState({ fields } as IState);
}

private onChangeFields(fields: IField[]) {
this.setState({ fields } as IState);
componentDidMount() {
store.subscribe(this.getStoreState);
}

private onFieldOptionChanged(field: IField) {
this.setState({ editingField: field } as IState);
this.fieldEdited(field);
private getStoreState() {
this.setState({fields: store.getFields()} as IState);
}

private onValueChanged(value: any, error: IFormError) {
Expand All @@ -85,34 +70,21 @@ class DemoPage extends React.Component<void, IState> {
<Col md={3}>
<span>Field Selector</span>
<Panel>
<FieldSelector
registry={FieldRegistry}
/>
<FieldSelector/>
</Panel>
</Col>
<Col md={5}>
<span>Form Builder</span>
<Panel>
<div className='form-horizontal'>
<FormBuilder
onFieldEditing={this.onFieldEditing}
onChange={this.onChangeFields}
registry={FieldRegistry}
fields={this.state.fields}
onBeforeAddField={this.onBeforeAddField}
/>
<FormBuilder onBeforeAddField={this.onBeforeAddField}/>
</div>
</Panel>
</Col>
<Col md={4}>
<span>Option Editor</span>
<Panel>
<FieldOptionEditor
registry={FieldRegistry}
field={this.state.editingField}
fieldContext={this.state.editingContext}
onChange={this.onFieldOptionChanged}
/>
<FieldOptionEditor/>
</Panel>
</Col>
</Row>
Expand Down
5 changes: 5 additions & 0 deletions docs/src/components/FieldOptionEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import store from '../state/store';
import { connectFieldOptionEditor } from '../../../src/state/connectFieldOptionEditor';
import { FieldOptionEditor } from 'react-dynamic-formbuilder';

export default connectFieldOptionEditor(store)(FieldOptionEditor);
5 changes: 5 additions & 0 deletions docs/src/components/FieldSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import store from '../state/store';
import { connectFieldSelector } from '../../../src/state/connectFieldSelector';
import { FieldSelector } from 'react-dynamic-formbuilder';

export default connectFieldSelector(store)(FieldSelector);
5 changes: 5 additions & 0 deletions docs/src/components/FormBuilder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import store from '../state/store';
import { connectFormBuilder } from '../../../src/state/connectFormBuilder';
import { FormBuilder } from 'react-dynamic-formbuilder';

export default connectFormBuilder(store)(FormBuilder);
4 changes: 4 additions & 0 deletions docs/src/state/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FieldRegistry } from '../components/constants';
import { createStore } from '../../../src/state/store';

export default createStore({registry: FieldRegistry});
6 changes: 5 additions & 1 deletion src/components/FormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { FormBuilderEditable as Editable } from './FormBuilderEditable';
import { FormBuilderDroppable as Droppable } from './FormBuilderDroppable';
import { FormBuilderContext } from './FormBuilderContext';

export interface IFormBuilderProps {
export interface IFormBuilderProps extends IFormBuilderRequiredProps, IFormBuilderOptionalProps {
}
export interface IFormBuilderRequiredProps {
fields: data.IField[];

// registry contains a map of field types to classes. FormBuilder
Expand All @@ -19,7 +21,9 @@ export interface IFormBuilderProps {

// fieldEditing is called when the user want to edit field options.
onFieldEditing: (field: data.IField, fieldContext: data.IFieldContext, done: (field: data.IField) => void) => void;
}

export interface IFormBuilderOptionalProps {
// onBeforeAddField is called before add the new field into the array.
// If this method returns false, onChange will not be called.
onBeforeAddField?: (field: data.IField) => boolean;
Expand Down
42 changes: 42 additions & 0 deletions src/state/connect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';
import { IStore } from './store';

export interface MapStateFunc<T> {
(store: IStore): T;
}

export interface ConnectComponent<WrappedComponentProps> {
(WrappedComponent: React.ComponentClass<WrappedComponentProps>): React.ComponentClass<any>;
}

// connect works similar to react-redux. It takes in a store and a function
// to map the state. This function should not be consumed directly because.
// Instead use a helper function: connectFormBuilder, connectFieldSelector, connectFieldOptionEditor.
export function connect<WrappedComponentProps>(store: IStore, mapState: MapStateFunc<WrappedComponentProps>): ConnectComponent<WrappedComponentProps> {
return (WrappedComponent: React.ComponentClass<WrappedComponentProps>) => {
return class Connected extends React.Component<void, WrappedComponentProps> {
constructor() {
super();
this.getState = this.getState.bind(this);
this.state = mapState(store) as WrappedComponentProps;
store.subscribe(this.getState);
}

private getState() {
this.setState(mapState(store));
}

componentDidMount() {
store.subscribe(this.getState);
}

componentWillUnmount() {
store.unsubscribe(this.getState);
}

render() {
return <WrappedComponent {...this.props} {...this.state}/>;
}
}
}
}
18 changes: 18 additions & 0 deletions src/state/connectFieldOptionEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { connect } from './connect';
import { IStore } from './store';
import { IFieldOptionEditorComponentProps } from '../components/FieldOptionEditor';

export function connectFieldOptionEditor(store: IStore) {
return (FieldOptionEditor: React.ComponentClass<IFieldOptionEditorComponentProps>): React.ComponentClass<void> => {
return connect(store, connectStoreToFieldOptionEditor)(FieldOptionEditor) as React.ComponentClass<void>;
}
}

export function connectStoreToFieldOptionEditor(store: IStore): IFieldOptionEditorComponentProps {
return {
field: store.getEditingField(),
fieldContext: store.getEditingContext(),
registry: store.getRegistry(),
onChange: store.onFieldChanged,
}
}
15 changes: 15 additions & 0 deletions src/state/connectFieldSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { connect } from './connect';
import { IStore } from './store';
import { IFieldSelectorProps } from '../components/FieldSelector';

export function connectFieldSelector(store: IStore) {
return (FieldSelector: React.ComponentClass<IFieldSelectorProps>): React.ComponentClass<void> => {
return connect(store, connectStoreToFieldSelector)(FieldSelector) as React.ComponentClass<void>;
}
}

export function connectStoreToFieldSelector(store: IStore): IFieldSelectorProps {
return {
registry: store.getRegistry(),
}
}
18 changes: 18 additions & 0 deletions src/state/connectFormBuilder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { connect } from './connect';
import { IStore } from './store';
import { IFormBuilderOptionalProps, IFormBuilderRequiredProps, IFormBuilderProps } from '../components/FormBuilder';

export function connectFormBuilder(store: IStore) {
return (FormBuilder: React.ComponentClass<IFormBuilderProps>): React.ComponentClass<IFormBuilderOptionalProps> => {
return connect(store, connectStoreToFormBuilder)(FormBuilder) as React.ComponentClass<IFormBuilderOptionalProps>;
}
}

export function connectStoreToFormBuilder(store: IStore): IFormBuilderRequiredProps {
return {
fields: store.getFields(),
registry: store.getRegistry(),
onChange: store.onChangeFields,
onFieldEditing: store.onFieldEditing,
}
}
4 changes: 4 additions & 0 deletions src/state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './connectFormBuilder';
export * from './connectFieldSelector';
export * from './connectFieldOptionEditor';
export * from './store';
Loading