Skip to content

Commit

Permalink
feat: add/remove items in dynamic lists
Browse files Browse the repository at this point in the history
Related to #796
  • Loading branch information
Skaiir committed Sep 25, 2023
1 parent 0fcdc19 commit 50d0b2a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 18 deletions.
35 changes: 35 additions & 0 deletions packages/form-js-viewer/assets/form-js-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,41 @@
margin: 4px 0;
}

.fjs-container .fjs-repeat-row-container {
position: relative;
}

.fjs-container .fjs-repeat-row-container:focus-within .fjs-repeat-row-delete,
.fjs-container .fjs-repeat-row-container:hover .fjs-repeat-row-delete-container .fjs-repeat-row-delete,
.fjs-container .fjs-repeat-row-container .fjs-repeat-row-delete-container:hover .fjs-repeat-row-delete {
display: flex;
}

.fjs-container .fjs-repeat-row-container .fjs-repeat-row-delete-container {
display: flex;
position: absolute;
height: 100px;
width: 30px;
top: calc(50% - 45px);
right: -35px;
align-items: center;
justify-content: center;
}

.fjs-container .fjs-repeat-row-container .fjs-repeat-row-delete {
display: none;
font-family: inherit;
font-size: inherit;
cursor: pointer;
color: var(--color-accent);
background: white;
border: 2px solid var(--color-accent);
height: 24px;
width: 24px;
align-items: center;
justify-content: center;
}

.fjs-container .fjs-repeat-render-footer {
display: flex;
align-items: center;
Expand Down
32 changes: 27 additions & 5 deletions packages/form-js-viewer/src/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export default class Form {
warnings
} = this.get('importer').importSchema(schema);

const initializedData = this._initializeFieldData(clone(data));
const initializedData = this._getInitializedFieldData(clone(data));

this._setState({
data: initializedData,
Expand Down Expand Up @@ -477,11 +477,16 @@ export default class Form {
/**
* @internal
*/
_initializeFieldData(data) {
_getInitializedFieldData(data, options = {}) {
const formFieldRegistry = this.get('formFieldRegistry');
const formFields = this.get('formFields');
const pathRegistry = this.get('pathRegistry');

const {
customRoot,
customIndexes
} = options;

function initializeFieldDataRecursively(initializedData, formField, indexes) {
const { defaultValue, type, isRepeating } = formField;
const { config: fieldConfig } = formFields.get(type);
Expand Down Expand Up @@ -518,7 +523,16 @@ export default class Form {
// (c) Initialize field value in output data
set(initializedData, valuePath, valueData);

// (d) Recurse repeatable parents both across the indexes of repetition and the children
// (d) If indexed ahead of time, recurse repeatable simply across the children
if (!isUndefined(indexes[formField.id])) {
formField.components.forEach(
(component) => initializeFieldDataRecursively(initializedData, component, { ...indexes })
);

return;
}

// (e) Recurse repeatable parents both across the indexes of repetition and the children
valueData.forEach((_, index) => {
formField.components.forEach(
(component) => initializeFieldDataRecursively(initializedData, component, { ...indexes, [formField.id]: index })
Expand All @@ -533,9 +547,17 @@ export default class Form {
}
}

// allows definition of a specific subfield to generate the data for
const root = customRoot || formFieldRegistry.getForm();
const indexes = customIndexes || {};
const basePath = pathRegistry.getValuePath(root, { indexes }) || [];

// if indexing ahead of time, we must add this index to the data path at the end
const path = !isUndefined(indexes[root.id]) ? [ ...basePath, indexes[root.id] ] : basePath;

const workingData = clone(data);
initializeFieldDataRecursively(workingData, formFieldRegistry.getForm(), {});
return workingData;
initializeFieldDataRecursively(workingData, root, indexes);
return get(workingData, path, {});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { get } from 'min-dash';

import ExpandSvg from '../../render/components/form-fields/icons/Expand.svg';
import CollapseSvg from '../../render/components/form-fields/icons/Collapse.svg';
import XMarkSvg from '../../render/components/form-fields/icons/XMark.svg';

export default class RepeatRenderManager {

Expand Down Expand Up @@ -48,15 +49,34 @@ export default class RepeatRenderManager {

const displayValues = isCollapsed ? values.slice(0, nonCollapsedItems) : values;

const onDeleteItem = (index) => {

const updatedValues = values.slice();
updatedValues.splice(index, 1);

props.onChange({
field: repeaterField,
value: updatedValues,
indexes
});
};

return (
<>
{displayValues.map((_, index) => {
const elementProps = {
...restProps,
indexes: { ...(indexes || {}), [ repeaterField.id ]: index },
indexes: { ...(indexes || {}), [ repeaterField.id ]: index }
};

return <RowsRenderer { ...elementProps } />;
return <div class="fjs-repeat-row-container">
<RowsRenderer { ...elementProps } />
<div class="fjs-repeat-row-delete-container">
<button class="fjs-repeat-row-delete" onClick={ () => onDeleteItem(index) }>
<XMarkSvg />
</button>
</div>
</div>;
})}
</>
);
Expand All @@ -81,17 +101,32 @@ export default class RepeatRenderManager {
setSharedRepeatState(state => ({ ...state, isCollapsed: !isCollapsed }));
};

return togglingEnabled
? <div className="fjs-repeat-render-footer">
<button onClick={ toggle }>
{
isCollapsed
? <><ExpandSvg /> { `Expand all (${values.length})` }</>
: <><CollapseSvg /> { 'Collapse' }</>
}
</button>
</div>
: null;
const onAddItem = () => {
const updatedValues = values.slice();
const newItem = this._form._getInitializedFieldData(this._form._state.data, {
customRoot : repeaterField,
customIndexes : { ...indexes, [ repeaterField.id ]: updatedValues.length }
});

updatedValues.push(newItem);

props.onChange({
field: repeaterField,
value: updatedValues,
indexes
});
};

return <div className="fjs-repeat-render-footer">
<button onClick={ onAddItem }>add</button>
{ togglingEnabled ? <button onClick={ toggle }>
{
isCollapsed
? <><ExpandSvg /> { `Expand all (${values.length})` }</>
: <><CollapseSvg /> { 'Collapse' }</>
}
</button> : null }
</div>;
}

_getNonCollapsedItems(field) {
Expand Down

0 comments on commit 50d0b2a

Please sign in to comment.