mirror of
https://github.com/strapi/strapi.git
synced 2025-12-24 13:43:41 +00:00
added audio preview in content builder
This commit is contained in:
parent
ba558ca530
commit
d32e31d488
@ -32,7 +32,8 @@
|
||||
"allowedTypes": [
|
||||
"files",
|
||||
"images",
|
||||
"videos"
|
||||
"videos",
|
||||
"audios"
|
||||
],
|
||||
"pluginOptions": {}
|
||||
},
|
||||
|
||||
@ -11,7 +11,8 @@ const options = [
|
||||
children: [
|
||||
{ label: 'images (JPEG, PNG, GIF, SVG, TIFF, ICO, DVU)', value: 'images' },
|
||||
{ label: 'videos (MPEG, MP4, Quicktime, WMV, AVI, FLV)', value: 'videos' },
|
||||
{ label: 'files (CSV, ZIP, MP3, PDF, Excel, JSON, ...)', value: 'files' },
|
||||
{ label: 'audios (MP3, WAV, OGG)', value: 'audios' },
|
||||
{ label: 'files (CSV, ZIP, PDF, Excel, JSON, ...)', value: 'files' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@ -215,7 +215,7 @@ const types = {
|
||||
required: validators.required(),
|
||||
allowedTypes: yup
|
||||
.array()
|
||||
.of(yup.string().oneOf(['images', 'videos', 'files']))
|
||||
.of(yup.string().oneOf(['images', 'videos', 'files', 'audios']))
|
||||
.min(1)
|
||||
.nullable(),
|
||||
};
|
||||
|
||||
@ -266,7 +266,7 @@ const reducer = (state = initialState, action) =>
|
||||
dataToSet = options;
|
||||
} else if (attributeType === 'media') {
|
||||
dataToSet = {
|
||||
allowedTypes: ['images', 'files', 'videos'],
|
||||
allowedTypes: ['images', 'files', 'videos', 'audios'],
|
||||
type: 'media',
|
||||
multiple: true,
|
||||
...options,
|
||||
|
||||
@ -582,7 +582,7 @@ describe('CTB | components | FormModal | reducer | actions', () => {
|
||||
modifiedData: {
|
||||
type: 'media',
|
||||
multiple: true,
|
||||
allowedTypes: ['images', 'files', 'videos'],
|
||||
allowedTypes: ['images', 'files', 'videos', 'audios'],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ export default {
|
||||
type: 'media',
|
||||
multiple: false,
|
||||
required: false,
|
||||
allowedTypes: ['files', 'images', 'videos'],
|
||||
allowedTypes: ['files', 'images', 'videos', 'audios'],
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
localized: true,
|
||||
|
||||
@ -53,7 +53,7 @@ const getTypeShape = (attribute, { modelType, attributes } = {}) => {
|
||||
required: validators.required,
|
||||
allowedTypes: yup
|
||||
.array()
|
||||
.of(yup.string().oneOf(['images', 'videos', 'files']))
|
||||
.of(yup.string().oneOf(['images', 'videos', 'files', 'audios']))
|
||||
.min(1),
|
||||
};
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { prefixFileUrlWithBackendUrl, getFileExtension } from '@strapi/helper-pl
|
||||
import { ImageAssetCard } from './ImageAssetCard';
|
||||
import { VideoAssetCard } from './VideoAssetCard';
|
||||
import { DocAssetCard } from './DocAssetCard';
|
||||
import { AudioAssetCard } from './AudioAssetCard';
|
||||
import { AssetType, AssetDefinition } from '../../constants';
|
||||
import { createAssetUrl } from '../../utils/createAssetUrl';
|
||||
import toSingularTypes from '../../utils/toSingularTypes';
|
||||
@ -64,7 +65,31 @@ export const AssetCard = ({
|
||||
);
|
||||
}
|
||||
|
||||
const canSelectAsset = singularTypes.includes('file') && !['video', 'image'].includes(fileType);
|
||||
if (asset.mime.includes(AssetType.Audio)) {
|
||||
const canSelectAsset = singularTypes.includes(fileType);
|
||||
|
||||
if (!canSelectAsset && !isSelected) {
|
||||
handleSelect = undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<AudioAssetCard
|
||||
id={asset.id}
|
||||
key={asset.id}
|
||||
name={asset.name}
|
||||
extension={getFileExtension(asset.ext)}
|
||||
url={local ? asset.url : createAssetUrl(asset, true)}
|
||||
mime={asset.mime}
|
||||
onEdit={onEdit ? () => onEdit(asset) : undefined}
|
||||
onSelect={handleSelect}
|
||||
selected={isSelected}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const canSelectAsset =
|
||||
singularTypes.includes('file') && !['video', 'image', 'audio'].includes(fileType);
|
||||
|
||||
if (!canSelectAsset && !isSelected) {
|
||||
handleSelect = undefined;
|
||||
@ -74,7 +99,7 @@ export const AssetCard = ({
|
||||
};
|
||||
|
||||
AssetCard.defaultProps = {
|
||||
allowedTypes: ['images', 'files', 'videos'],
|
||||
allowedTypes: ['images', 'files', 'videos', 'audios'],
|
||||
isSelected: false,
|
||||
// Determine if the asset is loaded locally or from a remote resource
|
||||
local: false,
|
||||
|
||||
@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardAsset,
|
||||
CardBadge,
|
||||
CardBody,
|
||||
CardCheckbox,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardSubtitle,
|
||||
} from '@strapi/design-system/Card';
|
||||
import { IconButton } from '@strapi/design-system/IconButton';
|
||||
import Pencil from '@strapi/icons/Pencil';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
import { AudioPreview } from './AudioPreview';
|
||||
import { getTrad } from '../../utils';
|
||||
|
||||
const Extension = styled.span`
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const AudioPreviewWrapper = styled(Box)`
|
||||
canvas,
|
||||
audio {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: ${({ size }) => (size === 'M' ? 164 / 16 : 88 / 16)}rem;
|
||||
}
|
||||
`;
|
||||
|
||||
export const AudioAssetCard = ({
|
||||
name,
|
||||
extension,
|
||||
url,
|
||||
mime,
|
||||
selected,
|
||||
onSelect,
|
||||
onEdit,
|
||||
size,
|
||||
}) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardAsset size={size}>
|
||||
<AudioPreviewWrapper size={size}>
|
||||
<AudioPreview url={url} mime={mime} alt={name} />
|
||||
</AudioPreviewWrapper>
|
||||
</CardAsset>
|
||||
{onSelect && <CardCheckbox value={selected} onValueChange={onSelect} />}
|
||||
{onEdit && (
|
||||
<CardAction position="end">
|
||||
<IconButton
|
||||
label={formatMessage({ id: getTrad('control-card.edit'), defaultMessage: 'Edit' })}
|
||||
icon={<Pencil />}
|
||||
onClick={onEdit}
|
||||
/>
|
||||
</CardAction>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<CardContent>
|
||||
<Box paddingTop={1}>
|
||||
<CardTitle as="h2">{name}</CardTitle>
|
||||
</Box>
|
||||
<CardSubtitle>
|
||||
<Extension>{extension}</Extension>
|
||||
</CardSubtitle>
|
||||
</CardContent>
|
||||
<CardBadge>
|
||||
{formatMessage({ id: getTrad('settings.section.audio.label'), defaultMessage: 'Audio' })}
|
||||
</CardBadge>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
AudioAssetCard.defaultProps = {
|
||||
onSelect: undefined,
|
||||
onEdit: undefined,
|
||||
selected: false,
|
||||
size: 'M',
|
||||
};
|
||||
|
||||
AudioAssetCard.propTypes = {
|
||||
extension: PropTypes.string.isRequired,
|
||||
mime: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onSelect: PropTypes.func,
|
||||
onEdit: PropTypes.func,
|
||||
url: PropTypes.string.isRequired,
|
||||
selected: PropTypes.bool,
|
||||
size: PropTypes.oneOf(['S', 'M']),
|
||||
};
|
||||
@ -0,0 +1,24 @@
|
||||
/* eslint-disable jsx-a11y/media-has-caption */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
||||
|
||||
export const AudioPreview = ({ url, mime, alt }) => {
|
||||
return (
|
||||
<Box>
|
||||
<audio controls>
|
||||
<source src={url} type={mime} />
|
||||
</audio>
|
||||
<VisuallyHidden as="figcaption">{alt}</VisuallyHidden>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
AudioPreview.defaultProps = {};
|
||||
|
||||
AudioPreview.propTypes = {
|
||||
alt: PropTypes.string.isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
mime: PropTypes.string.isRequired,
|
||||
};
|
||||
@ -44,6 +44,11 @@ export const UploadingAssetCard = ({ asset, onCancel, onStatusChange, addUploade
|
||||
id: getTrad('settings.section.video.label'),
|
||||
defaultMessage: 'Video',
|
||||
});
|
||||
} else if (asset.type === AssetType.Audio) {
|
||||
badgeContent = formatMessage({
|
||||
id: getTrad('settings.section.audio.label'),
|
||||
defaultMessage: 'Audio',
|
||||
});
|
||||
} else {
|
||||
badgeContent = formatMessage({
|
||||
id: getTrad('settings.section.doc.label'),
|
||||
|
||||
@ -75,7 +75,7 @@ export const AssetList = ({
|
||||
};
|
||||
|
||||
AssetList.defaultProps = {
|
||||
allowedTypes: ['images', 'files', 'videos'],
|
||||
allowedTypes: ['images', 'files', 'videos', 'audios'],
|
||||
onEditAsset: undefined,
|
||||
size: 'M',
|
||||
onReorderAsset: undefined,
|
||||
|
||||
@ -27,6 +27,14 @@ export const AssetPreview = forwardRef(({ mime, url, name }, ref) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (mime.includes(AssetType.Audio)) {
|
||||
return (
|
||||
<audio controls src={url} ref={ref}>
|
||||
<track label={name} default kind="captions" srcLang={lang} src="" />
|
||||
</audio>
|
||||
);
|
||||
}
|
||||
|
||||
if (mime.includes('pdf')) {
|
||||
return (
|
||||
<CardAsset justifyContent="center">
|
||||
|
||||
@ -27,7 +27,7 @@ export const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes }) =>
|
||||
};
|
||||
|
||||
MediaLibraryDialog.defaultProps = {
|
||||
allowedTypes: ['files', 'images', 'videos'],
|
||||
allowedTypes: ['files', 'images', 'videos', 'audios'],
|
||||
};
|
||||
|
||||
MediaLibraryDialog.propTypes = {
|
||||
|
||||
@ -6,6 +6,7 @@ import { Box } from '@strapi/design-system/Box';
|
||||
import { Flex } from '@strapi/design-system/Flex';
|
||||
import { AssetType, AssetDefinition } from '../../../constants';
|
||||
import { VideoPreview } from '../../AssetCard/VideoPreview';
|
||||
import { AudioPreview } from '../../AssetCard/AudioPreview';
|
||||
import { createAssetUrl } from '../../../utils/createAssetUrl';
|
||||
|
||||
const DocAsset = styled(Flex)`
|
||||
@ -33,6 +34,16 @@ export const CarouselAsset = ({ asset }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (asset.mime.includes(AssetType.Audio)) {
|
||||
return (
|
||||
<AudioPreview
|
||||
url={createAssetUrl(asset, true)}
|
||||
mime={asset.mime}
|
||||
alt={asset.alternativeText || asset.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (asset.mime.includes(AssetType.Image)) {
|
||||
return (
|
||||
<Box
|
||||
|
||||
@ -24,7 +24,7 @@ export const MediaLibraryInput = ({
|
||||
value,
|
||||
required,
|
||||
}) => {
|
||||
const fieldAllowedTypes = allowedTypes || ['files', 'images', 'videos'];
|
||||
const fieldAllowedTypes = allowedTypes || ['files', 'images', 'videos', 'audios'];
|
||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||
const [step, setStep] = useState(undefined);
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
@ -180,7 +180,7 @@ export const MediaLibraryInput = ({
|
||||
};
|
||||
|
||||
MediaLibraryInput.defaultProps = {
|
||||
attribute: { allowedTypes: ['videos', 'files', 'images'] },
|
||||
attribute: { allowedTypes: ['videos', 'files', 'images', 'audios'] },
|
||||
disabled: false,
|
||||
description: undefined,
|
||||
error: undefined,
|
||||
|
||||
@ -4,6 +4,7 @@ export const AssetType = {
|
||||
Video: 'video',
|
||||
Image: 'image',
|
||||
Document: 'doc',
|
||||
Audio: 'audio',
|
||||
};
|
||||
|
||||
export const AssetSource = {
|
||||
|
||||
@ -7,6 +7,9 @@ export const typeFromMime = mime => {
|
||||
if (mime.includes(AssetType.Video)) {
|
||||
return AssetType.Video;
|
||||
}
|
||||
if (mime.includes(AssetType.Audio)) {
|
||||
return AssetType.Audio;
|
||||
}
|
||||
|
||||
return AssetType.Document;
|
||||
};
|
||||
|
||||
@ -73,7 +73,7 @@ module.exports = plop => {
|
||||
}
|
||||
|
||||
if (answer.attributeType === 'media') {
|
||||
val.allowedTypes = ['images', 'files', 'videos'];
|
||||
val.allowedTypes = ['images', 'files', 'videos', 'audios'];
|
||||
val.multiple = answer.multiple;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user