UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

415 lines (413 loc) 12 kB
/* * 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 Card from '@mui/material/Card'; import CardActionArea from '@mui/material/CardActionArea'; import CardContent from '@mui/material/CardContent'; import CardHeader from '@mui/material/CardHeader'; import Typography from '@mui/material/Typography'; import CardActions from '@mui/material/CardActions'; import SwipeableViews from 'react-swipeable-views'; // @ts-ignore import { autoPlay } from 'react-swipeable-views-utils'; import { makeStyles } from 'tss-react/mui'; import { defineMessages, useIntl } from 'react-intl'; import MobileStepper from '../MobileStepper/MobileStepper'; import { backgroundColor } from '../../styles/theme'; import Button from '@mui/material/Button'; import Tooltip from '@mui/material/Tooltip'; import cardTitleStyles, { cardSubtitleStyles } from '../../styles/card'; import SecondaryButton from '../SecondaryButton'; const AutoPlaySwipeableViews = autoPlay(SwipeableViews); const useStyles = makeStyles()((theme) => ({ root: { flexGrow: 1 }, card: { maxWidth: '100%', minHeight: '339px', '& .cardTitle': Object.assign({}, cardTitleStyles), '& .cardContent': { height: '13.26em', padding: '12px 14px 5px 14px', position: 'relative' }, '& .cardActions': { justifyContent: 'space-around' }, '& .developer': Object.assign(Object.assign({}, cardSubtitleStyles), { WebkitLineClamp: 1, marginBottom: 0 }) }, gitCard: { minHeight: 'unset' }, gitCardActionArea: { display: 'flex', justifyContent: 'start' }, gitCardContent: { height: 'unset !important' }, carouselImg: { width: '100%', height: '180px', objectFit: 'cover', '&.git': { objectFit: 'fill', height: 'unset', width: '120px' } }, video: { width: '100%', height: '180px', outline: 'none', background: backgroundColor, '&.git': { height: 'unset' } }, chip: { fontSize: '11px', color: 'gray', backgroundColor: '#f5f5f5', padding: '2px 5px', borderRadius: '5px', display: 'inline-block', whiteSpace: 'nowrap', '& label': { marginRight: '5px', marginBottom: 0, fontWeight: 400 }, '& span': { color: '#2F2707' } }, options: { marginLeft: 'auto' }, dialogContent: { display: 'flex' }, imgWrapper: { position: 'relative' }, dots: { background: 'none', borderTop: '1px solid #e4e3e3', height: '30px', padding: '0', cursor: 'pointer', '& .MuiMobileStepper-dot': { padding: '6px', margin: '4px', '&:hover': { background: 'gray' } } }, use: { width: '50%' }, more: { width: '50%', textAlign: 'center', color: theme.palette.text.secondary, flex: 1 }, background: { background: backgroundColor, height: '180px', overflow: 'hidden', '&.git': { background: 'none', height: 'unset' } }, subtitleContainer: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } })); const messages = defineMessages({ version: { id: 'plugin.version', defaultMessage: 'Version' }, license: { id: 'plugin.license', defaultMessage: 'License' }, crafterCms: { id: 'plugin.crafterCMS', defaultMessage: 'CrafterCMS' }, by: { id: 'plugin.by', defaultMessage: 'By' }, noDev: { id: 'plugin.noDev', defaultMessage: 'No developer specified.' }, use: { id: 'plugin.use', defaultMessage: 'Use' }, more: { id: 'plugin.more', defaultMessage: 'More...' }, licenseTooltip: { id: 'plugin.licenseTooltip', defaultMessage: '{license} license' } }); function PluginCard(props) { const { classes, cx } = useStyles(); const [index, setIndex] = useState(0); const [play, setPlay] = useState(false); const { onPluginSelected, plugin, changeImageSlideInterval = 5000, isMarketplacePlugin = true, onDetails, inUse = false, usePermission = true, beingInstalled = false, disableCardActionClick = false, useLabel } = props; const { media, name, license, id, developer } = plugin; const { formatMessage } = useIntl(); const isGitCard = id === 'GIT'; function handleChangeIndex(value) { setIndex(value); } function onDotClick(e, step) { e.stopPropagation(); setIndex(step); } function handlePlay() { setPlay(true); } function handleEnded() { setPlay(false); } function onImageClick(e, index = 0) { if (plugin.id === 'GIT') return false; e.stopPropagation(); e.preventDefault(); onDetails(plugin, index); } function renderLicense() { return React.createElement( Tooltip, { title: formatMessage(messages.licenseTooltip, { license: license.name }) }, React.createElement('div', { className: classes.chip }, React.createElement('span', null, license.name)) ); } function renderSubtitle() { if (developer) { if (developer.company) { return React.createElement( 'div', { className: classes.subtitleContainer }, React.createElement( Typography, { gutterBottom: true, variant: 'subtitle2', className: 'developer', color: 'textSecondary' }, formatMessage(messages.by), ' ', developer.company.name ), renderLicense() ); } else { return developer.people.map((item) => item.name).join(','); } } else { return React.createElement( 'div', { className: classes.subtitleContainer }, React.createElement( Typography, { gutterBottom: true, variant: 'subtitle1', className: 'developer', color: 'textSecondary' }, formatMessage(messages.noDev) ), renderLicense() ); } } function renderMedias(id) { 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: cx(classes.background, id === 'GIT' && 'git'), onClick: (event) => onImageClick(event, index) }, React.createElement('img', { className: cx(classes.carouselImg, id === 'GIT' && 'git'), src: item.url, alt: item.description }) ); } else { return React.createElement( 'video', { muted: true, controls: true, key: index, autoPlay: play, onEnded: handleEnded, className: classes.video, onPlaying: handlePlay }, 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( Card, { className: cx(classes.card, id === 'GIT' ? classes.gitCard : null) }, id !== 'GIT' && React.createElement( CardActionArea, { disabled: disableCardActionClick, onClick: (e) => { if (isMarketplacePlugin && !plugin.compatible) { onImageClick(e); } else { onPluginSelected(plugin, 1); } } }, React.createElement(CardHeader, { title: name, subheader: id !== 'GIT' ? renderSubtitle() : '', titleTypographyProps: { variant: 'subtitle2', component: 'h2', className: 'cardTitle' }, subheaderTypographyProps: { variant: 'subtitle2', component: 'h2', color: 'textSecondary' } }) ), React.createElement( CardActionArea, { disabled: disableCardActionClick, onClick: () => { onPluginSelected(plugin, 1); }, className: isGitCard ? classes.gitCardActionArea : null }, React.createElement( AutoPlaySwipeableViews, { index: index, interval: changeImageSlideInterval, autoplay: false, onChangeIndex: handleChangeIndex, enableMouseEvents: true }, renderMedias(id) ), id === 'GIT' && React.createElement( CardContent, { className: cx('cardContent', isGitCard ? classes.gitCardContent : null) }, React.createElement( Typography, { gutterBottom: true, variant: 'subtitle2', component: 'h2', className: 'cardTitle' }, name ), React.createElement( Typography, { gutterBottom: true, variant: 'subtitle2', component: 'h2', color: 'textSecondary' }, plugin.description ) ) ), steps > 0 && id !== 'GIT' && React.createElement(MobileStepper, { variant: 'dots', steps: steps, onDotClick: onDotClick, className: classes.dots, position: 'static', activeStep: index }), id !== 'GIT' && React.createElement( CardActions, { className: 'cardActions' }, ((isMarketplacePlugin && plugin.compatible) || !isMarketplacePlugin) && // if it's from marketplace and compatible, or not from marketplace (private bps) React.createElement( SecondaryButton, { color: 'primary', disabled: !usePermission || inUse || beingInstalled, loading: beingInstalled, onClick: () => onPluginSelected(plugin, 1), className: classes.use }, useLabel ? useLabel : formatMessage(messages.use) ), React.createElement( Button, { className: classes.more, onClick: () => onDetails(plugin) }, formatMessage(messages.more) ) ) ); } export default PluginCard;