@eightshift/frontend-libs
Version:
A collection of useful frontend utility modules. powered by Eightshift
261 lines (247 loc) • 9.17 kB
JavaScript
import React from 'react';
import { __ } from '@wordpress/i18n';
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button, HStack, VStack, FilePlaceholder, AnimatedVisibility } from '@eightshift/ui-components';
import { icons } from '@eightshift/ui-components/icons';
/**
* A customizable button for managing files from the Media library.
*
* @component
* @param {Object} props - Component props.
* @param {ManageFileButtonType} [props.type] - The type of the button (browse, upload, replace).
* @param {Function} props.onChange - Function that handles the change event.
* @param {string} [props.currentId] - ID of the currently selected item. Used for the 'replace' type, to mark the currently selected item.
* @param {boolean} [props.compact] - Whether the button is compact (icon-only).
* @param {string[]} props.allowedTypes - Determines types of files which are allowed to be uploaded.
* @param {ManageFileButtonKind} [props.kind] - The kind of file to manage. Controls labels and icons on the buttons.
* @param {Object} [props.labels] - Custom UI labels for the buttons. Applies only if `kind` is set to `custom`.
*
* @returns {JSX.Element} The ManageFileButton component.
*
* @typedef {'browse' | 'upload' | 'replace'} ManageFileButtonType
* @typedef {'file' | 'image' | 'video' | 'subtitle' | 'geoJson' | 'lottie' | 'custom'} ManageFileButtonKind
*
* @example
* <ManageFileButton />
*
*/
export const ManageFileButton = (props) => {
const {
type = 'browse',
onChange,
currentId,
labels,
allowedTypes,
kind = 'file',
compact = false,
} = props;
const strings = {
file: {
buttonTooltip: {
browse: __('Select a file from Media library', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a file', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace file', 'eightshift-frontend-libs-tailwind'),
},
buttonLabel: {
browse: __('Select', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace', 'eightshift-frontend-libs-tailwind'),
},
modalTitle: {
browse: __('Select a file', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a file', 'eightshift-frontend-libs-tailwind'),
replace: __('Select a new file', 'eightshift-frontend-libs-tailwind'),
},
buttonIcon: {
browse: icons.itemSelect,
upload: icons.upload,
replace: icons.swap,
},
},
video: {
buttonTooltip: {
browse: __('Select a video from Media library', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a video', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace video', 'eightshift-frontend-libs-tailwind'),
},
modalTitle: {
browse: __('Select a video', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a video', 'eightshift-frontend-libs-tailwind'),
replace: __('Select a new video', 'eightshift-frontend-libs-tailwind'),
},
},
image: {
buttonTooltip: {
browse: __('Select an image from Media library', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload an image', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace image', 'eightshift-frontend-libs-tailwind'),
},
modalTitle: {
browse: __('Select an image', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload an image', 'eightshift-frontend-libs-tailwind'),
replace: __('Select a new image', 'eightshift-frontend-libs-tailwind'),
},
},
subtitle: {
buttonTooltip: {
browse: __('Select a subtitle file', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a subtitle file', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace subtitle file', 'eightshift-frontend-libs-tailwind'),
},
modalTitle: {
browse: __('Select a subtitle file', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a subtitle file', 'eightshift-frontend-libs-tailwind'),
replace: __('Select a new subtitle file', 'eightshift-frontend-libs-tailwind'),
},
},
geoJson: {
buttonTooltip: {
browse: __('Select a GeoJSON file', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a GeoJSON file', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace GeoJSON file', 'eightshift-frontend-libs-tailwind'),
},
modalTitle: {
browse: __('Select a GeoJSON file', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a GeoJSON file', 'eightshift-frontend-libs-tailwind'),
replace: __('Select a new GeoJSON file', 'eightshift-frontend-libs-tailwind'),
},
},
lottie: {
buttonTooltip: {
browse: __('Select a Lottie animation', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a Lottie animation', 'eightshift-frontend-libs-tailwind'),
replace: __('Replace Lottie animation', 'eightshift-frontend-libs-tailwind'),
},
modalTitle: {
browse: __('Select a Lottie animation', 'eightshift-frontend-libs-tailwind'),
upload: __('Upload a Lottie animation', 'eightshift-frontend-libs-tailwind'),
replace: __('Select a new Lottie animation', 'eightshift-frontend-libs-tailwind'),
},
},
custom: {
buttonTooltip: labels?.buttonTooltip,
buttonLabel: labels?.buttonLabel,
modalTitle: labels?.modalTitle,
buttonIcon: labels?.buttonIcon,
},
};
const buttonTooltip = strings?.[kind]?.buttonTooltip?.[type] ?? strings.file.buttonTooltip?.[type];
const buttonLabel = strings?.[kind]?.buttonLabel?.[type] ?? strings.file.buttonLabel?.[type];
const buttonIcon = strings?.[kind]?.buttonIcon?.[type] ?? strings.file.buttonIcon?.[type];
const modalTitle = strings?.[kind]?.modalTitle?.[type] ?? strings.file.modalTitle?.[type];
return (
<MediaUploadCheck>
<MediaUpload
onSelect={({ id, url, ...rest }) => onChange({ id, url, ...rest })}
allowedTypes={allowedTypes}
value={type === 'replace' && currentId}
title={modalTitle}
mode={type === 'upload' ? 'upload' : 'browse'}
render={({ open }) => (
<Button
onPress={open}
icon={compact && buttonIcon}
tooltip={buttonTooltip}
>
{!compact && buttonLabel}
</Button>
)}
/>
</MediaUploadCheck>
);
};
/**
* Renders a component for managing a media file
*
* @component
* @param {Object} props - Component props.
* @param {Function} props.onChange - The function that handles the change event.
* @param {string} props.fileId - ID of the currently selected file. Used to mark the currently selected item when replacing the file.
* @param {string} props.fileName - URL of the currently selected image.
* @param {boolean} [props.noDelete] - If `true`, the delete button will be hidden.
* @param {boolean} [props.noUpload] - If `true`, the upload button will be hidden.
* @param {string[]} props.allowedTypes - Determines types of files which are allowed to be uploaded.
* @param {FileKind} [props.kind] - The kind of file to manage.
* @param {Object} [props.labels] - Custom UI labels for the buttons. Applies only if `kind` is set to `custom`.
*
* @returns {JSX.Element} The FileSelector component.
*
* @typedef {'file' | 'image' | 'video' | 'subtitle' | 'geoJson' | 'lottie' | 'custom'} FileKind
*
* @example
* <FileSelector
* onChange={onChange}
* fileId={fileId}
* fileName={fileName}
* allowedTypes={['video']}
* />
*
*/
export const FileSelector = (props) => {
const { onChange, fileId, fileName, noDelete, noUpload, labels, allowedTypes, kind = 'file' } = props;
const commonManageFileButtonProps = {
onChange,
allowedTypes,
kind,
labels,
};
const removeTooltips = {
file: __('Remove file', 'eightshift-frontend-libs-tailwind'),
image: __('Remove image', 'eightshift-frontend-libs-tailwind'),
video: __('Remove video', 'eightshift-frontend-libs-tailwind'),
subtitle: __('Remove subtitle file', 'eightshift-frontend-libs-tailwind'),
geoJson: __('Remove GeoJSON file', 'eightshift-frontend-libs-tailwind'),
lottie: __('Remove Lottie animation', 'eightshift-frontend-libs-tailwind'),
custom: labels?.removeTooltip,
};
const fileIcons = {
image: icons.imageFile,
video: icons.videoFile,
subtitle: icons.closedCaptions,
geoJson: icons.fileMetadata,
lottie: icons.animationFile,
custom: labels?.removeIcon,
};
return (
<VStack noWrap>
<FilePlaceholder
icon={fileIcons[kind] ?? icons.file}
fileName={fileName}
>
<HStack
noWrap
className='es:pl-1'
>
<ManageFileButton {...commonManageFileButtonProps} />
{!noUpload && (
<ManageFileButton
{...commonManageFileButtonProps}
type='upload'
compact
/>
)}
</HStack>
</FilePlaceholder>
<AnimatedVisibility
visible={fileName}
noInitial
>
<HStack noWrap>
<ManageFileButton
{...commonManageFileButtonProps}
currentId={fileId}
type='replace'
/>
{!noDelete && (
<Button
icon={icons.trash}
tooltip={removeTooltips[kind] ?? removeTooltips.file}
onPress={() => onChange({ id: undefined, url: undefined })}
type='danger'
/>
)}
</HStack>
</AnimatedVisibility>
</VStack>
);
};