From 96eb24dfd565538f15f171c46e59b1bc44fc4b4a Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Mon, 10 Apr 2017 19:53:04 +0300 Subject: [PATCH 01/55] feat: add handleDeleteProperty --- ashes/src/components/object-form/object-form-inner.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 59c5f220bf..11052c9dff 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -126,6 +126,16 @@ export default class ObjectFormInner extends Component { }, () => this.handleChange(fieldLabel, propertyType, value)); } + @autobind + handleDeleteProperty(name) { + const newAttributes = _.omit(this.props.attributes, name); + + this.props.onChange(newAttributes); + this.setState({ + isAddingProperty: false + }); + } + @autobind handleChange(name: string, type: string, value: any) { const { attributes } = this.props; From 8c2757e973b09b47c6de169accbef3a924f25863 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Tue, 11 Apr 2017 14:02:48 +0300 Subject: [PATCH 02/55] feat: add edit/delete property --- .../object-form/object-form-inner.jsx | 55 ++++++++++++++++++- .../components/products/custom-property.jsx | 8 +++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 11052c9dff..bf8619a187 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -78,7 +78,11 @@ function guessType(value: any): string { export default class ObjectFormInner extends Component { props: Props; - state: State = { isAddingProperty: false, errors: {} }; + state: State = { + isAddingProperty: false, + isEditingProperty: false, + errors: {} + }; get addCustomProperty() { if (this.props.canAddProperty) { @@ -104,6 +108,22 @@ export default class ObjectFormInner extends Component { /> ); } + + if (this.state.isEditingProperty) { + const property = { + name: this.state.name, + type: this.state.type, + }; + + return ( + this.setState({ isEditingProperty: false })} + /> + ); + } } @autobind @@ -126,13 +146,42 @@ export default class ObjectFormInner extends Component { }, () => this.handleChange(fieldLabel, propertyType, value)); } + @autobind + handleEditProperty(property) { + const { attributes } = this.props; + const { name, value } = this.state; + const { fieldLabel, propertyType } = property; + + const preparedObject = _.omit(attributes, name); + const newAttributes = { + ...preparedObject, + [fieldLabel]: { + t: propertyType, + v: value, + } + }; + + this.setState({ + isEditingProperty: false, + name: '', + type: '', + value: '' + }, this.props.onChange(newAttributes)); + } + @autobind handleDeleteProperty(name) { const newAttributes = _.omit(this.props.attributes, name); + this.setState({ isAddingProperty: false }, this.props.onChange(newAttributes)); + } - this.props.onChange(newAttributes); + @autobind + onEdit(name, type, value) { this.setState({ - isAddingProperty: false + isEditingProperty: true, + name, + type, + value }); } diff --git a/ashes/src/components/products/custom-property.jsx b/ashes/src/components/products/custom-property.jsx index 0d8f68d536..398752a691 100644 --- a/ashes/src/components/products/custom-property.jsx +++ b/ashes/src/components/products/custom-property.jsx @@ -20,6 +20,7 @@ const propertyTypes = { date: 'Date', price: 'Price', bool: 'Yes/No', + color: 'Color', }; type Props = { @@ -49,6 +50,13 @@ class CustomProperty extends Component { if (fieldLabelInput) { fieldLabelInput.focus(); } + + if (this.props.property) { + this.setState({ + fieldLabel: this.props.property.name, + propertyType: this.props.property.type, + }) + } } get closeAction(): Element<*> { From 713b6939b980b2eb2480f6131c12378a4a88b663 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Tue, 11 Apr 2017 18:49:02 +0300 Subject: [PATCH 03/55] feat: add edit/delete buttons --- .../object-form/object-form-inner.css | 9 ++ .../object-form/object-form-inner.jsx | 85 +++++++++++++------ 2 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 ashes/src/components/object-form/object-form-inner.css diff --git a/ashes/src/components/object-form/object-form-inner.css b/ashes/src/components/object-form/object-form-inner.css new file mode 100644 index 0000000000..35bbe7788c --- /dev/null +++ b/ashes/src/components/object-form/object-form-inner.css @@ -0,0 +1,9 @@ + +.controls { + position: absolute; + right: 21px; + + & > i { + margin: 0 5px; + } +} diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index bf8619a187..a078e23737 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -2,6 +2,8 @@ * @flow */ + +// libs import React, { Component } from 'react'; import _ from 'lodash'; import { autobind } from 'core-decorators'; @@ -9,6 +11,7 @@ import classNames from 'classnames'; import { stripTags } from 'lib/text-utils'; import { isDefined } from 'lib/utils'; +// components import { FormField, FormFieldError } from '../forms'; import { SliderCheckbox } from '../checkbox/checkbox'; import CurrencyInput from '../forms/currency-input'; @@ -20,6 +23,9 @@ import SwatchInput from '../forms/swatch-input'; import type { AttrSchema } from 'paragons/object'; +// style +import s from './object-form-inner.css'; + type Props = { canAddProperty?: boolean, fieldsToRender?: Array, @@ -147,7 +153,7 @@ export default class ObjectFormInner extends Component { } @autobind - handleEditProperty(property) { + handleEditProperty(property: { fieldLabel: string, propertyType: string }) { const { attributes } = this.props; const { name, value } = this.state; const { fieldLabel, propertyType } = property; @@ -211,11 +217,14 @@ export default class ObjectFormInner extends Component { renderBoolean(name: string, value: boolean, options: AttrOptions) { const onChange = () => this.handleChange(name, 'bool', !value); const sliderCheckbox = ( - +
+ {this.controlButtons(name, 'bool', value, options)} + +
); return renderFormField(name, sliderCheckbox, options); @@ -232,7 +241,12 @@ export default class ObjectFormInner extends Component { renderDate(name: string, value: string, options: AttrOptions) { const dateValue = new Date(value); const onChange = (v: Date) => this.handleChange(name, 'date', v.toISOString()); - const dateInput = ; + const dateInput = ( +
+ {this.controlButtons(name, 'string', value, options)} + +
+ ); return renderFormField(name, dateInput, options); } @@ -244,12 +258,15 @@ export default class ObjectFormInner extends Component { value: Number(value) }); const currencyInput = ( - +
+ {this.controlButtons(name, 'price', value, options)} + +
); return renderFormField(name, currencyInput, options); @@ -265,6 +282,7 @@ export default class ObjectFormInner extends Component { return (
+ {this.controlButtons(name, 'richText', value, options)} +
+ {this.controlButtons(name, 'string', value, options)} + +
); return renderFormField(name, stringInput, options); @@ -348,18 +369,32 @@ export default class ObjectFormInner extends Component { } renderColor(name: string, value: string = '', options: AttrOptions) { - const label = _.upperFirst(options.label); const onChange = v => this.handleChange(name, 'color', v); - - return ( + const colorSwatch = (
- + {this.controlButtons(name, 'color', value, options)}
); + + return renderFormField(name, colorSwatch, options); + } + + controlButtons(name, type, value, options) { + if (options.required) { return null; } + + const reservedNames = ['description', 'metatitle', 'metadescription']; + if (reservedNames.includes(name.toLowerCase())) { return null; } + + return ( +
+ this.onEdit(name, type, value)}/> + this.handleDeleteProperty(name)}/> +
+ ); } shouldComponentUpdate(nextProps: Props, nextState: State): boolean { From f83c0e49ced26fa414fbfa520b2e8cbafa7af442 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Tue, 11 Apr 2017 19:17:14 +0300 Subject: [PATCH 04/55] feat: handle value on type change --- ashes/src/components/object-form/object-form-inner.jsx | 9 +++++---- ashes/src/components/products/custom-property.jsx | 9 +++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index a078e23737..09f44b2e50 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -119,6 +119,7 @@ export default class ObjectFormInner extends Component { const property = { name: this.state.name, type: this.state.type, + value: this.state.value }; return ( @@ -153,17 +154,17 @@ export default class ObjectFormInner extends Component { } @autobind - handleEditProperty(property: { fieldLabel: string, propertyType: string }) { + handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string }) { const { attributes } = this.props; - const { name, value } = this.state; - const { fieldLabel, propertyType } = property; + const { name } = this.state; + const { fieldLabel, propertyType, fieldValue } = property; const preparedObject = _.omit(attributes, name); const newAttributes = { ...preparedObject, [fieldLabel]: { t: propertyType, - v: value, + v: fieldValue, } }; diff --git a/ashes/src/components/products/custom-property.jsx b/ashes/src/components/products/custom-property.jsx index 398752a691..c355dfffc6 100644 --- a/ashes/src/components/products/custom-property.jsx +++ b/ashes/src/components/products/custom-property.jsx @@ -42,6 +42,7 @@ class CustomProperty extends Component { this.state = { fieldLabel: '', propertyType: '', + fieldValue: '', }; } @@ -55,7 +56,8 @@ class CustomProperty extends Component { this.setState({ fieldLabel: this.props.property.name, propertyType: this.props.property.type, - }) + fieldValue: this.props.property.value, + }); } } @@ -78,7 +80,10 @@ class CustomProperty extends Component { @autobind handleUpdateType(value) { - this.setState({ propertyType: value }); + this.setState({ + propertyType: value, + fieldValue: '' + }); } @autobind From f92b260f453bf9d99b36895f1f39c30dcea7501a Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Tue, 11 Apr 2017 19:38:30 +0300 Subject: [PATCH 05/55] fix: flow errors --- .../object-form/object-form-inner.jsx | 19 +++++++++++++------ .../components/products/custom-property.jsx | 6 ++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 09f44b2e50..cf19a353bf 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -38,7 +38,11 @@ type Props = { type State = { isAddingProperty: boolean, + isEditingProperty: boolean, errors: {[id:string]: any}, + name: string, + type: string, + value: string | number }; type AttrOptions = { @@ -87,7 +91,10 @@ export default class ObjectFormInner extends Component { state: State = { isAddingProperty: false, isEditingProperty: false, - errors: {} + errors: {}, + name: '', + type: '', + value: '', }; get addCustomProperty() { @@ -154,7 +161,7 @@ export default class ObjectFormInner extends Component { } @autobind - handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string }) { + handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string | number }) { const { attributes } = this.props; const { name } = this.state; const { fieldLabel, propertyType, fieldValue } = property; @@ -177,13 +184,13 @@ export default class ObjectFormInner extends Component { } @autobind - handleDeleteProperty(name) { + handleDeleteProperty(name: string) { const newAttributes = _.omit(this.props.attributes, name); this.setState({ isAddingProperty: false }, this.props.onChange(newAttributes)); } @autobind - onEdit(name, type, value) { + onEdit(name: string, type: string, value: string | number) { this.setState({ isEditingProperty: true, name, @@ -369,7 +376,7 @@ export default class ObjectFormInner extends Component { return renderFormField(name, textInput, options); } - renderColor(name: string, value: string = '', options: AttrOptions) { + renderColor(name: string, value: any, options: AttrOptions) { const onChange = v => this.handleChange(name, 'color', v); const colorSwatch = (
@@ -384,7 +391,7 @@ export default class ObjectFormInner extends Component { return renderFormField(name, colorSwatch, options); } - controlButtons(name, type, value, options) { + controlButtons(name: string, type: string, value: any, options: AttrOptions) { if (options.required) { return null; } const reservedNames = ['description', 'metatitle', 'metadescription']; diff --git a/ashes/src/components/products/custom-property.jsx b/ashes/src/components/products/custom-property.jsx index c355dfffc6..d104b8692f 100644 --- a/ashes/src/components/products/custom-property.jsx +++ b/ashes/src/components/products/custom-property.jsx @@ -26,11 +26,17 @@ const propertyTypes = { type Props = { onSave: (state: State) => void, onCancel: () => void, + property: { + name: string, + type: string, + value: string | number, + } }; type State = { fieldLabel: string, propertyType: string, + fieldValue: string | number, }; class CustomProperty extends Component { From f8c4d31ecae723fcdcb9d0adea66a8a904d0bd25 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Tue, 11 Apr 2017 20:22:28 +0300 Subject: [PATCH 06/55] fix: disable edit/delete on default fields --- .../object-form/object-form-inner.jsx | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index cf19a353bf..61ceefb4ba 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -226,7 +226,7 @@ export default class ObjectFormInner extends Component { const onChange = () => this.handleChange(name, 'bool', !value); const sliderCheckbox = (
- {this.controlButtons(name, 'bool', value, options)} + {this.controlButtons(name, 'bool', value)} this.handleChange(name, 'date', v.toISOString()); const dateInput = (
- {this.controlButtons(name, 'string', value, options)} + {this.controlButtons(name, 'string', value)}
); @@ -267,7 +267,7 @@ export default class ObjectFormInner extends Component { }); const currencyInput = (
- {this.controlButtons(name, 'price', value, options)} + {this.controlButtons(name, 'price', value)} - {this.controlButtons(name, 'richText', value, options)} + {this.controlButtons(name, 'richText', value)} - {this.controlButtons(name, 'string', value, options)} + {this.controlButtons(name, 'string', value)} this.handleChange(name, 'color', v); const colorSwatch = (
- {this.controlButtons(name, 'color', value, options)} + {this.controlButtons(name, 'color', value)} From 3c9ba9cca035e867c2fc59df88575a94b183b01d Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Wed, 12 Apr 2017 14:41:04 +0300 Subject: [PATCH 07/55] refactor: move render controlButtons to renderedAttributes --- .../object-form/object-form-inner.jsx | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 61ceefb4ba..77058864bd 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -2,7 +2,6 @@ * @flow */ - // libs import React, { Component } from 'react'; import _ from 'lodash'; @@ -225,14 +224,11 @@ export default class ObjectFormInner extends Component { renderBoolean(name: string, value: boolean, options: AttrOptions) { const onChange = () => this.handleChange(name, 'bool', !value); const sliderCheckbox = ( -
- {this.controlButtons(name, 'bool', value)} - -
+ ); return renderFormField(name, sliderCheckbox, options); @@ -249,12 +245,7 @@ export default class ObjectFormInner extends Component { renderDate(name: string, value: string, options: AttrOptions) { const dateValue = new Date(value); const onChange = (v: Date) => this.handleChange(name, 'date', v.toISOString()); - const dateInput = ( -
- {this.controlButtons(name, 'string', value)} - -
- ); + const dateInput = ; return renderFormField(name, dateInput, options); } @@ -266,15 +257,12 @@ export default class ObjectFormInner extends Component { value: Number(value) }); const currencyInput = ( -
- {this.controlButtons(name, 'price', value)} - -
+ ); return renderFormField(name, currencyInput, options); @@ -290,7 +278,6 @@ export default class ObjectFormInner extends Component { return (
- {this.controlButtons(name, 'richText', value)} - {this.controlButtons(name, 'string', value)} - -
+ ); return renderFormField(name, stringInput, options); @@ -379,13 +363,10 @@ export default class ObjectFormInner extends Component { renderColor(name: string, value: any, options: AttrOptions) { const onChange = v => this.handleChange(name, 'color', v); const colorSwatch = ( -
- {this.controlButtons(name, 'color', value)} - -
+ ); return renderFormField(name, colorSwatch, options); @@ -463,7 +444,14 @@ export default class ObjectFormInner extends Component { const renderName = this.guessRenderName(attrSchema, attribute); const attrOptions = this.getAttrOptions(name, attrSchema); // $FlowFixMe: guessRenderName is enough - return React.cloneElement(this[renderName](name, attribute && attribute.v, attrOptions), { key: name }); + const content = React.cloneElement(this[renderName](name, attribute && attribute.v, attrOptions), { key: name }); + const controlButtons = this.controlButtons(name, attribute && attribute.t, attribute && attribute.v); + return ( +
+ {controlButtons} + {content} +
+ ); }); return ( From 0c2c4413e74c305e1f73fee3868b9f07ea0b6a79 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Wed, 12 Apr 2017 15:05:41 +0300 Subject: [PATCH 08/55] refactor: put name, type, value to 'currentEdit' --- .../object-form/object-form-inner.jsx | 42 ++++++++++--------- .../components/products/custom-property.jsx | 10 ++--- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 77058864bd..57571f8848 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -39,9 +39,11 @@ type State = { isAddingProperty: boolean, isEditingProperty: boolean, errors: {[id:string]: any}, - name: string, - type: string, - value: string | number + currentEdit: { + name: string, + type: string, + value: string | number + }, }; type AttrOptions = { @@ -91,9 +93,11 @@ export default class ObjectFormInner extends Component { isAddingProperty: false, isEditingProperty: false, errors: {}, - name: '', - type: '', - value: '', + currentEdit: { + name: '', + type: '', + value: '', + }, }; get addCustomProperty() { @@ -122,15 +126,9 @@ export default class ObjectFormInner extends Component { } if (this.state.isEditingProperty) { - const property = { - name: this.state.name, - type: this.state.type, - value: this.state.value - }; - return ( this.setState({ isEditingProperty: false })} @@ -162,7 +160,7 @@ export default class ObjectFormInner extends Component { @autobind handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string | number }) { const { attributes } = this.props; - const { name } = this.state; + const { currentEdit: { name } } = this.state; const { fieldLabel, propertyType, fieldValue } = property; const preparedObject = _.omit(attributes, name); @@ -176,9 +174,11 @@ export default class ObjectFormInner extends Component { this.setState({ isEditingProperty: false, - name: '', - type: '', - value: '' + currentEdit: { + name: '', + type: '', + value: '' + } }, this.props.onChange(newAttributes)); } @@ -192,9 +192,11 @@ export default class ObjectFormInner extends Component { onEdit(name: string, type: string, value: string | number) { this.setState({ isEditingProperty: true, - name, - type, - value + currentEdit: { + name, + type, + value + }, }); } diff --git a/ashes/src/components/products/custom-property.jsx b/ashes/src/components/products/custom-property.jsx index d104b8692f..cf95ba1c17 100644 --- a/ashes/src/components/products/custom-property.jsx +++ b/ashes/src/components/products/custom-property.jsx @@ -26,7 +26,7 @@ const propertyTypes = { type Props = { onSave: (state: State) => void, onCancel: () => void, - property: { + currentEdit: { name: string, type: string, value: string | number, @@ -58,11 +58,11 @@ class CustomProperty extends Component { fieldLabelInput.focus(); } - if (this.props.property) { + if (this.props.currentEdit) { this.setState({ - fieldLabel: this.props.property.name, - propertyType: this.props.property.type, - fieldValue: this.props.property.value, + fieldLabel: this.props.currentEdit.name, + propertyType: this.props.currentEdit.type, + fieldValue: this.props.currentEdit.value, }); } } From 8af3242b95a7624f13ea5b4397f85f478f7fe732 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Wed, 12 Apr 2017 18:03:14 +0300 Subject: [PATCH 09/55] fix: handle fieldLabel if it's not unique --- .../object-form/object-form-inner.jsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 57571f8848..26d756d716 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -145,6 +145,12 @@ export default class ObjectFormInner extends Component { @autobind handleCreateProperty(property: { fieldLabel: string, propertyType: string }) { const { fieldLabel, propertyType } = property; + + // TODO show error message, if fieldLabel is not unique + if (!this.isUnique(fieldLabel)) { + return null; + } + const value = (() => { switch(propertyType) { case('date'): return new Date().toString(); @@ -163,6 +169,11 @@ export default class ObjectFormInner extends Component { const { currentEdit: { name } } = this.state; const { fieldLabel, propertyType, fieldValue } = property; + // TODO show error message, if fieldLabel is not unique + if (!this.isUnique(fieldLabel)) { + return null; + } + const preparedObject = _.omit(attributes, name); const newAttributes = { ...preparedObject, @@ -223,6 +234,13 @@ export default class ObjectFormInner extends Component { this.props.onChange(newAttributes); } + @autobind + isUnique(fieldLabel: string) { + const reservedNames = _.keys(_.get(this.props.schema, 'properties', {})); + const unique = !reservedNames.includes(fieldLabel); + return unique; + } + renderBoolean(name: string, value: boolean, options: AttrOptions) { const onChange = () => this.handleChange(name, 'bool', !value); const sliderCheckbox = ( @@ -449,7 +467,7 @@ export default class ObjectFormInner extends Component { const content = React.cloneElement(this[renderName](name, attribute && attribute.v, attrOptions), { key: name }); const controlButtons = this.controlButtons(name, attribute && attribute.t, attribute && attribute.v); return ( -
+
{controlButtons} {content}
From 9245261887b6160179160404954e2c0ceace483a Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Fri, 14 Apr 2017 00:10:55 +0300 Subject: [PATCH 10/55] refactor: move custom properties out from object-form-inner --- .../custom-properties.css} | 2 +- .../custom-properties/custom-properties.jsx | 202 ++++++++++++++++++ .../custom-property-modal.jsx} | 4 +- .../object-form/object-form-inner.jsx | 160 +------------- .../object-page/object-details-deux.jsx | 3 +- 5 files changed, 209 insertions(+), 162 deletions(-) rename ashes/src/components/{object-form/object-form-inner.css => custom-properties/custom-properties.css} (74%) create mode 100644 ashes/src/components/custom-properties/custom-properties.jsx rename ashes/src/components/{products/custom-property.jsx => custom-properties/custom-property-modal.jsx} (96%) diff --git a/ashes/src/components/object-form/object-form-inner.css b/ashes/src/components/custom-properties/custom-properties.css similarity index 74% rename from ashes/src/components/object-form/object-form-inner.css rename to ashes/src/components/custom-properties/custom-properties.css index 35bbe7788c..71338fb553 100644 --- a/ashes/src/components/object-form/object-form-inner.css +++ b/ashes/src/components/custom-properties/custom-properties.css @@ -4,6 +4,6 @@ right: 21px; & > i { - margin: 0 5px; + margin: 0 5px; } } diff --git a/ashes/src/components/custom-properties/custom-properties.jsx b/ashes/src/components/custom-properties/custom-properties.jsx new file mode 100644 index 0000000000..f3c6e31555 --- /dev/null +++ b/ashes/src/components/custom-properties/custom-properties.jsx @@ -0,0 +1,202 @@ +/** + * @flow + */ + +// libs +import React, { Component } from 'react'; +import { autobind } from 'core-decorators'; +import _ from 'lodash'; + +// components +import ObjectFormInner from 'components/object-form/object-form-inner'; +import CustomPropertyModal from './custom-property-modal'; + +// style +import s from './custom-properties.css'; + +export default class CustomProperties extends Component { + + props: Props; + state: State = { + isAddingProperty: false, + isEditingProperty: false, + errors: {}, + currentEdit: { + name: '', + type: '', + value: '', + }, + }; + + get customPropertyForm() { + if (this.state.isAddingProperty) { + return ( + this.setState({ isAddingProperty: false })} + /> + ); + } + + if (this.state.isEditingProperty) { + return ( + this.setState({ isAddingProperty: false })} + /> + ); + } + } + + @autobind + controlButtons(name: string, type: string, value: any) { + const defaultProperties = _.keys(_.get(this.props.schema, 'properties', {})); + if (defaultProperties.includes(name)) { return null; } + + return ( +
+ this.onEdit(name, type, value)}/> + this.handleDeleteProperty(name)}/> +
+ ); + } + + @autobind + handleCreateProperty(property: { fieldLabel: string, propertyType: string }) { + const { fieldLabel, propertyType } = property; + const label = fieldLabel.toLowerCase(); + // TODO show error message, if fieldLabel is not unique + if (!this.isUnique(label)) { + return null; + } + + const value = (() => { + switch(propertyType) { + case('date'): return new Date().toString(); + case('bool'): return false; + default: return ''; + } + })(); + this.setState({ + isAddingProperty: false + }, () => this.handleChange(label, propertyType, value)); + } + + @autobind + handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string | number }) { + const { attributes } = this.props; + const { currentEdit: { name } } = this.state; + const { fieldLabel, propertyType, fieldValue } = property; + + // TODO show error message, if fieldLabel is not unique + if (!this.isUnique(fieldLabel)) { + return null; + } + + const preparedObject = _.omit(attributes, name); + const newAttributes = { + ...preparedObject, + [fieldLabel]: { + t: propertyType, + v: fieldValue, + } + }; + + this.setState({ + isEditingProperty: false, + currentEdit: { + name: '', + type: '', + value: '' + } + }, this.props.onChange(newAttributes)); + } + + @autobind + handleDeleteProperty(name: string) { + const newAttributes = _.omit(this.props.attributes, name); + this.setState({ isAddingProperty: false }, this.props.onChange(newAttributes)); + } + + @autobind + handleChange(name: string, type: string, value: any) { + const { attributes } = this.props; + const newAttributes = { + ...attributes, + [name]: { + t: type, + v: value, + } + }; + + this.props.onChange(newAttributes); + } + + @autobind + isUnique(fieldLabel: string) { + const reservedNames = _.keys(_.get(this.props, 'attributes', {})); + const unique = !reservedNames.includes(fieldLabel); + return unique; + } + + @autobind + processAttr(content, name, type, value) { + return ( +
+ {this.controlButtons(name, type, value)} + {content} +
+ ); + } + + @autobind + handleAddProperty() { + this.setState({ isAddingProperty: true }); + } + + @autobind + onEdit(name: string, type: string, value: string | number) { + this.setState({ + isEditingProperty: true, + currentEdit: { + name, + type, + value + }, + }); + } + + get addCustomProperty() { + if (this.props.canAddProperty) { + return ( +
+ Custom Property + + + +
+ ); + } + } + + render() { + return ( +
+ + {this.addCustomProperty} + {this.customPropertyForm} +
+ ) + } +} diff --git a/ashes/src/components/products/custom-property.jsx b/ashes/src/components/custom-properties/custom-property-modal.jsx similarity index 96% rename from ashes/src/components/products/custom-property.jsx rename to ashes/src/components/custom-properties/custom-property-modal.jsx index cf95ba1c17..7681616052 100644 --- a/ashes/src/components/products/custom-property.jsx +++ b/ashes/src/components/custom-properties/custom-property-modal.jsx @@ -39,7 +39,7 @@ type State = { fieldValue: string | number, }; -class CustomProperty extends Component { +class CustomPropertyModal extends Component { props: Props; state: State; @@ -148,4 +148,4 @@ class CustomProperty extends Component { } } -export default wrapModal(CustomProperty); +export default wrapModal(CustomPropertyModal); diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 26d756d716..2994e703b4 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -14,7 +14,6 @@ import { isDefined } from 'lib/utils'; import { FormField, FormFieldError } from '../forms'; import { SliderCheckbox } from '../checkbox/checkbox'; import CurrencyInput from '../forms/currency-input'; -import CustomProperty from '../products/custom-property'; import DatePicker from '../datepicker/datepicker'; import RichTextEditor from '../rich-text-editor/rich-text-editor'; import { Dropdown } from '../dropdown'; @@ -22,9 +21,6 @@ import SwatchInput from '../forms/swatch-input'; import type { AttrSchema } from 'paragons/object'; -// style -import s from './object-form-inner.css'; - type Props = { canAddProperty?: boolean, fieldsToRender?: Array, @@ -36,14 +32,7 @@ type Props = { }; type State = { - isAddingProperty: boolean, - isEditingProperty: boolean, - errors: {[id:string]: any}, - currentEdit: { - name: string, - type: string, - value: string | number - }, + errors: {[id:string]: any} }; type AttrOptions = { @@ -90,127 +79,9 @@ function guessType(value: any): string { export default class ObjectFormInner extends Component { props: Props; state: State = { - isAddingProperty: false, - isEditingProperty: false, errors: {}, - currentEdit: { - name: '', - type: '', - value: '', - }, }; - get addCustomProperty() { - if (this.props.canAddProperty) { - return ( -
- Custom Property - - - -
- ); - } - } - - get customPropertyForm() { - if (this.state.isAddingProperty) { - return ( - this.setState({ isAddingProperty: false })} - /> - ); - } - - if (this.state.isEditingProperty) { - return ( - this.setState({ isEditingProperty: false })} - /> - ); - } - } - - @autobind - handleAddProperty() { - this.setState({ isAddingProperty: true }); - } - - @autobind - handleCreateProperty(property: { fieldLabel: string, propertyType: string }) { - const { fieldLabel, propertyType } = property; - - // TODO show error message, if fieldLabel is not unique - if (!this.isUnique(fieldLabel)) { - return null; - } - - const value = (() => { - switch(propertyType) { - case('date'): return new Date().toString(); - case('bool'): return false; - default: return ''; - } - })(); - this.setState({ - isAddingProperty: false - }, () => this.handleChange(fieldLabel, propertyType, value)); - } - - @autobind - handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string | number }) { - const { attributes } = this.props; - const { currentEdit: { name } } = this.state; - const { fieldLabel, propertyType, fieldValue } = property; - - // TODO show error message, if fieldLabel is not unique - if (!this.isUnique(fieldLabel)) { - return null; - } - - const preparedObject = _.omit(attributes, name); - const newAttributes = { - ...preparedObject, - [fieldLabel]: { - t: propertyType, - v: fieldValue, - } - }; - - this.setState({ - isEditingProperty: false, - currentEdit: { - name: '', - type: '', - value: '' - } - }, this.props.onChange(newAttributes)); - } - - @autobind - handleDeleteProperty(name: string) { - const newAttributes = _.omit(this.props.attributes, name); - this.setState({ isAddingProperty: false }, this.props.onChange(newAttributes)); - } - - @autobind - onEdit(name: string, type: string, value: string | number) { - this.setState({ - isEditingProperty: true, - currentEdit: { - name, - type, - value - }, - }); - } - @autobind handleChange(name: string, type: string, value: any) { const { attributes } = this.props; @@ -234,13 +105,6 @@ export default class ObjectFormInner extends Component { this.props.onChange(newAttributes); } - @autobind - isUnique(fieldLabel: string) { - const reservedNames = _.keys(_.get(this.props.schema, 'properties', {})); - const unique = !reservedNames.includes(fieldLabel); - return unique; - } - renderBoolean(name: string, value: boolean, options: AttrOptions) { const onChange = () => this.handleChange(name, 'bool', !value); const sliderCheckbox = ( @@ -392,18 +256,6 @@ export default class ObjectFormInner extends Component { return renderFormField(name, colorSwatch, options); } - controlButtons(name: string, type: string, value: any) { - const defaultProperties = _.keys(_.get(this.props.schema, 'properties', {})); - if (defaultProperties.includes(name)) { return null; } - - return ( -
- this.onEdit(name, type, value)}/> - this.handleDeleteProperty(name)}/> -
- ); - } - shouldComponentUpdate(nextProps: Props, nextState: State): boolean { const attributesChanged = !_.eq(this.props.attributes, nextProps.attributes); const stateChanged = !_.eq(this.state, nextState); @@ -465,20 +317,12 @@ export default class ObjectFormInner extends Component { const attrOptions = this.getAttrOptions(name, attrSchema); // $FlowFixMe: guessRenderName is enough const content = React.cloneElement(this[renderName](name, attribute && attribute.v, attrOptions), { key: name }); - const controlButtons = this.controlButtons(name, attribute && attribute.t, attribute && attribute.v); - return ( -
- {controlButtons} - {content} -
- ); + return this.props.processAttr(content, name, attribute && attribute.t, attribute && attribute.v) }); return (
{renderedAttributes} - {this.addCustomProperty} - {this.customPropertyForm}
); } diff --git a/ashes/src/components/object-page/object-details-deux.jsx b/ashes/src/components/object-page/object-details-deux.jsx index 7939ea44f6..42d1309a08 100644 --- a/ashes/src/components/object-page/object-details-deux.jsx +++ b/ashes/src/components/object-page/object-details-deux.jsx @@ -18,6 +18,7 @@ import ObjectFormInner from 'components/object-form/object-form-inner'; import ObjectScheduler from 'components/object-scheduler/object-scheduler'; import Tags from 'components/tags/tags'; import ParticipantsPanel from 'components/participants'; +import CustomProperties from 'components/custom-properties/custom-properties'; import styles from './object-details.css'; @@ -79,7 +80,7 @@ export default class ObjectDetailsDeux extends Component { const attrsSchema = this.schema.properties.attributes; return ( - Date: Fri, 14 Apr 2017 01:04:40 +0300 Subject: [PATCH 11/55] refactor: pass props through custom properties --- .../custom-properties/custom-properties.jsx | 27 +++++++------------ .../object-page/object-details-deux.jsx | 4 ++- .../components/object-page/object-details.jsx | 17 +++++++----- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/ashes/src/components/custom-properties/custom-properties.jsx b/ashes/src/components/custom-properties/custom-properties.jsx index f3c6e31555..f8c9d25194 100644 --- a/ashes/src/components/custom-properties/custom-properties.jsx +++ b/ashes/src/components/custom-properties/custom-properties.jsx @@ -8,14 +8,12 @@ import { autobind } from 'core-decorators'; import _ from 'lodash'; // components -import ObjectFormInner from 'components/object-form/object-form-inner'; import CustomPropertyModal from './custom-property-modal'; // style import s from './custom-properties.css'; export default class CustomProperties extends Component { - props: Props; state: State = { isAddingProperty: false, @@ -45,7 +43,7 @@ export default class CustomProperties extends Component { isVisible={true} currentEdit={this.state.currentEdit} onSave={this.handleEditProperty} - onCancel={() => this.setState({ isAddingProperty: false })} + onCancel={() => this.setState({ isEditingProperty: false })} /> ); } @@ -91,11 +89,6 @@ export default class CustomProperties extends Component { const { currentEdit: { name } } = this.state; const { fieldLabel, propertyType, fieldValue } = property; - // TODO show error message, if fieldLabel is not unique - if (!this.isUnique(fieldLabel)) { - return null; - } - const preparedObject = _.omit(attributes, name); const newAttributes = { ...preparedObject, @@ -183,20 +176,20 @@ export default class CustomProperties extends Component { } } + get children() { + return React.cloneElement((this.props.children), { + ...this.props, + processAttr: this.processAttr + }); + } + render() { return (
- + {this.children} {this.addCustomProperty} {this.customPropertyForm}
- ) + ); } } diff --git a/ashes/src/components/object-page/object-details-deux.jsx b/ashes/src/components/object-page/object-details-deux.jsx index 42d1309a08..a299de0a60 100644 --- a/ashes/src/components/object-page/object-details-deux.jsx +++ b/ashes/src/components/object-page/object-details-deux.jsx @@ -86,7 +86,9 @@ export default class ObjectDetailsDeux extends Component { fieldsToRender={fieldsToRender} attributes={this.attributes} schema={attrsSchema} - /> + > + + ); } diff --git a/ashes/src/components/object-page/object-details.jsx b/ashes/src/components/object-page/object-details.jsx index a459e5c2e3..c7c6393a0f 100644 --- a/ashes/src/components/object-page/object-details.jsx +++ b/ashes/src/components/object-page/object-details.jsx @@ -17,6 +17,7 @@ import ObjectScheduler from '../object-scheduler/object-scheduler'; import { Form } from '../forms'; import Tags from '../tags/tags'; import ParticipantsPanel from '../participants'; +import CustomProperties from 'components/custom-properties/custom-properties'; type Layout = { content: Array, @@ -109,13 +110,15 @@ export default class ObjectDetails extends Component { const attrsSchema = this.schema.properties.attributes; return ( - + + + ); } From 70fb53b749206da048bf7e38b490042871c82010 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Fri, 14 Apr 2017 13:30:57 +0300 Subject: [PATCH 12/55] fix: handle to date/boolean type change --- .../custom-properties/custom-property-modal.jsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ashes/src/components/custom-properties/custom-property-modal.jsx b/ashes/src/components/custom-properties/custom-property-modal.jsx index 7681616052..afbb0ad454 100644 --- a/ashes/src/components/custom-properties/custom-property-modal.jsx +++ b/ashes/src/components/custom-properties/custom-property-modal.jsx @@ -86,9 +86,17 @@ class CustomPropertyModal extends Component { @autobind handleUpdateType(value) { + const fieldValue = (() => { + switch(value) { + case('date'): return new Date().toString(); + case('bool'): return false; + default: return ''; + } + })(); + this.setState({ propertyType: value, - fieldValue: '' + fieldValue: fieldValue }); } From 23d4daaa0768970e60ebb2e311108c50b55f4038 Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Fri, 14 Apr 2017 13:36:02 +0300 Subject: [PATCH 13/55] fix: flow errors --- .../custom-properties/custom-properties.jsx | 38 +++++++++++++------ .../custom-property-modal.jsx | 4 +- .../object-form/object-form-inner.jsx | 9 ++++- .../object-page/object-details-deux.jsx | 22 +++++++---- .../components/object-page/object-details.jsx | 26 ++++++++----- 5 files changed, 68 insertions(+), 31 deletions(-) diff --git a/ashes/src/components/custom-properties/custom-properties.jsx b/ashes/src/components/custom-properties/custom-properties.jsx index f8c9d25194..fb6b15f8f8 100644 --- a/ashes/src/components/custom-properties/custom-properties.jsx +++ b/ashes/src/components/custom-properties/custom-properties.jsx @@ -1,9 +1,7 @@ -/** - * @flow - */ +// @flow // libs -import React, { Component } from 'react'; +import React, { Component, Element } from 'react'; import { autobind } from 'core-decorators'; import _ from 'lodash'; @@ -13,6 +11,25 @@ import CustomPropertyModal from './custom-property-modal'; // style import s from './custom-properties.css'; +type Props = { + canAddProperty?: boolean, + attributes: Attributes, + onChange: (attributes: Attributes) => void, + schema?: Object, + children: Element<*> +}; + +type State = { + isAddingProperty: boolean, + isEditingProperty: boolean, + errors: {[id:string]: any}, + currentEdit: { + name: string, + type: string, + value: any, + }, +} + export default class CustomProperties extends Component { props: Props; state: State = { @@ -84,7 +101,7 @@ export default class CustomProperties extends Component { } @autobind - handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: string | number }) { + handleEditProperty(property: { fieldLabel: string, propertyType: string, fieldValue: any }) { const { attributes } = this.props; const { currentEdit: { name } } = this.state; const { fieldLabel, propertyType, fieldValue } = property; @@ -136,7 +153,7 @@ export default class CustomProperties extends Component { } @autobind - processAttr(content, name, type, value) { + processAttr(content: Element<*>, name: string, type: string, value: any) { return (
{this.controlButtons(name, type, value)} @@ -151,7 +168,7 @@ export default class CustomProperties extends Component { } @autobind - onEdit(name: string, type: string, value: string | number) { + onEdit(name: string, type: string, value: any) { this.setState({ isEditingProperty: true, currentEdit: { @@ -176,11 +193,8 @@ export default class CustomProperties extends Component { } } - get children() { - return React.cloneElement((this.props.children), { - ...this.props, - processAttr: this.processAttr - }); + get children(): Element<*> { + return React.cloneElement((this.props.children), { processAttr: this.processAttr }); } render() { diff --git a/ashes/src/components/custom-properties/custom-property-modal.jsx b/ashes/src/components/custom-properties/custom-property-modal.jsx index afbb0ad454..c1f80e67ad 100644 --- a/ashes/src/components/custom-properties/custom-property-modal.jsx +++ b/ashes/src/components/custom-properties/custom-property-modal.jsx @@ -29,14 +29,14 @@ type Props = { currentEdit: { name: string, type: string, - value: string | number, + value: any, } }; type State = { fieldLabel: string, propertyType: string, - fieldValue: string | number, + fieldValue: any, }; class CustomPropertyModal extends Component { diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 2994e703b4..32eee3b71e 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -29,6 +29,7 @@ type Props = { onChange: (attributes: Attributes) => void, schema?: Object, className?: string, + processAttr?: Function, }; type State = { @@ -317,7 +318,13 @@ export default class ObjectFormInner extends Component { const attrOptions = this.getAttrOptions(name, attrSchema); // $FlowFixMe: guessRenderName is enough const content = React.cloneElement(this[renderName](name, attribute && attribute.v, attrOptions), { key: name }); - return this.props.processAttr(content, name, attribute && attribute.t, attribute && attribute.v) + + if (this.props.processAttr) { + return this.props.processAttr(content, name, attribute && attribute.t, attribute && attribute.v); + } + + return content; + }); return ( diff --git a/ashes/src/components/object-page/object-details-deux.jsx b/ashes/src/components/object-page/object-details-deux.jsx index a299de0a60..d3f8bde351 100644 --- a/ashes/src/components/object-page/object-details-deux.jsx +++ b/ashes/src/components/object-page/object-details-deux.jsx @@ -78,17 +78,25 @@ export default class ObjectDetailsDeux extends Component { renderFields(fields: Fields, section: Array) { const fieldsToRender = this.calcFieldsToRender(fields, section); const attrsSchema = this.schema.properties.attributes; + const commonProps = { + onChange: this.handleObjectChange, + attributes: this.attributes, + schema: attrsSchema + }; + + const children = ( + + ); return ( - - + children={children} + /> ); } diff --git a/ashes/src/components/object-page/object-details.jsx b/ashes/src/components/object-page/object-details.jsx index c7c6393a0f..321e44cfbc 100644 --- a/ashes/src/components/object-page/object-details.jsx +++ b/ashes/src/components/object-page/object-details.jsx @@ -108,17 +108,25 @@ export default class ObjectDetails extends Component { renderFields(fields: Fields, section: Array): Element<*> { const fieldsToRender = this.calcFieldsToRender(fields, section); const attrsSchema = this.schema.properties.attributes; + const commonProps = { + onChange: this.handleObjectChange, + attributes: this.attributes, + schema: attrsSchema + }; + + const children = ( + + ); return ( - - - + ); } From 661a955c34527ca96cb1a1dd9346f48ea189187c Mon Sep 17 00:00:00 2001 From: "tony.pizzicato" Date: Mon, 17 Apr 2017 22:00:39 +0300 Subject: [PATCH 14/55] spike on extracted render methods --- .../object-form/object-form-inner.jsx | 55 ++++--------------- .../components/object-form/renderers/index.js | 7 +++ .../object-form/renderers/richText.jsx | 34 ++++++++++++ .../object-form/renderers/string.jsx | 27 +++++++++ 4 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 ashes/src/components/object-form/renderers/index.js create mode 100644 ashes/src/components/object-form/renderers/richText.jsx create mode 100644 ashes/src/components/object-form/renderers/string.jsx diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 32eee3b71e..32df316eab 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -10,6 +10,8 @@ import classNames from 'classnames'; import { stripTags } from 'lib/text-utils'; import { isDefined } from 'lib/utils'; +import * as renderers from './renderers'; + // components import { FormField, FormFieldError } from '../forms'; import { SliderCheckbox } from '../checkbox/checkbox'; @@ -33,7 +35,7 @@ type Props = { }; type State = { - errors: {[id:string]: any} + errors: { [id: string]: any } }; type AttrOptions = { @@ -108,6 +110,7 @@ export default class ObjectFormInner extends Component { renderBoolean(name: string, value: boolean, options: AttrOptions) { const onChange = () => this.handleChange(name, 'bool', !value); + const sliderCheckbox = ( this.handleChange(name, 'richText', v); - const error = _.get(this.state, ['errors', name]); - const classForContainer = classNames('fc-object-form__field', { - '_has-error': error != null, - }); - const nameVal = _.kebabCase(name); - - return ( -
- - {error && } -
- ); - } - - renderString(name: string, value: string = '', options: AttrOptions) { - const onChange = ({target}) => { - return this.handleChange(name, 'string', target.value); - }; - const stringInput = ( - - ); - - return renderFormField(name, stringInput, options); - } - renderNumber(name: string, value: ?number = null, options: AttrOptions) { - const onChange = ({target}) => { + const onChange = ({ target }) => { return this.handleChange(name, 'number', target.value == '' ? null : Number(target.value)); }; const stringInput = ( @@ -230,7 +194,7 @@ export default class ObjectFormInner extends Component { } renderText(name: string, value: string = '', options: AttrOptions) { - const onChange = ({target}) => { + const onChange = ({ target }) => { return this.handleChange(name, 'text', target.value); }; const textInput = ( @@ -317,7 +281,12 @@ export default class ObjectFormInner extends Component { const renderName = this.guessRenderName(attrSchema, attribute); const attrOptions = this.getAttrOptions(name, attrSchema); // $FlowFixMe: guessRenderName is enough - const content = React.cloneElement(this[renderName](name, attribute && attribute.v, attrOptions), { key: name }); + + const renderFn = renderers[renderName] ? + renderers[renderName](this.state, this.handleChange) : + this[renderName].bind(this); + + const content = React.cloneElement(renderFn(name, attribute && attribute.v, attrOptions), { key: name }); if (this.props.processAttr) { return this.props.processAttr(content, name, attribute && attribute.t, attribute && attribute.v); diff --git a/ashes/src/components/object-form/renderers/index.js b/ashes/src/components/object-form/renderers/index.js new file mode 100644 index 0000000000..4a0675a6cd --- /dev/null +++ b/ashes/src/components/object-form/renderers/index.js @@ -0,0 +1,7 @@ +import renderString from './string'; +import renderRichText from './richText'; + +export { + renderString, + renderRichText, +}; diff --git a/ashes/src/components/object-form/renderers/richText.jsx b/ashes/src/components/object-form/renderers/richText.jsx new file mode 100644 index 0000000000..f91722f703 --- /dev/null +++ b/ashes/src/components/object-form/renderers/richText.jsx @@ -0,0 +1,34 @@ +/* @flow */ + +// libs +import classNames from 'classnames'; +import { get, kebabCase, noop } from 'lodash'; +import React from 'react'; + +// components +import { FormFieldError } from 'components/forms'; +import RichTextEditor from 'components/rich-text-editor/rich-text-editor'; + +export default function renderRichText(state: Object, onChange: Function = noop) { + return function (name: string, value: string = '', options: AttrOptions) { + const handler = v => onChange(name, 'richText', v); + + const error = get(state, ['errors', name]); + const classForContainer = classNames('fc-object-form__field', { + '_has-error': error != null, + }); + const nameVal = kebabCase(name); + + return ( +
+ + {error && } +
+ ); + }; +} diff --git a/ashes/src/components/object-form/renderers/string.jsx b/ashes/src/components/object-form/renderers/string.jsx new file mode 100644 index 0000000000..31c3d48669 --- /dev/null +++ b/ashes/src/components/object-form/renderers/string.jsx @@ -0,0 +1,27 @@ +import noop from 'lodash/noop'; +import React from 'react'; + +import { renderFormField } from '../object-form-inner'; + +const inputClass = 'fc-object-form__field-value'; + +export default function renderString(state: Object, onChange: Function = noop) { + return function (name: string, value: string = '', options: AttrOptions) { + const handler = ({ target }) => { + return onChange(name, 'string', target.value); + }; + + const stringInput = ( + + ); + + return renderFormField(name, stringInput, options); + }; +} From a271e8f225ef76f1c60614159701a98833be293f Mon Sep 17 00:00:00 2001 From: Bad_Company Date: Tue, 18 Apr 2017 01:15:53 +0300 Subject: [PATCH 15/55] refactor: move renderers out of object-form-inner to /renderers --- .../object-form/object-form-inner.jsx | 152 +----------------- .../components/object-form/renderers/bool.jsx | 7 + .../object-form/renderers/boolean.jsx | 27 ++++ .../object-form/renderers/color.jsx | 23 +++ .../components/object-form/renderers/date.jsx | 21 +++ .../object-form/renderers/element.jsx | 7 + .../object-form/renderers/form-field.jsx | 18 +++ .../components/object-form/renderers/index.js | 18 +++ .../object-form/renderers/number.jsx | 27 ++++ .../object-form/renderers/options.jsx | 31 ++++ .../object-form/renderers/price.jsx | 32 ++++ .../object-form/renderers/string.jsx | 10 +- .../components/object-form/renderers/text.jsx | 26 +++ .../src/components/products/product-form.jsx | 2 +- 14 files changed, 245 insertions(+), 156 deletions(-) create mode 100644 ashes/src/components/object-form/renderers/bool.jsx create mode 100644 ashes/src/components/object-form/renderers/boolean.jsx create mode 100644 ashes/src/components/object-form/renderers/color.jsx create mode 100644 ashes/src/components/object-form/renderers/date.jsx create mode 100644 ashes/src/components/object-form/renderers/element.jsx create mode 100644 ashes/src/components/object-form/renderers/form-field.jsx create mode 100644 ashes/src/components/object-form/renderers/number.jsx create mode 100644 ashes/src/components/object-form/renderers/options.jsx create mode 100644 ashes/src/components/object-form/renderers/price.jsx create mode 100644 ashes/src/components/object-form/renderers/text.jsx diff --git a/ashes/src/components/object-form/object-form-inner.jsx b/ashes/src/components/object-form/object-form-inner.jsx index 32df316eab..36f48b56a8 100644 --- a/ashes/src/components/object-form/object-form-inner.jsx +++ b/ashes/src/components/object-form/object-form-inner.jsx @@ -12,15 +12,6 @@ import { isDefined } from 'lib/utils'; import * as renderers from './renderers'; -// components -import { FormField, FormFieldError } from '../forms'; -import { SliderCheckbox } from '../checkbox/checkbox'; -import CurrencyInput from '../forms/currency-input'; -import DatePicker from '../datepicker/datepicker'; -import RichTextEditor from '../rich-text-editor/rich-text-editor'; -import { Dropdown } from '../dropdown'; -import SwatchInput from '../forms/swatch-input'; - import type { AttrSchema } from 'paragons/object'; type Props = { @@ -45,40 +36,12 @@ type AttrOptions = { disabled?: boolean, }; -const inputClass = 'fc-object-form__field-value'; - function formatLabel(label: string): string { return _.snakeCase(label).split('_').reduce((res, val) => { return `${res} ${_.capitalize(val)}`; }); } -// TODO: fix content type -export function renderFormField(name: string, content: any, options: AttrOptions) { - return ( - - {content} - - ); -} - -function guessType(value: any): string { - const typeOf = typeof value; - switch (typeOf) { - case 'string': - case 'number': - case 'boolean': - return typeOf; - default: - return 'string'; - } -} - export default class ObjectFormInner extends Component { props: Props; state: State = { @@ -108,119 +71,6 @@ export default class ObjectFormInner extends Component { this.props.onChange(newAttributes); } - renderBoolean(name: string, value: boolean, options: AttrOptions) { - const onChange = () => this.handleChange(name, 'bool', !value); - - const sliderCheckbox = ( - - ); - - return renderFormField(name, sliderCheckbox, options); - } - - renderBool(...args: Array) { - return this.renderBoolean(...args); - } - - renderElement(name: string, value: any, options: AttrOptions) { - return renderFormField(name, value, options); - } - - renderDate(name: string, value: string, options: AttrOptions) { - const dateValue = new Date(value); - const onChange = (v: Date) => this.handleChange(name, 'date', v.toISOString()); - const dateInput = ; - return renderFormField(name, dateInput, options); - } - - renderPrice(name: string, value: any, options: AttrOptions) { - const priceValue: string = _.get(value, 'value', ''); - const priceCurrency: string = _.get(value, 'currency', 'USD'); - const onChange = value => this.handleChange(name, 'price', { - currency: priceCurrency, - value: Number(value) - }); - const currencyInput = ( - - ); - - return renderFormField(name, currencyInput, options); - } - - renderNumber(name: string, value: ?number = null, options: AttrOptions) { - const onChange = ({ target }) => { - return this.handleChange(name, 'number', target.value == '' ? null : Number(target.value)); - }; - const stringInput = ( - - ); - - return renderFormField(name, stringInput, options); - } - - renderOptions(name: string, value: any, options: AttrOptions) { - const fieldOptions = this.props.fieldsOptions && this.props.fieldsOptions[name]; - if (!fieldOptions) throw new Error('You must define fieldOptions for options fields'); - - const onChange = v => this.handleChange(name, 'options', v); - const error = _.get(this.state, ['errors', name]); - - return ( -
-
{options.label}
- - {error && } -
- ); - } - - renderText(name: string, value: string = '', options: AttrOptions) { - const onChange = ({ target }) => { - return this.handleChange(name, 'text', target.value); - }; - const textInput = ( -