diff --git a/package-lock.json b/package-lock.json index e81c9aac..788b1c74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-form-builder2", - "version": "0.5.14", + "version": "0.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5205066c..2996023f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-form-builder2", - "version": "0.5.14", + "version": "0.6.0", "description": "A complete form builder for react.", "main": "lib/index.js", "license": "MIT", diff --git a/src/form-elements/component-header.jsx b/src/form-elements/component-header.jsx new file mode 100644 index 00000000..54d2412f --- /dev/null +++ b/src/form-elements/component-header.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import HeaderBar from './header-bar'; + +const ComponentHeader = (props) => { + if (props.mutable) { + return null; + } + return ( +
+ { props.data.pageBreakBefore && +
Page Break
+ } + +
+ ); +}; + +export default ComponentHeader; diff --git a/src/form-elements/component-label.jsx b/src/form-elements/component-label.jsx new file mode 100644 index 00000000..25065ef7 --- /dev/null +++ b/src/form-elements/component-label.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import myxss from './myxss'; + +const ComponentLabel = (props) => { + const hasRequiredLabel = (props.data.hasOwnProperty('required') && props.data.required === true && !props.read_only); + + return ( + + ); +}; + +export default ComponentLabel; diff --git a/src/form-elements/date-picker.jsx b/src/form-elements/date-picker.jsx new file mode 100644 index 00000000..9f7e440a --- /dev/null +++ b/src/form-elements/date-picker.jsx @@ -0,0 +1,156 @@ +import React from 'react'; +import { format, parse } from 'date-fns'; +import ReactDatePicker from 'react-datepicker'; +import ComponentHeader from './component-header'; +import ComponentLabel from './component-label'; + +class DatePicker extends React.Component { + constructor(props) { + super(props); + this.inputField = React.createRef(); + + const { formatMask } = DatePicker.updateFormat(props, null); + this.state = DatePicker.updateDateTime(props, { formatMask }, formatMask); + } + + // formatMask = ''; + + handleChange = (dt) => { + let placeholder; + const { formatMask } = this.state; + if (dt && dt.target) { + placeholder = (dt && dt.target && dt.target.value === '') ? formatMask.toLowerCase() : ''; + const formattedDate = (dt.target.value) ? format(dt.target.value, formatMask) : ''; + this.setState({ + value: formattedDate, + internalValue: formattedDate, + placeholder, + }); + } else { + this.setState({ + value: (dt) ? format(dt, formatMask) : '', + internalValue: dt, + placeholder, + }); + } + }; + + static updateFormat(props, oldFormatMask) { + const { showTimeSelect, showTimeSelectOnly } = props.data; + const dateFormat = showTimeSelect && showTimeSelectOnly ? '' : props.data.dateFormat; + const timeFormat = showTimeSelect ? props.data.timeFormat : ''; + const formatMask = (`${dateFormat} ${timeFormat}`).trim(); + const updated = formatMask !== oldFormatMask; + + return { updated, formatMask }; + } + + static updateDateTime(props, state, formatMask) { + let value; + let internalValue; + const { defaultToday } = props.data; + if (defaultToday && (props.defaultValue === '' || props.defaultValue === undefined)) { + value = format(new Date(), formatMask); + internalValue = new Date(); + } else { + value = props.defaultValue; + + if (value === '' || value === undefined) { + internalValue = undefined; + } else { + internalValue = parse(value, state.formatMask, new Date()); + } + } + return { + value, + internalValue, + placeholder: formatMask.toLowerCase(), + defaultToday, + formatMask: state.formatMask, + }; + } + + // componentWillReceiveProps(props) { + // const formatUpdated = this.updateFormat(props); + // if ((props.data.defaultToday !== !this.state.defaultToday) || formatUpdated) { + // const state = this.updateDateTime(props, this.formatMask); + // this.setState(state); + // } + // } + + static getDerivedStateFromProps(props, state) { + const { updated, formatMask } = DatePicker.updateFormat(props, state.formatMask); + if ((props.data.defaultToday !== state.defaultToday) || updated) { + const newState = DatePicker.updateDateTime(props, state, formatMask); + return newState; + } + return null; + } + + render() { + const { showTimeSelect, showTimeSelectOnly } = this.props.data; + const props = {}; + props.type = 'date'; + props.className = 'form-control'; + props.name = this.props.data.field_name; + const readOnly = this.props.data.readOnly || this.props.read_only; + const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; + const placeholderText = this.state.formatMask.toLowerCase(); + + if (this.props.mutable) { + props.defaultValue = this.props.defaultValue; + props.ref = this.inputField; + } + + let baseClasses = 'SortableItem rfb-item'; + if (this.props.data.pageBreakBefore) { baseClasses += ' alwaysbreak'; } + + return ( +
+ +
+ +
+ { readOnly && + + } + { iOS && !readOnly && + + } + { !iOS && !readOnly && + + } +
+
+
+ ); + } +} + +export default DatePicker; diff --git a/src/header-bar.jsx b/src/form-elements/header-bar.jsx similarity index 100% rename from src/header-bar.jsx rename to src/form-elements/header-bar.jsx diff --git a/src/form-elements.jsx b/src/form-elements/index.jsx similarity index 78% rename from src/form-elements.jsx rename to src/form-elements/index.jsx index 7c31dcb0..10e45d0e 100644 --- a/src/form-elements.jsx +++ b/src/form-elements/index.jsx @@ -1,59 +1,17 @@ // eslint-disable-next-line max-classes-per-file import React from 'react'; import Select from 'react-select'; -import xss from 'xss'; -import { format, parse } from 'date-fns'; -// import moment from 'moment'; import SignaturePad from 'react-signature-canvas'; import ReactBootstrapSlider from 'react-bootstrap-slider'; -import ReactDatePicker from 'react-datepicker'; + import StarRating from './star-rating'; import HeaderBar from './header-bar'; +import DatePicker from './date-picker'; +import ComponentHeader from './component-header'; +import ComponentLabel from './component-label'; +import myxss from './myxss'; const FormElements = {}; -const myxss = new xss.FilterXSS({ - whiteList: { - u: [], - br: [], - b: [], - i: [], - ol: ['style'], - ul: ['style'], - li: [], - p: ['style'], - sub: [], - sup: [], - div: ['style'], - em: [], - strong: [], - span: ['style'], - }, -}); - -const ComponentLabel = (props) => { - const hasRequiredLabel = (props.data.hasOwnProperty('required') && props.data.required === true && !props.read_only); - - return ( - - ); -}; - -const ComponentHeader = (props) => { - if (props.mutable) { - return null; - } - return ( -
- { props.data.pageBreakBefore && -
Page Break
- } - -
- ); -}; class Header extends React.Component { render() { @@ -230,142 +188,6 @@ class TextArea extends React.Component { } } -class DatePicker extends React.Component { - constructor(props) { - super(props); - this.inputField = React.createRef(); - - this.updateFormat(props); - this.state = this.updateDateTime(props, this.formatMask); - } - - formatMask = ''; - - handleChange = (dt) => { - let placeholder; - if (dt && dt.target) { - placeholder = (dt && dt.target && dt.target.value === '') ? this.formatMask.toLowerCase() : ''; - const formattedDate = (dt.target.value) ? format(dt.target.value, this.formatMask) : ''; - this.setState({ - value: formattedDate, - internalValue: formattedDate, - placeholder, - }); - } else { - this.setState({ - value: (dt) ? format(dt, this.formatMask) : '', - internalValue: dt, - placeholder, - }); - } - }; - - updateFormat(props) { - const { showTimeSelect, showTimeSelectOnly } = props.data; - const dateFormat = showTimeSelect && showTimeSelectOnly ? '' : props.data.dateFormat; - const timeFormat = showTimeSelect ? props.data.timeFormat : ''; - const formatMask = (`${dateFormat} ${timeFormat}`).trim(); - const updated = formatMask !== this.formatMask; - this.formatMask = formatMask; - return updated; - } - - updateDateTime(props, formatMask) { - let value; - let internalValue; - const { defaultToday } = props.data; - if (defaultToday && (props.defaultValue === '' || props.defaultValue === undefined)) { - value = format(new Date(), formatMask); - internalValue = new Date(); - } else { - value = props.defaultValue; - - if (value === '' || value === undefined) { - internalValue = undefined; - } else { - internalValue = parse(value, this.formatMask, new Date()); - } - } - return { - value, - internalValue, - placeholder: formatMask.toLowerCase(), - defaultToday, - }; - } - - componentWillReceiveProps(props) { - const formatUpdated = this.updateFormat(props); - if ((props.data.defaultToday !== !this.state.defaultToday) || formatUpdated) { - const state = this.updateDateTime(props, this.formatMask); - this.setState(state); - } - } - - render() { - const { showTimeSelect, showTimeSelectOnly } = this.props.data; - const props = {}; - props.type = 'date'; - props.className = 'form-control'; - props.name = this.props.data.field_name; - const readOnly = this.props.data.readOnly || this.props.read_only; - const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; - const placeholderText = this.formatMask.toLowerCase(); - - if (this.props.mutable) { - props.defaultValue = this.props.defaultValue; - props.ref = this.inputField; - } - - let baseClasses = 'SortableItem rfb-item'; - if (this.props.data.pageBreakBefore) { baseClasses += ' alwaysbreak'; } - - return ( -
- -
- -
- { readOnly && - - } - { iOS && !readOnly && - - } - { !iOS && !readOnly && - - } -
-
-
- ); - } -} class Dropdown extends React.Component { constructor(props) { diff --git a/src/form-elements/myxss.js b/src/form-elements/myxss.js new file mode 100644 index 00000000..ba6fabf3 --- /dev/null +++ b/src/form-elements/myxss.js @@ -0,0 +1,22 @@ +import xss from 'xss'; + +const myxss = new xss.FilterXSS({ + whiteList: { + u: [], + br: [], + b: [], + i: [], + ol: ['style'], + ul: ['style'], + li: [], + p: ['style'], + sub: [], + sup: [], + div: ['style'], + em: [], + strong: [], + span: ['style'], + }, +}); + +export default myxss; diff --git a/src/star-rating.jsx b/src/form-elements/star-rating.jsx similarity index 88% rename from src/star-rating.jsx rename to src/form-elements/star-rating.jsx index f515ff59..904d2d70 100644 --- a/src/star-rating.jsx +++ b/src/form-elements/star-rating.jsx @@ -22,15 +22,21 @@ export default class StarRating extends React.Component { constructor(props) { super(props); + this.min = 0; + this.max = props.ratingAmount || 5; + + const ratingVal = props.rating; + const ratingCache = { + pos: ratingVal ? this.getStarRatingPosition(ratingVal) : 0, + rating: props.rating, + }; + this.state = { - ratingCache: { - pos: 0, - rating: 0, - }, - editing: props.editing || true, + ratingCache, + editing: props.editing || !props.rating, stars: 5, - rating: 0, - pos: 0, + rating: ratingCache.rating, + pos: ratingCache.pos, glyph: this.getStars(), }; } @@ -48,22 +54,22 @@ export default class StarRating extends React.Component { return stars; } - componentWillMount() { - this.min = 0; - this.max = this.props.ratingAmount || 5; - if (this.props.rating) { - this.state.editing = this.props.editing || false; - const ratingVal = this.props.rating; - this.state.ratingCache.pos = this.getStarRatingPosition(ratingVal); - this.state.ratingCache.rating = ratingVal; - - this.setState({ - ratingCache: this.state.ratingCache, - rating: ratingVal, - pos: this.getStarRatingPosition(ratingVal), - }); - } - } + // componentWillMount() { + // this.min = 0; + // this.max = this.props.ratingAmount || 5; + // if (this.props.rating) { + // this.state.editing = this.props.editing || false; + // const ratingVal = this.props.rating; + // this.state.ratingCache.pos = this.getStarRatingPosition(ratingVal); + // this.state.ratingCache.rating = ratingVal; + + // this.setState({ + // ratingCache: this.state.ratingCache, + // rating: ratingVal, + // pos: this.getStarRatingPosition(ratingVal), + // }); + // } + // } componentDidMount() { this.root = ReactDOM.findDOMNode(this.rootNode); diff --git a/src/form-validator.jsx b/src/form-validator.jsx index d4213236..61b9e7cf 100644 --- a/src/form-validator.jsx +++ b/src/form-validator.jsx @@ -32,7 +32,7 @@ export default class FormValidator extends React.Component { }; } - componentWillMount() { + componentDidMount() { this.subscription = this.props.emitter.addListener('formValidation', (errors) => { this.setState({ errors }); }); diff --git a/src/preview.jsx b/src/preview.jsx index f006e21d..5110a351 100644 --- a/src/preview.jsx +++ b/src/preview.jsx @@ -31,10 +31,11 @@ export default class Preview extends React.Component { this.insertCard = this.insertCard.bind(this); } - componentWillReceiveProps(nextProps) { - if (this.props.data !== nextProps.data) { - store.dispatch('updateOrder', nextProps.data); + static getDerivedStateFromProps(props, state) { + if (props.data && props.data !== state.data) { + store.dispatch('updateOrder', props.data); } + return null; } componentDidMount() {