diff --git a/packages/form-js-editor/src/FormEditor.js b/packages/form-js-editor/src/FormEditor.js index 41b64910b..c0ebafb91 100644 --- a/packages/form-js-editor/src/FormEditor.js +++ b/packages/form-js-editor/src/FormEditor.js @@ -248,13 +248,14 @@ export default class FormEditor { */ _createInjector(options, container) { const { - additionalModules = [], modules = this._getModules(), - renderer = {} + additionalModules = [], + renderer = {}, + ...config } = options; - const config = { - ...options, + const enrichedConfig = { + ...config, renderer: { ...renderer, container @@ -262,7 +263,7 @@ export default class FormEditor { }; return createInjector([ - { config: [ 'value', config ] }, + { config: [ 'value', enrichedConfig ] }, { formEditor: [ 'value', this ] }, core, ...modules, diff --git a/packages/form-js-viewer/src/Form.js b/packages/form-js-viewer/src/Form.js index c493c0389..144c0d1d4 100644 --- a/packages/form-js-viewer/src/Form.js +++ b/packages/form-js-viewer/src/Form.js @@ -335,18 +335,20 @@ export default class Form { */ _createInjector(options, container) { const { + modules = this._getModules(), additionalModules = [], - modules = this._getModules() + ...config } = options; - const config = { + const enrichedConfig = { + ...config, renderer: { container } }; return createInjector([ - { config: [ 'value', config ] }, + { config: [ 'value', enrichedConfig ] }, { form: [ 'value', this ] }, core, ...modules, diff --git a/packages/form-js-viewer/src/index.js b/packages/form-js-viewer/src/index.js index 1be1568db..97732687e 100644 --- a/packages/form-js-viewer/src/index.js +++ b/packages/form-js-viewer/src/index.js @@ -27,10 +27,10 @@ export function createForm(options) { const { data, schema, - ...rest + ...formOptions } = options; - const form = new Form(rest); + const form = new Form(formOptions); return form.importSchema(schema, data).then(function() { return form; diff --git a/packages/form-js-viewer/src/render/components/form-fields/Number.js b/packages/form-js-viewer/src/render/components/form-fields/Number.js index ad51e6601..e5d656095 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/Number.js +++ b/packages/form-js-viewer/src/render/components/form-fields/Number.js @@ -1,6 +1,8 @@ import Big from 'big.js'; import classNames from 'classnames'; + import { useCallback, useMemo, useRef, useState } from 'preact/hooks'; +import useFlushDebounce from '../../hooks/useFlushDebounce'; import Description from '../Description'; import Errors from '../Errors'; @@ -32,8 +34,7 @@ export default function Numberfield(props) { onFocus, field, value, - readonly, - onChange + readonly } = props; const { @@ -57,6 +58,19 @@ export default function Numberfield(props) { const [ stringValueCache, setStringValueCache ] = useState(''); + const [ onChangeDebounced, flushOnChange ] = useFlushDebounce((params) => { + props.onChange(params); + }, [ props.onChange ]); + + const onInputBlur = () => { + flushOnChange && flushOnChange(); + onBlur && onBlur(); + }; + + const onInputFocus = () => { + onFocus && onFocus(); + }; + // checks whether the value currently in the form data is practically different from the one in the input field cache // this allows us to guarantee the field always displays valid form data, but without auto-simplifying values like 1.000 to 1 const cacheValueMatchesState = useMemo(() => Numberfield.config.sanitizeValue({ value, formField: field }) === Numberfield.config.sanitizeValue({ value: stringValueCache, formField: field }), [ stringValueCache, value, field ]); @@ -82,7 +96,7 @@ export default function Numberfield(props) { if (isNullEquivalentValue(stringValue)) { setStringValueCache(''); - onChange({ field, value: null }); + onChangeDebounced({ field, value: null }); return; } @@ -96,14 +110,14 @@ export default function Numberfield(props) { if (isNaN(Number(stringValue))) { setStringValueCache('NaN'); - onChange({ field, value: 'NaN' }); + onChangeDebounced({ field, value: 'NaN' }); return; } setStringValueCache(stringValue); - onChange({ field, value: serializeToString ? stringValue : Number(stringValue) }); + onChangeDebounced({ field, value: serializeToString ? stringValue : Number(stringValue) }); - }, [ field, onChange, serializeToString ]); + }, [ field, onChangeDebounced, serializeToString ]); const increment = () => { if (readonly) { @@ -187,8 +201,8 @@ export default function Numberfield(props) { id={ domId } onKeyDown={ onKeyDown } onKeyPress={ onKeyPress } - onBlur={ () => onBlur && onBlur() } - onFocus={ () => onFocus && onFocus() } + onBlur={ onInputBlur } + onFocus={ onInputFocus } // @ts-ignore onInput={ (e) => setValue(e.target.value) } diff --git a/packages/form-js-viewer/src/render/components/form-fields/Textarea.js b/packages/form-js-viewer/src/render/components/form-fields/Textarea.js index 3fb396f51..3b2fdf5a0 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/Textarea.js +++ b/packages/form-js-viewer/src/render/components/form-fields/Textarea.js @@ -1,5 +1,8 @@ import { isArray, isObject, isNil } from 'min-dash'; + import { useEffect, useLayoutEffect, useRef } from 'preact/hooks'; +import useFlushDebounce from '../../hooks/useFlushDebounce'; + import { formFieldClasses } from '../Util'; import Description from '../Description'; @@ -28,14 +31,22 @@ export default function Textarea(props) { } = field; const { required } = validate; - const textareaRef = useRef(); - const onInput = ({ target }) => { + const [ onInputChange, flushOnChange ] = useFlushDebounce(({ target }) => { props.onChange({ field, value: target.value }); + }, [ props.onChange ]); + + const onInputBlur = () => { + flushOnChange && flushOnChange(); + onBlur && onBlur(); + }; + + const onInputFocus = () => { + onFocus && onFocus(); }; useLayoutEffect(() => { @@ -55,9 +66,9 @@ export default function Textarea(props) { disabled={ disabled } readonly={ readonly } id={ domId } - onInput={ onInput } - onBlur={ () => onBlur && onBlur() } - onFocus={ () => onFocus && onFocus() } + onInput={ onInputChange } + onBlur={ onInputBlur } + onFocus={ onInputFocus } value={ value } ref={ textareaRef } aria-describedby={ errorMessageId } /> diff --git a/packages/form-js-viewer/src/render/components/form-fields/Textfield.js b/packages/form-js-viewer/src/render/components/form-fields/Textfield.js index 734cb5cba..0ad91ed4b 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/Textfield.js +++ b/packages/form-js-viewer/src/render/components/form-fields/Textfield.js @@ -6,6 +6,8 @@ import Errors from '../Errors'; import Label from '../Label'; import InputAdorner from './parts/TemplatedInputAdorner'; +import useFlushDebounce from '../../hooks/useFlushDebounce'; + const type = 'textfield'; export default function Textfield(props) { @@ -35,11 +37,20 @@ export default function Textfield(props) { const { required } = validate; - const onChange = ({ target }) => { + const [ onInputChange, flushOnChange ] = useFlushDebounce(({ target }) => { props.onChange({ field, value: target.value }); + }, [ props.onChange ]); + + const onInputBlur = () => { + flushOnChange && flushOnChange(); + onBlur && onBlur(); + }; + + const onInputFocus = () => { + onFocus && onFocus(); }; return