Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Skaiir committed Apr 25, 2024
1 parent 7bf30fd commit 71c7e21
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 92 deletions.
18 changes: 5 additions & 13 deletions packages/form-js-viewer/src/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,24 +330,16 @@ export class Form {
_update(update) {
const { fieldInstance, value } = update;

const { id, valuePath, indexes } = fieldInstance;
const { valuePath } = fieldInstance;

const { data, errors } = this._getState();
const { data: _data } = this._getState();

const validator = this.get('validator');
const data = set(_data, valuePath, value);
this._setState({ data: clone(data) });

const fieldErrors = validator.validateFieldInstance(fieldInstance, value);

set(data, valuePath, value);

set(errors, [id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
this.validate();

this._emit('field.updated', update);

this._setState({
data: clone(data),
errors: clone(errors),
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { unaryTest } from 'feelin';
import { get, isString, set, values, isObject } from 'min-dash';
import { buildExpressionContext, clone } from '../../util';
import { wrapExpressionContext, clone } from '../../util';

/**
* @typedef {object} Condition
Expand Down Expand Up @@ -35,17 +35,15 @@ export class ConditionChecker {
const { conditional, components, id } = field;

// build the expression context in the right format
const localExpressionContext = buildExpressionContext({
const expressionContext = wrapExpressionContext({
this: scopeData,
data: contextData,
i: expressionIndexes,
parent: parentScopeData,
});

context.isHidden =
startHidden ||
context.isHidden ||
(conditional && this._checkHideCondition(conditional, localExpressionContext));
startHidden || context.isHidden || (conditional && this._checkHideCondition(conditional, expressionContext));

// if a field is repeatable and visible, we need to implement custom recursion on its children
if (isRepeatable && !context.isHidden) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

import { get } from 'min-dash';
import { useContext, useMemo, useRef } from 'preact/hooks';
import { LocalExpressionContext } from '../../render/context/LocalExpressionContext';
import { ExpressionContextInfo } from '../../render/context/ExpressionContextInfo';

import ExpandSvg from '../../render/components/form-fields/icons/Expand.svg';
import CollapseSvg from '../../render/components/form-fields/icons/Collapse.svg';
import AddSvg from '../../render/components/form-fields/icons/Add.svg';
import DeleteSvg from '../../render/components/form-fields/icons/Delete.svg';

import { buildExpressionContext } from '../../util';
import { useScrollIntoView } from '../../render/hooks';
import classNames from 'classnames';

Expand Down Expand Up @@ -48,8 +47,8 @@ export class RepeatRenderManager {
const { data } = this._form._getState();

const repeaterField = props.field;
const dataPath = this._pathRegistry.getValuePath(repeaterField, { indexes });
const values = get(data, dataPath) || [];
const valuePath = this._pathRegistry.getValuePath(repeaterField, { indexes });
const values = get(data, valuePath) || [];

const nonCollapsedItems = this._getNonCollapsedItems(repeaterField);
const collapseEnabled = !repeaterField.disableCollapse && values.length > nonCollapsedItems;
Expand All @@ -71,7 +70,7 @@ export class RepeatRenderManager {
});
};

const parentExpressionContextInfo = useContext(LocalExpressionContext);
const parentExpressionContextInfo = useContext(ExpressionContextInfo);

return (
<>
Expand All @@ -81,6 +80,7 @@ export class RepeatRenderManager {
itemIndex={itemIndex}
itemValue={itemValue}
parentExpressionContextInfo={parentExpressionContextInfo}
parentValuePath={valuePath}
repeaterField={repeaterField}
RowsRenderer={RowsRenderer}
indexes={indexes}
Expand Down Expand Up @@ -196,6 +196,7 @@ export class RepeatRenderManager {
* @param {number} props.itemIndex
* @param {Object} props.itemValue
* @param {Object} props.parentExpressionContextInfo
* @param {string} props.parentValuePath
* @param {Object} props.repeaterField
* @param {Function} props.RowsRenderer
* @param {Object} props.indexes
Expand All @@ -208,6 +209,7 @@ const RepetitionScaffold = (props) => {
itemIndex,
itemValue,
parentExpressionContextInfo,
parentValuePath,
repeaterField,
RowsRenderer,
indexes,
Expand All @@ -224,26 +226,30 @@ const RepetitionScaffold = (props) => {
[itemIndex, indexes, repeaterField.id, restProps],
);

const localExpressionContextInfo = useMemo(
const expressionContextInfo = useMemo(
() => ({
data: parentExpressionContextInfo.data,
this: itemValue,
parent: buildExpressionContext(parentExpressionContextInfo),
i: [...parentExpressionContextInfo.i, itemIndex + 1],
...parentExpressionContextInfo,
segments: [
...parentExpressionContextInfo.segments,
{
path: parentValuePath,
index: itemIndex,
},
],
}),
[itemIndex, parentExpressionContextInfo, itemValue],
[parentExpressionContextInfo, parentValuePath, itemIndex],
);

return !showRemove ? (
<LocalExpressionContext.Provider value={localExpressionContextInfo}>
<ExpressionContextInfo.Provider value={expressionContextInfo}>
<RowsRenderer {...elementProps} />
</LocalExpressionContext.Provider>
</ExpressionContextInfo.Provider>
) : (
<div class="fjs-repeat-row-container">
<div class="fjs-repeat-row-rows">
<LocalExpressionContext.Provider value={localExpressionContextInfo}>
<ExpressionContextInfo.Provider value={expressionContextInfo}>
<RowsRenderer {...elementProps} />
</LocalExpressionContext.Provider>
</ExpressionContextInfo.Provider>
</div>
<button
type="button"
Expand Down
17 changes: 8 additions & 9 deletions packages/form-js-viewer/src/render/components/FormComponent.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FormField } from './FormField';
import { PoweredBy } from './PoweredBy';
import { LocalExpressionContext } from '../context/LocalExpressionContext';
import { ExpressionContextInfo } from '../context/ExpressionContextInfo';

import { useMemo } from 'preact/hooks';
import { useFilteredFormData, useService } from '../hooks';
import { useDeepCompareMemoize, useFilteredFormData, useService } from '../hooks';

const noop = () => {};

Expand All @@ -28,23 +28,22 @@ export function FormComponent(props) {
onReset();
};

const filteredFormData = useFilteredFormData();
const _filteredFormData = useFilteredFormData();
const filteredFormData = useDeepCompareMemoize(_filteredFormData);

const localExpressionContext = useMemo(
const expressionContextInfo = useMemo(
() => ({
data: filteredFormData,
parent: null,
this: filteredFormData,
i: [],
segments: [],
}),
[filteredFormData],
);

return (
<form class="fjs-form" onSubmit={handleSubmit} onReset={handleReset} aria-label={ariaLabel} noValidate>
<LocalExpressionContext.Provider value={localExpressionContext}>
<ExpressionContextInfo.Provider value={expressionContextInfo}>
<FormField field={schema} onChange={onChange} />
</LocalExpressionContext.Provider>
</ExpressionContextInfo.Provider>
<PoweredBy />
</form>
);
Expand Down
49 changes: 26 additions & 23 deletions packages/form-js-viewer/src/render/components/FormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import isEqual from 'lodash/isEqual';

import { get } from 'min-dash';

import { FormContext, FormRenderContext, LocalExpressionContext } from '../context';
import { FormContext, FormRenderContext, ExpressionContextInfo } from '../context';

import { useCondition, useReadonly, useService } from '../hooks';

Expand All @@ -27,9 +27,7 @@ export function FormField(props) {

const { formId } = useContext(FormContext);

// track whether we should trigger initial validation on certain actions, e.g. field blur
// disabled straight away, if viewerCommands are not available
const [initialValidationTrigger, setInitialValidationTrigger] = useState(!!viewerCommands);
const [validationGrace, setValidationGrace] = useState(!!viewerCommands);

const FormFieldComponent = formFields.get(field.type);

Expand All @@ -39,7 +37,7 @@ export function FormField(props) {

const fieldConfig = FormFieldComponent.config;

const localExpressionContext = useContext(LocalExpressionContext);
const expressionContextInfo = useContext(ExpressionContextInfo);
const valuePath = useMemo(() => pathRegistry.getValuePath(field, { indexes }), [field, indexes, pathRegistry]);

const initialValue = useMemo(() => get(initialData, valuePath), [initialData, valuePath]);
Expand All @@ -56,11 +54,11 @@ export function FormField(props) {
const fieldInstance = useMemo(
() => ({
id: field.id,
expressionContextInfo: localExpressionContext,
expressionContextInfo,
valuePath,
indexes,
}),
[field.id, valuePath, localExpressionContext, indexes],
[field.id, valuePath, expressionContextInfo, indexes],
);

// register form field instance
Expand All @@ -80,57 +78,62 @@ export function FormField(props) {
return;
}

const resetValidation = () => {
setInitialValidationTrigger(true);
const resetValidationGrace = () => {
setValidationGrace(true);
};

eventBus.on('import.done', resetValidation);
eventBus.on('reset', resetValidation);
eventBus.on('import.done', resetValidationGrace);
eventBus.on('reset', resetValidationGrace);

return () => {
eventBus.off('import.done', resetValidation);
eventBus.off('reset', resetValidation);
eventBus.off('import.done', resetValidationGrace);
eventBus.off('reset', resetValidationGrace);
};
}, [eventBus, viewerCommands]);

useEffect(() => {
const hasInitialValue = initialValue && !isEqual(initialValue, []);

if (initialValidationTrigger && hasInitialValue) {
setInitialValidationTrigger(false);
if (validationGrace && hasInitialValue) {
setValidationGrace(false);
viewerCommands.updateFieldInstanceValidation(fieldInstance, initialValue);
}
}, [fieldInstance, initialValidationTrigger, initialValue, viewerCommands]);
}, [fieldInstance, validationGrace, initialValue, viewerCommands]);

const onBlur = useCallback(() => {
const value = get(data, valuePath);

if (initialValidationTrigger) {
setInitialValidationTrigger(false);
if (validationGrace) {
setValidationGrace(false);
viewerCommands.updateFieldInstanceValidation(fieldInstance, value);
}

eventBus.fire('formField.blur', { formField: field });
}, [data, eventBus, field, fieldInstance, initialValidationTrigger, valuePath, viewerCommands]);
}, [data, eventBus, field, fieldInstance, validationGrace, valuePath, viewerCommands]);

const onFocus = useCallback(() => {
eventBus.fire('formField.focus', { formField: field });
}, [eventBus, field]);

const onChange = useCallback(
(update) => {
setInitialValidationTrigger(false);
setValidationGrace(false);
_onChange({ field, indexes, fieldInstance, ...update });
},
[_onChange, field, fieldInstance, indexes],
);

const fieldErrors = useMemo(() => {
if (validationGrace) {
return [];
}

return get(errors, [field.id, ...Object.values(indexes || {})]) || [];
}, [errors, field.id, indexes, validationGrace]);

if (hidden) {
return <Hidden field={field} />;
}

const domId = `${prefixId(field.id, formId, indexes)}`;
const fieldErrors = get(errors, [field.id, ...Object.values(indexes || {})]) || [];

const formFieldElement = (
<FormFieldComponent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createContext } from 'preact';

export const ExpressionContextInfo = createContext({
data: {},
segments: [],
});

This file was deleted.

2 changes: 1 addition & 1 deletion packages/form-js-viewer/src/render/context/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { FormRenderContext } from './FormRenderContext';
export { LocalExpressionContext } from './LocalExpressionContext';
export { ExpressionContextInfo } from './ExpressionContextInfo';
export { FormContext } from './FormContext';
4 changes: 2 additions & 2 deletions packages/form-js-viewer/src/render/hooks/useCondition.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useService } from './useService.js';
import { useContext, useMemo } from 'preact/hooks';
import { LocalExpressionContext } from '../context/LocalExpressionContext.js';
import { ExpressionContextInfo } from '../context/ExpressionContextInfo.js';
import { buildExpressionContext } from '../../util/expressions.js';

/**
Expand All @@ -12,7 +12,7 @@ import { buildExpressionContext } from '../../util/expressions.js';
*/
export function useCondition(condition) {
const conditionChecker = useService('conditionChecker', false);
const expressionContextInfo = useContext(LocalExpressionContext);
const expressionContextInfo = useContext(ExpressionContextInfo);

return useMemo(() => {
return conditionChecker ? conditionChecker.check(condition, buildExpressionContext(expressionContextInfo)) : null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useService } from './useService';
import { LocalExpressionContext } from '../context/LocalExpressionContext';
import { ExpressionContextInfo } from '../context/ExpressionContextInfo';
import { useContext, useMemo } from 'preact/hooks';
import { runExpressionEvaluation } from '../../util/expressions';

Expand All @@ -13,7 +13,7 @@ import { runExpressionEvaluation } from '../../util/expressions';
*/
export function useExpressionEvaluation(value) {
const expressionLanguage = useService('expressionLanguage');
const expressionContextInfo = useContext(LocalExpressionContext);
const expressionContextInfo = useContext(ExpressionContextInfo);
return useMemo(
() => runExpressionEvaluation(expressionLanguage, value, expressionContextInfo),
[expressionLanguage, expressionContextInfo, value],
Expand Down
4 changes: 2 additions & 2 deletions packages/form-js-viewer/src/render/hooks/useReadonly.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { buildExpressionContext } from '../../util/expressions.js';
import { LocalExpressionContext } from '../context/LocalExpressionContext.js';
import { ExpressionContextInfo } from '../context/ExpressionContextInfo.js';
import { useService } from './useService.js';
import { useContext } from 'preact/hooks';

Expand All @@ -17,7 +17,7 @@ import { useContext } from 'preact/hooks';
export function useReadonly(formField, properties = {}) {
const expressionLanguage = useService('expressionLanguage');
const conditionChecker = useService('conditionChecker', false);
const expressionContextInfo = useContext(LocalExpressionContext);
const expressionContextInfo = useContext(ExpressionContextInfo);

const { readonly } = formField;

Expand Down
Loading

0 comments on commit 71c7e21

Please sign in to comment.