Skip to content

Commit 1d21d00

Browse files
authored
Merge pull request #6871 from marmelab/fix-sfi-animation
Fix SimpleFormIterator transition animations on add and remove items
2 parents 35c9e09 + 9bbe487 commit 1d21d00

File tree

2 files changed

+128
-116
lines changed

2 files changed

+128
-116
lines changed

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
158158
resource={resource}
159159
source={source}
160160
variant={variant}
161+
ref={nodeRef}
161162
>
162163
{children}
163164
</SimpleFormIteratorItem>

packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIteratorItem.tsx

+127-116
Original file line numberDiff line numberDiff line change
@@ -23,129 +23,140 @@ import {
2323
SimpleFormIteratorItemContextValue,
2424
} from './SimpleFormIteratorItemContext';
2525

26-
export const SimpleFormIteratorItem = (props: SimpleFormIteratorItemProps) => {
27-
const {
28-
basePath,
29-
children,
30-
classes,
31-
disabled,
32-
disableReordering,
33-
disableRemove,
34-
getItemLabel,
35-
index,
36-
margin,
37-
member,
38-
record,
39-
removeButton,
40-
reOrderButtons,
41-
resource,
42-
source,
43-
variant,
44-
} = props;
26+
export const SimpleFormIteratorItem = React.forwardRef(
27+
(props: SimpleFormIteratorItemProps, ref: any) => {
28+
const {
29+
basePath,
30+
children,
31+
classes,
32+
disabled,
33+
disableReordering,
34+
disableRemove,
35+
getItemLabel,
36+
index,
37+
margin,
38+
member,
39+
record,
40+
removeButton,
41+
reOrderButtons,
42+
resource,
43+
source,
44+
variant,
45+
} = props;
4546

46-
const { total, reOrder, remove } = useSimpleFormIterator();
47-
// Returns a boolean to indicate whether to disable the remove button for certain fields.
48-
// If disableRemove is a function, then call the function with the current record to
49-
// determining if the button should be disabled. Otherwise, use a boolean property that
50-
// enables or disables the button for all of the fields.
51-
const disableRemoveField = (record: Record) => {
52-
if (typeof disableRemove === 'boolean') {
53-
return disableRemove;
54-
}
55-
return disableRemove && disableRemove(record);
56-
};
47+
const { total, reOrder, remove } = useSimpleFormIterator();
48+
// Returns a boolean to indicate whether to disable the remove button for certain fields.
49+
// If disableRemove is a function, then call the function with the current record to
50+
// determining if the button should be disabled. Otherwise, use a boolean property that
51+
// enables or disables the button for all of the fields.
52+
const disableRemoveField = (record: Record) => {
53+
if (typeof disableRemove === 'boolean') {
54+
return disableRemove;
55+
}
56+
return disableRemove && disableRemove(record);
57+
};
5758

58-
// remove field and call the onClick event of the button passed as removeButton prop
59-
const handleRemoveButtonClick = (
60-
originalOnClickHandler: MouseEventHandler,
61-
index: number
62-
) => (event: MouseEvent) => {
63-
remove(index);
64-
if (originalOnClickHandler) {
65-
originalOnClickHandler(event);
66-
}
67-
};
59+
// remove field and call the onClick event of the button passed as removeButton prop
60+
const handleRemoveButtonClick = (
61+
originalOnClickHandler: MouseEventHandler,
62+
index: number
63+
) => (event: MouseEvent) => {
64+
remove(index);
65+
if (originalOnClickHandler) {
66+
originalOnClickHandler(event);
67+
}
68+
};
6869

69-
const context = useMemo<SimpleFormIteratorItemContextValue>(
70-
() => ({
71-
index,
72-
total,
73-
reOrder: newIndex => reOrder(index, newIndex),
74-
remove: () => remove(index),
75-
}),
76-
[index, total, reOrder, remove]
77-
);
70+
const context = useMemo<SimpleFormIteratorItemContextValue>(
71+
() => ({
72+
index,
73+
total,
74+
reOrder: newIndex => reOrder(index, newIndex),
75+
remove: () => remove(index),
76+
}),
77+
[index, total, reOrder, remove]
78+
);
7879

79-
return (
80-
<SimpleFormIteratorItemContext.Provider value={context}>
81-
<li className={classes.line}>
82-
<div>
83-
<div className={classes.indexContainer}>
84-
<Typography variant="body1" className={classes.index}>
85-
{getItemLabel(index)}
86-
</Typography>
87-
{!disabled &&
88-
!disableReordering &&
89-
cloneElement(reOrderButtons, {
90-
index,
91-
max: total,
92-
reOrder,
80+
return (
81+
<SimpleFormIteratorItemContext.Provider value={context}>
82+
<li className={classes.line} ref={ref}>
83+
<div>
84+
<div className={classes.indexContainer}>
85+
<Typography
86+
variant="body1"
87+
className={classes.index}
88+
>
89+
{getItemLabel(index)}
90+
</Typography>
91+
{!disabled &&
92+
!disableReordering &&
93+
cloneElement(reOrderButtons, {
94+
index,
95+
max: total,
96+
reOrder,
97+
className: classNames(
98+
'button-reorder',
99+
`button-reorder-${source}-${index}`
100+
),
101+
})}
102+
</div>
103+
</div>
104+
<section className={classes.form}>
105+
{Children.map(
106+
children,
107+
(input: ReactElement, index2) => {
108+
if (!isValidElement<any>(input)) {
109+
return null;
110+
}
111+
const { source, ...inputProps } = input.props;
112+
return (
113+
<FormInput
114+
basePath={
115+
input.props.basePath || basePath
116+
}
117+
input={cloneElement(input, {
118+
source: source
119+
? `${member}.${source}`
120+
: member,
121+
index: source ? undefined : index2,
122+
label:
123+
typeof input.props.label ===
124+
'undefined'
125+
? source
126+
? `resources.${resource}.fields.${source}`
127+
: undefined
128+
: input.props.label,
129+
disabled,
130+
...inputProps,
131+
})}
132+
record={record}
133+
resource={resource}
134+
variant={variant}
135+
margin={margin}
136+
/>
137+
);
138+
}
139+
)}
140+
</section>
141+
{!disabled && !disableRemoveField(record) && (
142+
<span className={classes.action}>
143+
{cloneElement(removeButton, {
144+
onClick: handleRemoveButtonClick(
145+
removeButton.props.onClick,
146+
index
147+
),
93148
className: classNames(
94-
'button-reorder',
95-
`button-reorder-${source}-${index}`
149+
'button-remove',
150+
`button-remove-${source}-${index}`
96151
),
97152
})}
98-
</div>
99-
</div>
100-
<section className={classes.form}>
101-
{Children.map(children, (input: ReactElement, index2) => {
102-
if (!isValidElement<any>(input)) {
103-
return null;
104-
}
105-
const { source, ...inputProps } = input.props;
106-
return (
107-
<FormInput
108-
basePath={input.props.basePath || basePath}
109-
input={cloneElement(input, {
110-
source: source
111-
? `${member}.${source}`
112-
: member,
113-
index: source ? undefined : index2,
114-
label:
115-
typeof input.props.label === 'undefined'
116-
? source
117-
? `resources.${resource}.fields.${source}`
118-
: undefined
119-
: input.props.label,
120-
disabled,
121-
...inputProps,
122-
})}
123-
record={record}
124-
resource={resource}
125-
variant={variant}
126-
margin={margin}
127-
/>
128-
);
129-
})}
130-
</section>
131-
{!disabled && !disableRemoveField(record) && (
132-
<span className={classes.action}>
133-
{cloneElement(removeButton, {
134-
onClick: handleRemoveButtonClick(
135-
removeButton.props.onClick,
136-
index
137-
),
138-
className: classNames(
139-
'button-remove',
140-
`button-remove-${source}-${index}`
141-
),
142-
})}
143-
</span>
144-
)}
145-
</li>
146-
</SimpleFormIteratorItemContext.Provider>
147-
);
148-
};
153+
</span>
154+
)}
155+
</li>
156+
</SimpleFormIteratorItemContext.Provider>
157+
);
158+
}
159+
);
149160

150161
export type DisableRemoveFunction = (record: Record) => boolean;
151162

0 commit comments

Comments
 (0)