Skip to content

Commit

Permalink
Improve the ImageInput for custom components (#1047)
Browse files Browse the repository at this point in the history
  • Loading branch information
connor-baer authored Jul 21, 2021
1 parent 96e03ac commit 19fa9c2
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 232 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-snails-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': patch
---

Fixed the alignment of the icon next to a validation hint when the text is center-aligned.
5 changes: 5 additions & 0 deletions .changeset/good-buttons-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': patch
---

Passed the click event to the `onClear` prop of the ImageInput.
5 changes: 5 additions & 0 deletions .changeset/rotten-balloons-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': patch
---

Tweaked the ImageInput to work with images with arbitrary border-radii.
4 changes: 4 additions & 0 deletions .storybook/components/Teaser.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const Wrapper = styled(Card)(
margin-right: ${theme.spacings.giga};
min-height: ${CARD_HEIGHT};
}
*:last-child {
margin-bottom: 0;
}
`,
);

Expand Down
139 changes: 78 additions & 61 deletions packages/circuit-ui/components/ImageInput/ImageInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
useRef,
InputHTMLAttributes,
ChangeEvent,
MouseEvent,
KeyboardEvent,
Fragment,
} from 'react';
import { css, jsx } from '@emotion/core';
Expand Down Expand Up @@ -47,11 +49,11 @@ export interface ImageInputProps
/**
* A callback function to call when the user has selected an image.
*/
onChange: (event: File) => Promise<void>;
onChange: (event: File) => void | Promise<void>;
/**
* A callback function to call when the input is cleared.
*/
onClear: () => void;
onClear: (event: MouseEvent | KeyboardEvent) => void;
/**
* An accessible label for the "clear" icon button.
*/
Expand Down Expand Up @@ -88,92 +90,107 @@ const InputWrapper = styled.div`
const HiddenInput = styled.input(
({ theme }) => css`
${hideVisually()};
&:focus + label {
&:focus + label > *:last-child {
${focusOutline({ theme })};
}
`,
);

type StyledLabelProps = StyleProps & { isLoading: boolean; invalid: boolean };

const baseLabelStyles = css`
border-radius: 12px;
overflow: hidden;
&:hover {
cursor: pointer;
const baseLabelStyles = ({ theme }: StyleProps) => css`
cursor: pointer;
&::before {
content: '';
position: absolute;
top: 0;
left: 0%;
width: 100%;
height: 100%;
border-radius: 12px;
pointer-events: none;
background-color: ${theme.colors.black};
opacity: 0;
transition: opacity ${theme.transitions.default};
}
`;
const addButtonStyles = ({ theme }: StyledLabelProps) => css`
&:hover {
& > button {
background-color: ${theme.colors.p700};
border-color: ${theme.colors.p700};
}
> *:last-child {
transition: box-shadow ${theme.transitions.default};
}
&:active {
& > button {
background-color: ${theme.colors.p900};
border-color: ${theme.colors.p900};
@supports (-webkit-filter: brightness(1)) or (filter: brightness(1)) {
transition: filter ${theme.transitions.default};
&::before {
content: none;
}
}
`;

const invalidLabelStyles = ({ theme, invalid }: StyledLabelProps) =>
invalid &&
css`
&::after {
content: '';
position: absolute;
top: 0;
left: 0%;
width: 100%;
height: 100%;
border-radius: 12px;
box-shadow: inset 0 0 0 2px ${theme.colors.danger};
> *:last-child {
box-shadow: 0 0 0 2px ${theme.colors.danger};
}
&:hover::after {
box-shadow: inset 0 0 0 2px ${theme.colors.r700};
&:hover > *:last-child {
box-shadow: 0 0 0 2px ${theme.colors.r700};
}
`;

const overlayLabelStyles = ({ theme, isLoading }: StyledLabelProps) => css`
&::before {
// @FIXME replace with a brightness filter when we drop IE support
content: '';
position: absolute;
top: 0;
left: 0%;
width: 100%;
height: 100%;
border-radius: 12px;
background-color: ${theme.colors.black};
opacity: 0;
pointer-events: none;
${isLoading &&
css`
opacity: 0.4;
`}
const loadingLabelStyles = ({ isLoading }: StyledLabelProps) => {
if (isLoading) {
return css`
&::before {
opacity: 0.4;
}
@supports (-webkit-filter: brightness(1)) or (filter: brightness(1)) {
filter: brightness(0.6);
}
`;
}
&:hover::before {
${!isLoading &&
css`

return css`
&:hover::before {
opacity: 0.1;
`}
}
&:active::before {
${!isLoading &&
css`
}
&:active::before {
opacity: 0.2;
`}
}
@supports (-webkit-filter: brightness(1)) or (filter: brightness(1)) {
&:hover {
filter: brightness(0.9);
}
&:active {
filter: brightness(0.8);
}
}
`;
};

const addButtonStyles = ({ theme }: StyledLabelProps) => css`
&:hover {
& > button {
background-color: ${theme.colors.p700};
border-color: ${theme.colors.p700};
}
}
&:active {
& > button {
background-color: ${theme.colors.p900};
border-color: ${theme.colors.p900};
}
}
`;

const StyledLabel = styled(Label)<StyledLabelProps>(
baseLabelStyles,
addButtonStyles,
invalidLabelStyles,
overlayLabelStyles,
loadingLabelStyles,
addButtonStyles,
);

const ActionButton = styled(IconButton)(
Expand Down Expand Up @@ -251,7 +268,7 @@ export const ImageInput = ({
// URL.createObjectURL is not supported in Node, but the handleChange will only run client-side
// eslint-disable-next-line node/no-unsupported-features/node-builtins
setPreviewImage(URL.createObjectURL(file));
onChange(file)
Promise.resolve(onChange(file))
.then(() => setIsLoading(false))
.catch(() => setIsLoading(false));
};
Expand All @@ -262,10 +279,10 @@ export const ImageInput = ({
}
};

const handleClear = () => {
const handleClear = (event: MouseEvent | KeyboardEvent) => {
clearInputElement();
setPreviewImage('');
onClear();
onClear(event);
};

/**
Expand Down
Loading

1 comment on commit 19fa9c2

@vercel
Copy link

@vercel vercel bot commented on 19fa9c2 Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.