diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bab40e6..e65cecfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - edit user shows now email in title - focus is now passed also do slide layout fields - empty fields are no longer submitted +- stuttery form because of logic rerenders ### Security diff --git a/components/form/layouts/card/index.tsx b/components/form/layouts/card/index.tsx index c9b5211d..843e90e7 100644 --- a/components/form/layouts/card/index.tsx +++ b/components/form/layouts/card/index.tsx @@ -4,10 +4,10 @@ import { darken, lighten } from 'polished' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import { FormPublicFieldFragment } from '../../../../graphql/fragment/form.public.fragment' import { Omf } from '../../../omf' import { StyledButton } from '../../../styled/button' import { useMath } from '../../../use.math' +import { fieldTypes } from '../../types' import { LayoutProps } from '../layout.props' import { Field } from './field' import { Page } from './page' @@ -39,7 +39,7 @@ export const CardLayout: React.FC = (props) => { const [loading, setLoading] = useState(false) const [step, setStep] = useState(props.form.startPage.show ? 'start' : 'form') const evaluator = useMath() - const [values, setValues] = useState({}) + const [visiblity, setVisibility] = useState({}) const { design, startPage, endPage, fields } = props.form const { setField } = props.submission @@ -48,7 +48,9 @@ export const CardLayout: React.FC = (props) => { const defaults = {} fields.forEach(field => { - const defaultValue = field.defaultValue ? JSON.parse(field.defaultValue) : null + const defaultValue = field.defaultValue + ? fieldTypes[field.type].parseValue(field.defaultValue) + : null defaults[`@${field.id}`] = form.getFieldValue([field.id, 'value']) ?? defaultValue @@ -57,8 +59,43 @@ export const CardLayout: React.FC = (props) => { } }) - setValues(defaults) - }, [fields, form]) + // now calculate visibility + const nextVisibility = {} + fields.forEach(field => { + if (!field.logic) return + + const logic = field.logic + .filter(logic => logic.action === 'visible') + + if (logic.length === 0) { + return + } + + nextVisibility[field.id] = logic + .map(logic => { + try { + const r = evaluator( + logic.formula, + defaults + ) + + return Boolean(r) + } catch { + return true + } + }) + .reduce((previous, current) => previous && current, true) + }) + + // TODO improve logic of how we calculate new logic checks + if (Object.values(nextVisibility).join(',') == Object.values(visiblity).join(',')) { + return + } + + setVisibility(nextVisibility) + }, [ + fields, form, visiblity, + ]) useEffect(() => { updateValues() @@ -95,27 +132,7 @@ export const CardLayout: React.FC = (props) => { setLoading(false) } - const isVisible = useCallback((field: FormPublicFieldFragment): boolean => { - if (!field.logic) return true - - return field.logic - .filter(logic => logic.action === 'visible') - .map(logic => { - try { - const r = evaluator( - logic.formula, - values - ) - - return Boolean(r) - } catch { - return true - } - }) - .reduce((previous, current) => previous && current, true) - }, [ - fields, form, values, - ]) + console.log('render') const render = () => { switch (step) { @@ -130,12 +147,12 @@ export const CardLayout: React.FC = (props) => { onFinish={finish} onValuesChange={updateValues} > - {fields.map((field, i) => { + {fields.map((field) => { if (field.type === 'hidden') { return null } - if (!isVisible(field)) { + if (visiblity[field.id] === false) { return null } diff --git a/components/form/types/image/index.ts b/components/form/types/image/index.ts index a931518a..319507ff 100644 --- a/components/form/types/image/index.ts +++ b/components/form/types/image/index.ts @@ -1,3 +1,4 @@ +/* import dynamic from 'next/dynamic' import { ComponentType } from 'react' import { AbstractType } from '../abstract.type' @@ -13,3 +14,4 @@ export class ImageType extends AbstractType { return dynamic(() => import('./dropdown.input').then(c => c.builder(this))); } } +*/