Skip to content

Commit

Permalink
Merge pull request #8756 from PennyJeans/custom-image-delete-icon
Browse files Browse the repository at this point in the history
make the remove icon of the file preview configurable and default to RemoveCircle
  • Loading branch information
slax57 authored Apr 24, 2023
2 parents 27fc437 + 00ee231 commit c933ab5
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 13 deletions.
29 changes: 20 additions & 9 deletions docs/FileInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,17 @@ Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and

## Props

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

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

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

## `removeIcon`

Use the `removeIcon` prop to change the icon displayed as the remove button:

```jsx
<ImageInput source="attachments" removeIcon={CustomSvgIcon}>
<ImageField source="src" title="title" />
</ImageInput>
```

## `sx`: CSS API

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:
Expand Down
13 changes: 12 additions & 1 deletion docs/ImageInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@ Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and

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

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

## `removeIcon`

Use the `removeIcon` prop to change the icon displayed as the remove button:

```jsx
<ImageInput source="pictures" removeIcon={CustomSvgIcon}>
<ImageField source="src" title="title" />
</ImageInput>
```

## `sx`: CSS API

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:
Expand Down
10 changes: 10 additions & 0 deletions packages/ra-ui-materialui/src/input/FileInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FileInput } from './FileInput';
import { FileField } from '../field';
import { required } from 'ra-core';
import { FormInspector } from './common.stories';
import DeleteIcon from '@mui/icons-material/DeleteOutline';

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

Expand Down Expand Up @@ -104,6 +105,15 @@ export const Disabled = () => (
</Wrapper>
);

export const CustomRemoveIcon = () => (
<Wrapper>
<FileInput source="attachments" removeIcon={DeleteIcon}>
<FileField source="src" title="title" />
</FileInput>
<FormInspector name="attachments" />
</Wrapper>
);

const i18nProvider = polyglotI18nProvider(() => englishMessages);

const Wrapper = ({ children }) => (
Expand Down
6 changes: 6 additions & 0 deletions packages/ra-ui-materialui/src/input/FileInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {
Children,
FC,
isValidElement,
ReactElement,
ReactNode,
Expand All @@ -22,6 +23,7 @@ import { FileInputPreview } from './FileInputPreview';
import { sanitizeInputRestProps } from './sanitizeInputRestProps';
import { InputHelperText } from './InputHelperText';
import { SxProps } from '@mui/system';
import { SvgIconProps } from '@mui/material';

export const FileInput = (props: FileInputProps) => {
const {
Expand All @@ -41,6 +43,7 @@ export const FileInput = (props: FileInputProps) => {
onRemove: onRemoveProp,
parse,
placeholder,
removeIcon,
resource,
source,
validate,
Expand Down Expand Up @@ -192,6 +195,7 @@ export const FileInput = (props: FileInputProps) => {
file={file}
onRemove={onRemove(file)}
className={FileInputClasses.removeButton}
removeIcon={removeIcon}
>
<RecordContextProvider value={file}>
{childrenElement}
Expand Down Expand Up @@ -223,6 +227,7 @@ FileInput.propTypes = {
multiple: PropTypes.bool,
validateFileRemoval: PropTypes.func,
options: PropTypes.object,
removeIcon: PropTypes.elementType,
resource: PropTypes.string,
source: PropTypes.string,
placeholder: PropTypes.node,
Expand Down Expand Up @@ -264,6 +269,7 @@ export type FileInputProps = CommonInputProps & {
options?: DropzoneOptions;
onRemove?: Function;
placeholder?: ReactNode;
removeIcon?: FC<SvgIconProps>;
inputProps?: any;
validateFileRemoval?(file): boolean | Promise<boolean>;
sx?: SxProps;
Expand Down
16 changes: 13 additions & 3 deletions packages/ra-ui-materialui/src/input/FileInputPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import * as React from 'react';
import { FC, ReactNode, useEffect } from 'react';
import { styled } from '@mui/material/styles';
import { useEffect, ReactNode } from 'react';
import PropTypes from 'prop-types';
import RemoveCircle from '@mui/icons-material/RemoveCircle';
import IconButton from '@mui/material/IconButton';
import { useTranslate } from 'ra-core';
import { SvgIconProps } from '@mui/material';

export const FileInputPreview = (props: FileInputPreviewProps) => {
const { children, className, onRemove, file, ...rest } = props;
const {
children,
className,
onRemove,
file,
removeIcon: RemoveIcon = RemoveCircle,
...rest
} = props;

const translate = useTranslate();

Expand All @@ -30,7 +38,7 @@ export const FileInputPreview = (props: FileInputPreviewProps) => {
title={translate('ra.action.delete')}
size="small"
>
<RemoveCircle className={FileInputPreviewClasses.removeIcon} />
<RemoveIcon className={FileInputPreviewClasses.removeIcon} />
</IconButton>
{children}
</Root>
Expand All @@ -42,6 +50,7 @@ FileInputPreview.propTypes = {
className: PropTypes.string,
file: PropTypes.object,
onRemove: PropTypes.func.isRequired,
removeIcon: PropTypes.element,
};

FileInputPreview.defaultProps = {
Expand Down Expand Up @@ -71,4 +80,5 @@ export interface FileInputPreviewProps {
className?: string;
onRemove: () => void;
file: any;
removeIcon?: FC<SvgIconProps>;
}
10 changes: 10 additions & 0 deletions packages/ra-ui-materialui/src/input/ImageInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ImageInput } from './ImageInput';
import { ImageField } from '../field';
import { required } from 'ra-core';
import { FormInspector } from './common.stories';
import DeleteIcon from '@mui/icons-material/DeleteOutline';

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

Expand Down Expand Up @@ -83,6 +84,15 @@ export const Required = () => (
</Wrapper>
);

export const CustomRemoveIcon = () => (
<Wrapper>
<ImageInput source="image" removeIcon={DeleteIcon}>
<ImageField source="src" title="title" />
</ImageInput>
<FormInspector name="attachments" />
</Wrapper>
);

const i18nProvider = polyglotI18nProvider(() => englishMessages);

const Wrapper = ({ children }) => (
Expand Down

0 comments on commit c933ab5

Please sign in to comment.