@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
368 lines (366 loc) • 11.5 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useState } from 'react';
import { makeStyles } from 'tss-react/mui';
import Typography from '@mui/material/Typography';
import SwipeableViews from 'react-swipeable-views';
// @ts-ignore
import { autoPlay } from 'react-swipeable-views-utils';
import MobileStepper from '../MobileStepper/MobileStepper';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import Fab from '@mui/material/Fab';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Grid from '@mui/material/Grid';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import Alert from '@mui/material/Alert';
import { backgroundColor } from '../../styles/theme';
// @ts-ignore
import { fadeIn } from 'react-animations';
import PrimaryButton from '../PrimaryButton';
import PluginDocumentation from '../PluginDocumentation';
const useStyles = makeStyles()((theme) => ({
fadeIn: {
animation: `${fadeIn} 1s`
},
detailsView: {
height: '100%',
overflow: 'auto'
},
topBar: {
display: 'flex',
padding: '20px',
alignItems: 'center'
},
carouselImg: {
width: '100%',
height: '340px',
objectFit: 'contain'
},
detailsContainer: {
position: 'relative',
padding: '20px'
},
dots: {
background: 'none',
borderTop: '1px solid #e4e3e3',
height: '30px',
padding: '0',
cursor: 'pointer',
'& .MuiMobileStepper-dot': {
padding: '7px',
margin: '4px',
'&:hover': {
background: 'gray'
}
}
},
useBtn: {
marginLeft: 'auto',
maxHeight: '36px'
},
circleBtn: {
color: '#4F4F4F',
backgroundColor: '#FFFFFF',
marginRight: '30px',
'&:hover': {
backgroundColor: '#FFFFFF'
}
},
section: {
marginBottom: '5px'
},
sectionChips: {
display: 'flex',
padding: 0,
alignitems: 'center',
marginTop: '10px'
},
bold: {
fontWeight: 'bold'
},
video: {
width: '100%',
height: '300px',
outline: 'none',
background: backgroundColor
},
chip: {
fontSize: '12px',
color: 'gray',
marginRight: theme.spacing(1),
backgroundColor: theme.palette.background.default,
padding: '5px',
borderRadius: '5px',
'& label': {
display: 'block',
marginBottom: 0,
fontWeight: 400
},
'& span': {
color: theme.palette.text.primary
}
},
link: {
color: theme.palette.text.secondary,
'& svg': {
verticalAlign: 'sub',
fontSize: '1.1rem'
}
},
background: {
background: theme.palette.background.default,
height: '340px'
},
detailsNotCompatible: {
marginBottom: '15px',
backgroundColor: theme.palette.error.light,
'& .MuiAlert-icon': {
color: theme.palette.error.main
},
'& .MuiAlert-message': {
color: theme.palette.error.contrastText
}
}
}));
const messages = defineMessages({
use: {
id: 'common.use',
defaultMessage: 'Use'
},
version: {
id: 'common.version',
defaultMessage: 'Version'
},
developer: {
id: 'common.developer',
defaultMessage: 'Developer'
},
website: {
id: 'common.website',
defaultMessage: 'Website'
},
license: {
id: 'common.license',
defaultMessage: 'License'
},
craftercms: {
id: 'common.craftercms',
defaultMessage: 'CrafterCMS'
}
});
const AutoPlaySwipeableViews = autoPlay(SwipeableViews);
export function PluginDetailsView(props) {
const { classes, cx } = useStyles();
const [play, setPlay] = useState(false);
const {
plugin,
changeImageSlideInterval = 5000,
onBlueprintSelected,
onCloseDetails,
selectedImageSlideIndex = 0,
isMarketplacePlugin = true,
inUse = false,
usePermission = true,
beingInstalled = false,
useLabel
} = props;
const [index, setIndex] = useState(selectedImageSlideIndex);
const { media, name, description, version, license, developer, website, compatible } = plugin;
const fullVersion = version ? `${version.major}.${version.minor}.${version.patch}` : null;
const { formatMessage } = useIntl();
function handleChangeIndex(value) {
setIndex(value);
}
function onDotClick(e, step) {
e.stopPropagation();
setIndex(step);
}
function handlePlay() {
setPlay(true);
}
function handleEnded() {
setPlay(false);
}
function renderMedias() {
let videos = media && media.videos ? Object.assign(Object.assign({}, media.videos), { type: 'video' }) : [];
videos = videos.length ? videos.map((obj) => Object.assign(Object.assign({}, obj), { type: 'video' })) : [];
let screenshots = media && media.screenshots ? media.screenshots : [];
const merged = [...videos, ...screenshots];
return merged.map((item, index) => {
if (item.type !== 'video') {
return React.createElement(
'div',
{ key: index, className: classes.background },
React.createElement('img', { className: classes.carouselImg, src: item.url, alt: item.description })
);
} else {
return React.createElement(
'video',
{
key: index,
controls: true,
className: classes.video,
autoPlay: play,
onPlaying: handlePlay,
onEnded: handleEnded
},
React.createElement('source', { src: item.url, type: 'video/mp4' }),
'Your browser does not support the video tag.'
);
}
});
}
let steps = 0;
plugin.media && plugin.media.screenshots ? (steps = plugin.media.screenshots.length) : (steps = 0);
plugin.media && plugin.media.videos ? (steps += plugin.media.videos.length) : (steps += 0);
return React.createElement(
'div',
{ className: cx(classes.detailsView, classes.fadeIn) },
React.createElement(
'div',
{ className: classes.topBar },
React.createElement(
Fab,
{ 'aria-label': 'back', className: classes.circleBtn, onClick: onCloseDetails },
React.createElement(ArrowBackIcon, null)
),
React.createElement(Typography, { variant: 'h5', component: 'h1' }, name),
((isMarketplacePlugin && compatible) || !isMarketplacePlugin) && // if it's from marketplace and compatible, or not from marketplace (private bps)
React.createElement(
PrimaryButton,
{
variant: 'contained',
color: 'primary',
className: classes.useBtn,
disabled: !usePermission || inUse || beingInstalled,
loading: beingInstalled,
onClick: () => onBlueprintSelected(plugin, 1)
},
useLabel ? useLabel : formatMessage(messages.use)
)
),
React.createElement(
AutoPlaySwipeableViews,
{
index: index,
autoplay: !play,
interval: changeImageSlideInterval,
onChangeIndex: handleChangeIndex,
enableMouseEvents: true,
slideStyle: { height: '340px' }
},
renderMedias()
),
steps > 1 &&
React.createElement(MobileStepper, {
variant: 'dots',
steps: steps,
onDotClick: onDotClick,
className: classes.dots,
position: 'static',
activeStep: index
}),
React.createElement(
'div',
{ className: classes.detailsContainer },
React.createElement(
Grid,
{ container: true, spacing: 3 },
React.createElement(
Grid,
{ item: true, xs: 8 },
isMarketplacePlugin &&
!compatible &&
React.createElement(
Alert,
{ severity: 'error', className: classes.detailsNotCompatible },
React.createElement(FormattedMessage, {
id: 'pluginDetails.notCompatible',
defaultMessage: 'This blueprint is not compatible with your current version of CrafterCMS.'
})
),
React.createElement(Typography, { variant: 'body1' }, description),
React.createElement(PluginDocumentation, { plugin: plugin })
),
React.createElement(
Grid,
{ item: true, xs: 4 },
React.createElement(
'div',
{ className: classes.section },
developer && React.createElement(Typography, { variant: 'subtitle2' }, formatMessage(messages.developer)),
developer &&
developer.company &&
React.createElement(Typography, { variant: 'subtitle2', color: 'textSecondary' }, developer.company.name),
developer &&
developer.people &&
React.createElement(Typography, { variant: 'subtitle2', color: 'textSecondary' }, developer.people.name)
),
React.createElement(
'div',
{ className: classes.section },
website && React.createElement(Typography, { variant: 'subtitle2' }, formatMessage(messages.website)),
website &&
website.name &&
React.createElement(
Typography,
{ variant: 'subtitle2', component: 'p' },
React.createElement(
'a',
{ className: classes.link, href: website.url, target: 'blank' },
website.name,
' ',
React.createElement(OpenInNewIcon, null)
)
)
),
React.createElement(
'div',
{ className: classes.sectionChips },
React.createElement(
'div',
{ className: classes.chip },
React.createElement('label', null, formatMessage(messages.version)),
React.createElement('span', null, fullVersion)
),
React.createElement(
'div',
{ className: classes.chip },
React.createElement('label', null, formatMessage(messages.license)),
React.createElement('span', null, license.name)
)
)
)
)
)
);
}
export default PluginDetailsView;