import { useEffect, useState, ReactNode, FC, useRef } from 'react';
import { observer } from 'mobx-react';
import {
FilePicker,
ButtonGroup,
Tooltip,
Button,
Card,
Banner,
Grid,
} from '@servicetitan/design-system';
import { Label } from '@servicetitan/form';
import { getImageSize, formatBytes } from '../../../utils/helpers';
export const LOGO_MIN_SIZE = 180;
export const DEFAULT_MIN_DIMENSIONS = { width: LOGO_MIN_SIZE, height: LOGO_MIN_SIZE };
export const DEFAULT_LOGO_TIPS = (
- PNGs with a transparent background work best, but aren't required.
-
Don't know where your logo is? Your social media profile would be a good place to look.
- Use a logo that is at least 180x180px in size.
-
Avoid using a screenshot of your logo, right click and “save as” if you're pulling it
from somewhere else.
);
import * as Styles from './logo-picker.module.less';
export interface LogoPickerProps {
error?: string;
image?: File;
imageUrl?: string;
loaded?: boolean;
maxSize?: number;
minDimensions?: { width: number; height: number };
tips?: ReactNode;
deletable?: boolean;
isGrid?: boolean;
deleteImage(): void;
downloadImage?(): void;
onBadImage?(opts: { category: string; error: string; data: { imageUrl: string } }): void;
onFileChange(files: FileList | null): void;
setError?(error: string): void;
}
export const LogoPicker: FC = observer(
({
deleteImage,
downloadImage,
error,
image,
imageUrl,
loaded = true,
maxSize,
minDimensions = DEFAULT_MIN_DIMENSIONS,
onBadImage,
onFileChange,
setError,
deletable = true,
tips = DEFAULT_LOGO_TIPS,
isGrid = true,
}) => {
const [recommendLargerImage, setRecommendLargerImage] = useState(false);
const [localError, setLocalError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [sizeError, setSizeError] = useState('');
const formattedMaxSize = useRef(formatBytes(maxSize, 2, false));
useEffect(() => {
const handleImageUrlChange = async () => {
if (!imageUrl || !minDimensions) {
return;
}
setIsLoading(true);
try {
const size = await getImageSize(imageUrl);
setRecommendLargerImage(
size.height < minDimensions.height || size.width < minDimensions.width,
);
setLocalError('');
if (setError) {
setError('');
}
} catch {
onBadImage?.({
category: 'LogoPicker',
error: 'Got a bad image',
data: { imageUrl },
});
deleteImage();
setLocalError('File is not a valid image');
setRecommendLargerImage(false);
if (setError) {
setError('File is not a valid image');
}
} finally {
setIsLoading(false);
}
};
handleImageUrlChange();
}, [imageUrl, minDimensions, setError, deleteImage, onBadImage]);
useEffect(() => {
if (!image?.size || !maxSize) {
return;
}
setIsLoading(true);
if (image && image.size > maxSize) {
deleteImage();
setSizeError(`File size should be less than ${formattedMaxSize.current}.`);
if (setError) {
setError(`File size should be less than ${formattedMaxSize.current}.`);
}
} else {
setSizeError('');
if (setError) {
setError('');
}
}
setIsLoading(false);
}, [image, maxSize, setError, deleteImage]);
if (!loaded || isLoading) {
return null;
}
return (
{(error || localError) && (
)}
{sizeError && (
)}
{imageUrl === undefined ? (
) : isGrid ? (
) : (
)}
{recommendLargerImage && minDimensions && (imageUrl || image) && (
)}
{(imageUrl === undefined || recommendLargerImage || localError || sizeError) && (
{tips}
)}
);
},
);
interface LogoCardProps {
imageUrl: string;
image?: File;
deletable?: boolean;
onFileChange(files: FileList | null): void;
downloadImage?(): void;
deleteImage(): void;
}
const LogoCard: FC = ({
imageUrl,
onFileChange,
image,
downloadImage,
deletable = true,
deleteImage,
}) => {
const fileRef = useRef(null);
const handleClick = () => fileRef.current?.click();
return (
);
};