Skip to content

Commit

Permalink
allow for collections using portrait trails (#1590)
Browse files Browse the repository at this point in the history
* use hard coded constant so all trails are required to be portrait

* allow grid to select either image type, add state for the dimension of the selected replacement image, use to style the ImageContainer

* bug fix? aspect ratio test should use absolute difference

* use the criteria to set the gird url, not a separate prop

* export the default trail image criteria from constants

* remove unused imports

* shorted var name, comments about use of constant criteria

* dont need state for imageDims - can get from props.input.value
  • Loading branch information
dblatcher authored Jun 10, 2024
1 parent fddcadd commit d5e30c9
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 23 deletions.
4 changes: 2 additions & 2 deletions fronts-client/src/components/card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import {
ValidationResponse,
} from 'util/validateImageSrc';
import {
cardImageCriteria,
editionsCardImageCriteria,
DRAG_DATA_CARD_IMAGE_OVERRIDE,
defaultCardTrailImageCriteria,
} from 'constants/image';
import Sublinks from '../FrontsEdit/CollectionComponents/Sublinks';
import {
Expand Down Expand Up @@ -346,7 +346,7 @@ class Card extends React.Component<CardContainerProps> {
const isEditionsMode = this.props.editMode === 'editions';
const imageCriteria = isEditionsMode
? editionsCardImageCriteria
: cardImageCriteria;
: defaultCardTrailImageCriteria;

// Our drag contains Grid data
validateImageEvent(e, this.props.frontId, imageCriteria)
Expand Down
8 changes: 5 additions & 3 deletions fronts-client/src/components/form/ArticleMetaForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ import {
import { CapiFields } from 'util/form';
import { Dispatch } from 'types/Store';
import {
cardImageCriteria,
landScapeCardImageCriteria,
editionsCardImageCriteria,
editionsMobileCardImageCriteria,
editionsTabletCardImageCriteria,
defaultCardTrailImageCriteria,
} from 'constants/image';
import { selectors as collectionSelectors } from 'bundles/collectionsBundle';
import { getContributorImage } from 'util/CAPIUtils';
Expand Down Expand Up @@ -284,7 +285,8 @@ const RenderSlideshow = ({
name={name}
component={InputImage}
small
criteria={cardImageCriteria}
// TO DO - will slideshows always be landscape?
criteria={landScapeCardImageCriteria}
frontId={frontId}
isSelected={index === slideshowIndex}
isInvalid={isInvalidCaptionLength(index)}
Expand Down Expand Up @@ -682,7 +684,7 @@ class FormComponent extends React.Component<Props, FormComponentState> {
criteria={
isEditionsMode
? editionsCardImageCriteria
: cardImageCriteria
: defaultCardTrailImageCriteria
}
frontId={frontId}
defaultImageUrl={
Expand Down
103 changes: 87 additions & 16 deletions fronts-client/src/components/inputs/InputImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,22 @@ import {
WarningIcon,
} from '../icons/Icons';
import imageDragIcon from 'images/icons/image-drag-icon.svg';
import { DRAG_DATA_GRID_IMAGE_URL } from 'constants/image';
import {
DRAG_DATA_GRID_IMAGE_URL,
portraitCardImageCriteria,
} from 'constants/image';
import ImageDragIntentIndicator from 'components/image/ImageDragIntentIndicator';
import { EditMode } from 'types/EditMode';
import { selectEditMode } from '../../selectors/pathSelectors';
import CircularIconContainer from '../icons/CircularIconContainer';
import { error } from '../../styleConstants';

// assuming any portrait image (ie height>width)
// is in the 4:5 ratio for purposes of styling
// the image container
const ImageContainer = styled.div<{
small?: boolean;
portrait?: boolean;
}>`
display: flex;
flex-direction: column;
Expand All @@ -47,6 +54,13 @@ const ImageContainer = styled.div<{
padding: 40%;`}
height: ${(props) => (props.small ? '0' : '115px')};
transition: background-color 0.15s;
${({ portrait, small }) =>
portrait &&
`
width: ${small ? 50 : 200}px;
height: ${small ? 62 : 250}px;
`}
`;

const AddImageButton = styled(ButtonDefault)<{ small?: boolean }>`
Expand Down Expand Up @@ -245,16 +259,30 @@ const dragImage = new Image();
dragImage.src = imageDragIcon;

class InputImage extends React.Component<ComponentProps, ComponentState> {
public state = {
isDragging: false,
modalOpen: false,
imageSrc: '',
confirmDelete: false,
cancelDeleteTimeout: undefined,
} as ComponentState;

private inputRef = React.createRef<HTMLInputElement>();

public constructor(props: ComponentProps) {
super(props);

const { value } = props.input;
const valueRecord =
value && typeof value === 'object'
? (value as Record<string, unknown>)
: undefined;

const { src } = valueRecord ?? {};
const imageSrc = typeof src === 'string' ? src : '';


this.state = {
isDragging: false,
modalOpen: false,
imageSrc,
confirmDelete: false,
cancelDeleteTimeout: undefined,
} as ComponentState;
}

public render() {
const {
small = false,
Expand All @@ -270,6 +298,8 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
isInvalid,
} = this.props;

const imageDims = this.getCurrentImageDimensions();

if (!gridUrl) {
return (
<div>
Expand All @@ -279,12 +309,19 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
}

const gridSearchUrl =
editMode === 'editions' ? `${gridUrl}` : `${gridUrl}?cropType=landscape`;
editMode === 'editions' ? `${gridUrl}` : this.criteriaToGridUrl();
const hasImage = !useDefault && !!input.value && !!input.value.thumb;
const imageUrl =
!useDefault && input.value && input.value.thumb
? input.value.thumb
: defaultImageUrl;

const portraitImage = !!(
!useDefault &&
imageDims &&
imageDims.height > imageDims.width
);

return (
<InputImageContainer
small={small}
Expand All @@ -305,7 +342,7 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
onDragIntentStart={() => this.setState({ isDragging: true })}
onDragIntentEnd={() => this.setState({ isDragging: false })}
>
<ImageContainer small={small}>
<ImageContainer small={small} portrait={portraitImage}>
<ImageComponent
style={{
backgroundImage: `url(${imageUrl}`,
Expand Down Expand Up @@ -448,15 +485,12 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {

private validateAndGetImage = () => {
events.imageAdded(this.props.frontId, 'paste');

validateImageSrc(
this.state.imageSrc,
this.props.frontId,
this.props.criteria
)
.then((mediaItem) => {
this.props.input.onChange(mediaItem);
})
.then(this.props.input.onChange)
.catch((err) => {
alert(err);
// tslint:disable-next-line no-console
Expand All @@ -465,7 +499,9 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
this.setState({ imageSrc: '' });
};

private clearField = () => this.props.input.onChange(null);
private clearField = () => {
return this.props.input.onChange(null);
};

private validMessage(data: GridData) {
return data && data.crop && data.crop.data && data.image && data.image.data;
Expand Down Expand Up @@ -520,6 +556,41 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
this.setState({ modalOpen: true });
window.addEventListener('message', this.onMessage, false);
};

private criteriaToGridUrl = (): string => {
const { criteria, gridUrl } = this.props;

if (!criteria) {
return `${gridUrl}?cropType=portrait,landscape`;
}

// assumes the only criteria that will be passed as props the defined
// constants for portrait(4:5) and landscape (5:3)
const usingPortrait =
portraitCardImageCriteria.widthAspectRatio == criteria.widthAspectRatio &&
portraitCardImageCriteria.heightAspectRatio == criteria.heightAspectRatio;

return usingPortrait
? `${gridUrl}?cropType=portrait`
: `${gridUrl}?cropType=landscape`;
};

private getCurrentImageDimensions = () => {
const { value } = this.props.input;
const valueRecord =
value && typeof value === 'object'
? (value as Record<string, unknown>)
: undefined;

const { height, width } = valueRecord ?? {};

return typeof height === 'number' && typeof width === 'number'
? {
height,
width,
}
: undefined;
};
}

const mapStateToProps = (state: State) => {
Expand Down
10 changes: 9 additions & 1 deletion fronts-client/src/constants/image.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
export const cardImageCriteria = {
export const landScapeCardImageCriteria = {
minWidth: 400,
widthAspectRatio: 5,
heightAspectRatio: 3,
};

export const portraitCardImageCriteria = {
minWidth: 400,
widthAspectRatio: 4,
heightAspectRatio: 5,
};

export const defaultCardTrailImageCriteria = landScapeCardImageCriteria;

export const editionsCardImageCriteria = {
minWidth: 400,
};
Expand Down
2 changes: 1 addition & 1 deletion fronts-client/src/util/validateImageSrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ function validateActualImage(image: ImageDescription, frontId?: string) {
return reject(
new Error(`Images cannot be less than ${minWidth} pixels wide`)
);
} else if (criteriaRatio && criteriaRatio - ratio > 0.01) {
} else if (criteriaRatio && Math.abs(criteriaRatio - ratio) > 0.01) {
return reject(
new Error(
`Images must have a ${widthAspectRatio || ''}:${
Expand Down

0 comments on commit d5e30c9

Please sign in to comment.