Merge branch 'master' into feature/german-translations-update

This commit is contained in:
Moritz Eck 2020-11-18 17:35:06 +01:00 committed by GitHub
commit c38e1c4da8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 272 additions and 49 deletions

View File

@ -11,15 +11,16 @@ const ComponentInitializer = ({ componentUid, isReadOnly, name }) => {
const { addNonRepeatableComponentToField } = useDataManager();
return (
<NonRepeatableWrapper isEmpty isReadOnly={isReadOnly}>
<PlusButton
onClick={() => {
if (!isReadOnly) {
addNonRepeatableComponentToField(name, componentUid);
}
}}
type="button"
/>
<NonRepeatableWrapper
isEmpty
isReadOnly={isReadOnly}
onClick={() => {
if (!isReadOnly) {
addNonRepeatableComponentToField(name, componentUid);
}
}}
>
<PlusButton type="button" />
<FormattedMessage id={`${pluginId}.components.empty-repeatable`}>
{msg => <P style={{ paddingTop: 78 }}>{msg}</P>}
</FormattedMessage>

View File

@ -4,7 +4,7 @@ const Wrapper = styled.div`
position: relative;
height: 90px;
width: 139px !important;
margin-right: 10px;
margin: 0 10px 10px 0;
padding: 18px 10px;
background-color: #ffffff;
color: #919bae;

View File

@ -0,0 +1,35 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React from 'react';
import PropTypes from 'prop-types';
import { Carret } from '@buffetjs/icons';
import BannerWrapper from './BannerWrapper';
/* eslint-disable jsx-a11y/no-static-element-interactions */
const Banner = ({ category, isOpen, onClickToggle, isFirst }) => {
return (
<BannerWrapper type="button" isFirst={isFirst} isOpen={isOpen} onClick={onClickToggle}>
<div className="img-wrapper">
<Carret />
</div>
<div className="label">{category}</div>
</BannerWrapper>
);
};
Banner.defaultProps = {
isFirst: false,
isOpen: false,
onClickToggle: () => {},
};
Banner.propTypes = {
category: PropTypes.string.isRequired,
isFirst: PropTypes.bool,
isOpen: PropTypes.bool,
onClickToggle: PropTypes.func,
};
Banner.displayName = 'Banner';
export default Banner;

View File

@ -0,0 +1,78 @@
import styled from 'styled-components';
/* eslint-disable */
const BannerWrapper = styled.button`
display: flex;
height: 36px;
width: 100%;
padding: 0 15px;
border-bottom: 0;
border: 1px solid rgba(227, 233, 243, 0.75);
background-color: ${({ theme }) => theme.main.colors.white};
font-size: ${({ theme }) => theme.main.sizes.fonts.md};
font-weight: ${({ theme }) => theme.main.fontWeights.semiBold};
cursor: pointer;
&:focus {
outline: 0;
}
.img-wrapper {
width: 19px;
height: 19px;
margin-right: 19px;
border-radius: 50%;
background-color: ${({ theme }) => theme.main.colors.mediumGrey};
text-align: center;
}
.label {
text-transform: capitalize;
}
svg {
path {
fill: ${({ theme }) => theme.main.colors.leftMenu['link-color']} !important;
}
}
-webkit-font-smoothing: antialiased;
> div {
align-self: center;
margin-top: -2px;
}
${({ isFirst, theme }) => {
if (isFirst) {
return `
border-top-right-radius: ${theme.main.sizes.borderRadius};
border-top-left-radius: ${theme.main.sizes.borderRadius};
`;
}
}}
${({ isOpen, theme }) => {
if (isOpen) {
return `
border: 1px solid ${theme.main.colors.darkBlue};
background-color: ${theme.main.colors.lightBlue};
color: ${theme.main.colors.mediumBlue};
font-weight: ${theme.main.fontWeights.bold};
.img-wrapper {
background-color: ${theme.main.colors.darkBlue};
transform: rotate(180deg);
}
svg {
path {
fill: ${theme.main.colors.mediumBlue} !important;
}
}
`;
}
}}
`;
export default BannerWrapper;

View File

@ -0,0 +1,65 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Collapse } from 'reactstrap';
import Banner from './Banner';
import ComponentsList from './ComponentsList';
import DynamicComponentCard from '../DynamicComponentCard';
const CategoryItem = ({
category,
components,
isOpen,
isFirst,
onClickToggle,
onClickComponent,
}) => {
const [showComponents, setShowComponents] = useState(false);
useEffect(() => {
if (isOpen) {
setShowComponents(true);
}
}, [isOpen]);
const handleExited = () => setShowComponents(false);
return (
<>
<Banner onClickToggle={onClickToggle} isFirst={isFirst} isOpen={isOpen} category={category} />
<Collapse isOpen={isOpen} onExited={handleExited}>
{showComponents && (
<ComponentsList className="componentsList">
{components.map(component => {
const {
info: { icon, name: friendlyName },
componentUid,
} = component;
return (
<DynamicComponentCard
key={componentUid}
componentUid={componentUid}
friendlyName={friendlyName}
icon={icon}
onClick={() => {
onClickComponent(componentUid);
}}
/>
);
})}
</ComponentsList>
)}
</Collapse>
</>
);
};
CategoryItem.propTypes = {
category: PropTypes.string.isRequired,
components: PropTypes.array.isRequired,
isOpen: PropTypes.bool.isRequired,
isFirst: PropTypes.bool.isRequired,
onClickToggle: PropTypes.func.isRequired,
onClickComponent: PropTypes.func.isRequired,
};
export default CategoryItem;

View File

@ -0,0 +1,9 @@
import styled from 'styled-components';
const ComponentsList = styled.div`
padding-top: 10px;
padding-left: 15px;
padding-right: 15px;
`;
export default ComponentsList;

View File

@ -3,8 +3,6 @@ import styled from 'styled-components';
/* eslint-disable indent */
const ComponentsPicker = styled.div`
overflow: hidden;
max-height: 0;
transition: max-height 0.2s ease-out;
> div {
margin-top: 15px;
@ -12,14 +10,8 @@ const ComponentsPicker = styled.div`
background-color: #f2f3f4;
}
${({ isOpen }) =>
isOpen &&
`
max-height: 260px;
`}
.componentPickerTitle {
margin-bottom: 15px;
margin-bottom: 10px;
color: #919bae;
font-weight: 600;
font-size: 13px;
@ -27,7 +19,11 @@ const ComponentsPicker = styled.div`
}
.componentsList {
display: flex;
overflow-x: auto;
flex-wrap: wrap;
}
.categoriesList {
padding-bottom: 4px;
}
`;

View File

@ -1,13 +1,13 @@
import React, { memo, useCallback, useMemo, useState } from 'react';
import { get } from 'lodash';
import { get, groupBy } from 'lodash';
import isEqual from 'react-fast-compare';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Arrow } from '@buffetjs/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Collapse } from 'reactstrap';
import pluginId from '../../pluginId';
import useEditView from '../../hooks/useEditView';
import DynamicComponentCard from '../DynamicComponentCard';
import FieldComponent from '../FieldComponent';
import NotAllowedInput from '../NotAllowedInput';
import connect from './utils/connect';
@ -20,6 +20,7 @@ import DynamicZoneWrapper from './DynamicZoneWrapper';
import Label from './Label';
import RoundCTA from './RoundCTA';
import Wrapper from './Wrapper';
import CategoryItem from './CategoryItem';
/* eslint-disable react/no-array-index-key */
@ -42,16 +43,26 @@ const DynamicZone = ({
}) => {
const [isOpen, setIsOpen] = useState(false);
const [categoryToOpen, setCategoryToOpen] = useState('');
const { components } = useEditView();
const getDynamicComponent = useCallback(
componentUid => {
const component = components.find(compo => compo.uid === componentUid);
return component;
},
[components]
);
const getDynamicComponentSchemaData = useCallback(
componentUid => {
const component = components.find(compo => compo.uid === componentUid);
const { schema } = component;
const { schema } = getDynamicComponent(componentUid);
return schema;
},
[components]
[getDynamicComponent]
);
const getDynamicComponentInfos = useCallback(
@ -78,6 +89,28 @@ const DynamicZone = ({
[layout, name]
);
const dynamicComponentCategories = useMemo(() => {
const componentsWithInfos = dynamicZoneAvailableComponents.map(componentUid => {
const {
category,
schema: { info },
} = getDynamicComponent(componentUid);
return { componentUid, category, info };
});
return groupBy(componentsWithInfos, 'category');
}, [dynamicZoneAvailableComponents, getDynamicComponent]);
const handleClickToggle = useCallback(
categoryName => {
const nextCategoryToOpen = categoryToOpen === categoryName ? '' : categoryName;
setCategoryToOpen(nextCategoryToOpen);
},
[categoryToOpen]
);
const metas = useMemo(() => get(layout, ['metadatas', name, 'edit'], {}), [layout, name]);
const dynamicDisplayedComponentsLength = dynamicDisplayedComponents.length;
const missingComponentNumber = min - dynamicDisplayedComponentsLength;
@ -202,32 +235,38 @@ const DynamicZone = ({
values={{ componentName: name }}
/>
</div>
<ComponentsPicker isOpen={isOpen}>
<div>
<p className="componentPickerTitle">
<FormattedMessage id={`${pluginId}.components.DynamicZone.pick-compo`} />
</p>
<div className="componentsList">
{dynamicZoneAvailableComponents.map(componentUid => {
const { icon, name: friendlyName } = getDynamicComponentInfos(componentUid);
<Collapse isOpen={isOpen}>
<ComponentsPicker>
<div>
<p className="componentPickerTitle">
<FormattedMessage id={`${pluginId}.components.DynamicZone.pick-compo`} />
</p>
<div className="categoriesList">
{Object.keys(dynamicComponentCategories).map((categoryName, index) => {
const components = dynamicComponentCategories[categoryName];
return (
<DynamicComponentCard
key={componentUid}
componentUid={componentUid}
friendlyName={friendlyName}
icon={icon}
onClick={() => {
setIsOpen(false);
const shouldCheckErrors = hasError;
addComponentToDynamicZone(name, componentUid, shouldCheckErrors);
}}
/>
);
})}
return (
<CategoryItem
key={categoryName}
category={categoryName}
components={components}
isOpen={categoryToOpen === categoryName}
isFirst={index === 0}
onClickToggle={() => {
handleClickToggle(categoryName);
}}
onClickComponent={componentUid => {
setCategoryToOpen('');
const shouldCheckErrors = hasError;
addComponentToDynamicZone(name, componentUid, shouldCheckErrors);
}}
/>
);
})}
</div>
</div>
</div>
</ComponentsPicker>
</ComponentsPicker>
</Collapse>
</Wrapper>
) : (
<BaselineAlignement top="9px" />