Skip to content

Commit c933ab5

Browse files
authored
Merge pull request #8756 from PennyJeans/custom-image-delete-icon
make the remove icon of the file preview configurable and default to RemoveCircle
2 parents 27fc437 + 00ee231 commit c933ab5

6 files changed

Lines changed: 71 additions & 13 deletions

File tree

docs/FileInput.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,17 @@ Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and
5353

5454
## Props
5555

56-
| Prop | Required | Type | Default | Description |
57-
|------------------------|----------|---------------------|------------|---------------------------------------------------------------------|
58-
| `accept` | Optional | `string | string[]` | - | Accepted file type(s). When empty, all file types are accepted. |
59-
| `children` | Optional | `ReactNode` | - | Element used to preview file(s) |
60-
| `minSize` | Optional | `number` | 0 | Minimum file size (in bytes), e.g. 5000 for 5KB |
56+
| Prop | Required | Type | Default | Description |
57+
|------------------------|----------|---------------------|-----------|---------------------------------------------------------------------|
58+
| `accept` | Optional | `string | string[]` | - | Accepted file type(s). When empty, all file types are accepted. |
59+
| `children` | Optional | `ReactNode` | - | Element used to preview file(s) |
60+
| `minSize` | Optional | `number` | 0 | Minimum file size (in bytes), e.g. 5000 for 5KB |
6161
| `maxSize` | Optional | `number` | `Infinity` | Maximum file size (in bytes), e.g. 5000000 for 5MB |
62-
| `multiple` | Optional | `boolean` | `false` | Whether the inputs can accept multiple files. |
63-
| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. |
64-
| `placeholder` | Optional | `ReactNode` | - | Invite displayed in the drop zone |
65-
| `validateFile Removal` | Optional | `function` | - | Allows to cancel the removal of files |
62+
| `multiple` | Optional | `boolean` | `false` | Whether the inputs can accept multiple files. |
63+
| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. |
64+
| `placeholder` | Optional | `ReactNode` | - | Invite displayed in the drop zone |
65+
| `removeIcon` | Optional | `ReactNode` | [MUI's RemoveCircle icon](https://mui.com/material-ui/material-icons/?query=removeCir&selected=RemoveCircle) | The clickable icon for removing files |
66+
| `validateFile Removal` | Optional | `function` | - | Allows to cancel the removal of files |
6667

6768
`<FileInput>` also accepts the [common input props](./Inputs.md#common-input-props).
6869

@@ -152,6 +153,16 @@ If that's not enough, you can pass a `placeholder` prop to overwrite it. The val
152153
</FileInput>
153154
```
154155

156+
## `removeIcon`
157+
158+
Use the `removeIcon` prop to change the icon displayed as the remove button:
159+
160+
```jsx
161+
<ImageInput source="attachments" removeIcon={CustomSvgIcon}>
162+
<ImageField source="src" title="title" />
163+
</ImageInput>
164+
```
165+
155166
## `sx`: CSS API
156167

157168
The `<FileInput>` component accepts the usual `className` prop. You can also override many styles of the inner components thanks to the `sx` property (as most MUI components, see their [documentation about it](https://mui.com/customization/how-to-customize/#overriding-nested-component-styles)). This property accepts the following subclasses:

docs/ImageInput.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@ Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and
5353

5454
| Prop | Required | Type | Default | Description |
5555
|------------------------|----------|---------------------|------------|---------------------------------------------------------------------|
56-
| `accept` | Optional | `string | string[]` | - | Accepted file type(s). When empty, all file types are accepted. |
56+
| `accept` | Optional | `string | string[]` | - | Accepted file type(s). When empty, all file types are accepted. |
5757
| `children` | Optional | `ReactNode` | - | Element used to preview file(s) |
5858
| `minSize` | Optional | `number` | 0 | Minimum file size (in bytes), e.g. 5000 for 5KB |
5959
| `maxSize` | Optional | `number` | `Infinity` | Maximum file size (in bytes), e.g. 5000000 for 5MB |
6060
| `multiple` | Optional | `boolean` | `false` | Whether the inputs can accept multiple files. |
6161
| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. |
6262
| `placeholder` | Optional | `ReactNode` | - | Invite displayed in the drop zone |
63+
| `removeIcon` | Optional | `ReactNode` | [MUI's RemoveCircle icon](https://mui.com/material-ui/material-icons/?query=removeCir&selected=RemoveCircle) | The clickable icon for removing images |
6364
| `validateFile Removal` | Optional | `function` | - | Allows to cancel the removal of files |
6465

6566
`<ImageInput>` also accepts the [common input props](./Inputs.md#common-input-props).
@@ -149,6 +150,16 @@ If that's not enough, you can pass a `placeholder` prop to overwrite it. The val
149150
</ImageInput>
150151
```
151152

153+
## `removeIcon`
154+
155+
Use the `removeIcon` prop to change the icon displayed as the remove button:
156+
157+
```jsx
158+
<ImageInput source="pictures" removeIcon={CustomSvgIcon}>
159+
<ImageField source="src" title="title" />
160+
</ImageInput>
161+
```
162+
152163
## `sx`: CSS API
153164

154165
The `<ImageInput>` component accepts the usual `className` prop. You can also override many styles of the inner components thanks to the `sx` property (as most MUI components, see their [documentation about it](https://mui.com/customization/how-to-customize/#overriding-nested-component-styles)). This property accepts the following subclasses:

packages/ra-ui-materialui/src/input/FileInput.stories.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { FileInput } from './FileInput';
99
import { FileField } from '../field';
1010
import { required } from 'ra-core';
1111
import { FormInspector } from './common.stories';
12+
import DeleteIcon from '@mui/icons-material/DeleteOutline';
1213

1314
export default { title: 'ra-ui-materialui/input/FileInput' };
1415

@@ -104,6 +105,15 @@ export const Disabled = () => (
104105
</Wrapper>
105106
);
106107

108+
export const CustomRemoveIcon = () => (
109+
<Wrapper>
110+
<FileInput source="attachments" removeIcon={DeleteIcon}>
111+
<FileField source="src" title="title" />
112+
</FileInput>
113+
<FormInspector name="attachments" />
114+
</Wrapper>
115+
);
116+
107117
const i18nProvider = polyglotI18nProvider(() => englishMessages);
108118

109119
const Wrapper = ({ children }) => (

packages/ra-ui-materialui/src/input/FileInput.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {
22
Children,
3+
FC,
34
isValidElement,
45
ReactElement,
56
ReactNode,
@@ -22,6 +23,7 @@ import { FileInputPreview } from './FileInputPreview';
2223
import { sanitizeInputRestProps } from './sanitizeInputRestProps';
2324
import { InputHelperText } from './InputHelperText';
2425
import { SxProps } from '@mui/system';
26+
import { SvgIconProps } from '@mui/material';
2527

2628
export const FileInput = (props: FileInputProps) => {
2729
const {
@@ -41,6 +43,7 @@ export const FileInput = (props: FileInputProps) => {
4143
onRemove: onRemoveProp,
4244
parse,
4345
placeholder,
46+
removeIcon,
4447
resource,
4548
source,
4649
validate,
@@ -192,6 +195,7 @@ export const FileInput = (props: FileInputProps) => {
192195
file={file}
193196
onRemove={onRemove(file)}
194197
className={FileInputClasses.removeButton}
198+
removeIcon={removeIcon}
195199
>
196200
<RecordContextProvider value={file}>
197201
{childrenElement}
@@ -223,6 +227,7 @@ FileInput.propTypes = {
223227
multiple: PropTypes.bool,
224228
validateFileRemoval: PropTypes.func,
225229
options: PropTypes.object,
230+
removeIcon: PropTypes.elementType,
226231
resource: PropTypes.string,
227232
source: PropTypes.string,
228233
placeholder: PropTypes.node,
@@ -264,6 +269,7 @@ export type FileInputProps = CommonInputProps & {
264269
options?: DropzoneOptions;
265270
onRemove?: Function;
266271
placeholder?: ReactNode;
272+
removeIcon?: FC<SvgIconProps>;
267273
inputProps?: any;
268274
validateFileRemoval?(file): boolean | Promise<boolean>;
269275
sx?: SxProps;

packages/ra-ui-materialui/src/input/FileInputPreview.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import * as React from 'react';
2+
import { FC, ReactNode, useEffect } from 'react';
23
import { styled } from '@mui/material/styles';
3-
import { useEffect, ReactNode } from 'react';
44
import PropTypes from 'prop-types';
55
import RemoveCircle from '@mui/icons-material/RemoveCircle';
66
import IconButton from '@mui/material/IconButton';
77
import { useTranslate } from 'ra-core';
8+
import { SvgIconProps } from '@mui/material';
89

910
export const FileInputPreview = (props: FileInputPreviewProps) => {
10-
const { children, className, onRemove, file, ...rest } = props;
11+
const {
12+
children,
13+
className,
14+
onRemove,
15+
file,
16+
removeIcon: RemoveIcon = RemoveCircle,
17+
...rest
18+
} = props;
1119

1220
const translate = useTranslate();
1321

@@ -30,7 +38,7 @@ export const FileInputPreview = (props: FileInputPreviewProps) => {
3038
title={translate('ra.action.delete')}
3139
size="small"
3240
>
33-
<RemoveCircle className={FileInputPreviewClasses.removeIcon} />
41+
<RemoveIcon className={FileInputPreviewClasses.removeIcon} />
3442
</IconButton>
3543
{children}
3644
</Root>
@@ -42,6 +50,7 @@ FileInputPreview.propTypes = {
4250
className: PropTypes.string,
4351
file: PropTypes.object,
4452
onRemove: PropTypes.func.isRequired,
53+
removeIcon: PropTypes.element,
4554
};
4655

4756
FileInputPreview.defaultProps = {
@@ -71,4 +80,5 @@ export interface FileInputPreviewProps {
7180
className?: string;
7281
onRemove: () => void;
7382
file: any;
83+
removeIcon?: FC<SvgIconProps>;
7484
}

packages/ra-ui-materialui/src/input/ImageInput.stories.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ImageInput } from './ImageInput';
99
import { ImageField } from '../field';
1010
import { required } from 'ra-core';
1111
import { FormInspector } from './common.stories';
12+
import DeleteIcon from '@mui/icons-material/DeleteOutline';
1213

1314
export default { title: 'ra-ui-materialui/input/ImageInput' };
1415

@@ -83,6 +84,15 @@ export const Required = () => (
8384
</Wrapper>
8485
);
8586

87+
export const CustomRemoveIcon = () => (
88+
<Wrapper>
89+
<ImageInput source="image" removeIcon={DeleteIcon}>
90+
<ImageField source="src" title="title" />
91+
</ImageInput>
92+
<FormInspector name="attachments" />
93+
</Wrapper>
94+
);
95+
8696
const i18nProvider = polyglotI18nProvider(() => englishMessages);
8797

8898
const Wrapper = ({ children }) => (

0 commit comments

Comments
 (0)