Skip to content

Commit ea8fb82

Browse files
committed
Array field without default array item
1 parent 54a0657 commit ea8fb82

File tree

7 files changed

+180
-130
lines changed

7 files changed

+180
-130
lines changed

packages/orchestrator-ui-components/src/components/WfoPydanticForm/WfoPydanticForm.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,16 @@ import { useAppSelector } from '@/rtk/hooks';
2323

2424
import { Footer } from './Footer';
2525
import { Row } from './Row';
26-
import { Checkbox, Divider, Label, Summary, Text, TextArea, WfoObjectField, WfoArrayField} from './fields';
26+
import {
27+
Checkbox,
28+
Divider,
29+
Label,
30+
Summary,
31+
Text,
32+
TextArea,
33+
WfoArrayField,
34+
WfoObjectField,
35+
} from './fields';
2736

2837
interface WfoPydanticFormProps {
2938
processName: string;
@@ -146,21 +155,21 @@ export const WfoPydanticForm = ({
146155
id: 'object',
147156
ElementMatch: {
148157
isControlledElement: false,
149-
Element: WfoObjectField
158+
Element: WfoObjectField,
150159
},
151160
matcher: (field) => {
152-
return (field.type === PydanticFormFieldType.OBJECT);
153-
}
161+
return field.type === PydanticFormFieldType.OBJECT;
162+
},
154163
},
155164
{
156165
id: 'array',
157166
ElementMatch: {
158167
isControlledElement: true,
159-
Element: WfoArrayField
168+
Element: WfoArrayField,
160169
},
161170
matcher: (field) => {
162-
return (field.type === PydanticFormFieldType.ARRAY);
163-
}
171+
return field.type === PydanticFormFieldType.ARRAY;
172+
},
164173
},
165174
{
166175
id: 'summary',

packages/orchestrator-ui-components/src/components/WfoPydanticForm/fields/WfoArrayField.tsx

Lines changed: 0 additions & 112 deletions
This file was deleted.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import React from 'react';
2+
import { useFieldArray } from 'react-hook-form';
3+
4+
import {
5+
PydanticFormElementProps,
6+
RenderFields,
7+
fieldToComponentMatcher,
8+
itemizeArrayItem,
9+
usePydanticFormContext,
10+
} from 'pydantic-forms';
11+
12+
import { EuiIcon } from '@elastic/eui';
13+
14+
import { useOrchestratorTheme } from '@/hooks';
15+
16+
import { getWfoArrayFieldStyles } from './styles';
17+
18+
// adjust relative path accordingly
19+
20+
const MinusButton = ({
21+
index,
22+
onRemove,
23+
}: {
24+
index: number;
25+
onRemove: (index: number) => void;
26+
}) => {
27+
const { theme } = useOrchestratorTheme();
28+
const { minusButton } = getWfoArrayFieldStyles();
29+
30+
return (
31+
<span css={minusButton} onClick={() => onRemove(index)}>
32+
<EuiIcon type="minus" size="xxl" color={theme.colors.danger} />
33+
</span>
34+
);
35+
};
36+
37+
const PlusButton = ({ onClick }: { onClick: () => void }) => {
38+
const { theme } = useOrchestratorTheme();
39+
const { plusButtonWrapper } = getWfoArrayFieldStyles();
40+
41+
return (
42+
<div css={plusButtonWrapper}>
43+
<EuiIcon
44+
onClick={onClick}
45+
type="plus"
46+
size="xxl"
47+
color={theme.colors.success}
48+
/>
49+
</div>
50+
);
51+
};
52+
53+
export const WfoArrayField = ({
54+
pydanticFormField,
55+
}: PydanticFormElementProps) => {
56+
const { config, rhf } = usePydanticFormContext();
57+
const { control } = rhf;
58+
const { id: arrayName, arrayItem } = pydanticFormField;
59+
const { minItems, maxItems } = pydanticFormField.validations;
60+
const { container, fieldWrapper } = getWfoArrayFieldStyles();
61+
62+
const { fields, append, remove } = useFieldArray({
63+
control,
64+
name: arrayName,
65+
});
66+
67+
const showMinus = !minItems || fields.length > minItems;
68+
const showPlus = !maxItems || fields.length < maxItems;
69+
70+
if (!arrayItem) return null;
71+
72+
const component = fieldToComponentMatcher(
73+
arrayItem,
74+
config?.componentMatcher,
75+
);
76+
77+
const renderField = (field: Record<'id', string>, index: number) => {
78+
const arrayField = itemizeArrayItem(index, arrayItem);
79+
80+
return (
81+
<div key={field.id} css={fieldWrapper}>
82+
<RenderFields
83+
pydanticFormComponents={[
84+
{
85+
Element: component.Element,
86+
pydanticFormField: arrayField,
87+
},
88+
]}
89+
extraTriggerFields={[arrayName]}
90+
/>
91+
{showMinus && <MinusButton index={index} onRemove={remove} />}
92+
</div>
93+
);
94+
};
95+
96+
return (
97+
<div css={container}>
98+
{fields.map(renderField)}
99+
100+
{showPlus && (
101+
<PlusButton
102+
onClick={() => {
103+
append({
104+
[arrayName]: arrayItem.default ?? undefined,
105+
});
106+
}}
107+
/>
108+
)}
109+
</div>
110+
);
111+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { css } from '@emotion/react';
2+
3+
export const getWfoArrayFieldStyles = () => {
4+
const container = css({
5+
padding: '1rem',
6+
display: 'flex',
7+
flexDirection: 'column',
8+
flexGrow: 1,
9+
});
10+
11+
const fieldWrapper = css({
12+
display: 'flex',
13+
gap: '10px',
14+
alignItems: 'center',
15+
});
16+
17+
const minusButton = css({
18+
width: '40px',
19+
cursor: 'pointer',
20+
});
21+
22+
const plusButtonWrapper = css({
23+
display: 'flex',
24+
cursor: 'pointer',
25+
justifyContent: 'end',
26+
});
27+
28+
return {
29+
container,
30+
fieldWrapper,
31+
minusButton,
32+
plusButtonWrapper,
33+
};
34+
};
Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import React from 'react';
2+
23
import {
3-
getPydanticFormComponents,
44
PydanticFormElementProps,
5-
RenderFields, usePydanticFormContext,
5+
RenderFields,
6+
getPydanticFormComponents,
7+
usePydanticFormContext,
68
} from 'pydantic-forms';
9+
710
import { EuiFlexGroup } from '@elastic/eui';
11+
812
import { getWfoObjectFieldStyles } from '../fields/styles';
913

10-
export const WfoObjectField = ({pydanticFormField}: PydanticFormElementProps) => {
14+
export const WfoObjectField = ({
15+
pydanticFormField,
16+
}: PydanticFormElementProps) => {
1117
const { config } = usePydanticFormContext();
12-
const {wfoObjectFieldStyles} = getWfoObjectFieldStyles()
18+
const { wfoObjectFieldStyles } = getWfoObjectFieldStyles();
1319
const components = getPydanticFormComponents(
1420
pydanticFormField.properties || {},
1521
config?.componentMatcher,
1622
);
1723

18-
return <EuiFlexGroup css={wfoObjectFieldStyles}>
19-
<RenderFields pydanticFormComponents={components} />
20-
</EuiFlexGroup>
21-
}
24+
return (
25+
<EuiFlexGroup css={wfoObjectFieldStyles}>
26+
<RenderFields pydanticFormComponents={components} />
27+
</EuiFlexGroup>
28+
);
29+
};

packages/orchestrator-ui-components/src/components/WfoPydanticForm/fields/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ export * from './Divider';
55
export * from './Checkbox';
66
export * from './Summary';
77
export * from './WfoObjectField';
8-
export * from './WfoArrayField';
8+
export * from './WfoArrayField/WfoArrayField';

packages/orchestrator-ui-components/src/components/WfoPydanticForm/fields/styles.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export const getWfoObjectFieldStyles = () => {
88
},
99
});
1010
return {
11-
wfoObjectFieldStyles
11+
wfoObjectFieldStyles,
1212
};
13-
}
13+
};

0 commit comments

Comments
 (0)