Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow for collections using portrait trails #1590

Merged
merged 8 commits into from
Jun 10, 2024
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
Loading