diff --git a/examples/getstarted/src/api/temp/content-types/temp/schema.json b/examples/getstarted/src/api/temp/content-types/temp/schema.json index 1871f063d1..233145331b 100644 --- a/examples/getstarted/src/api/temp/content-types/temp/schema.json +++ b/examples/getstarted/src/api/temp/content-types/temp/schema.json @@ -49,7 +49,9 @@ "default.dish", "default.openingtimes", "default.restaurantservice", - "default.temp" + "default.temp", + "default.apple", + "default.car" ] } } diff --git a/examples/getstarted/src/components/default/apple.json b/examples/getstarted/src/components/default/apple.json new file mode 100644 index 0000000000..5213e36ca0 --- /dev/null +++ b/examples/getstarted/src/components/default/apple.json @@ -0,0 +1,13 @@ +{ + "collectionName": "components_default_apples", + "info": { + "name": "apple", + "icon": "apple-alt" + }, + "options": {}, + "attributes": { + "name": { + "type": "string" + } + } +} diff --git a/examples/getstarted/src/components/default/car.json b/examples/getstarted/src/components/default/car.json new file mode 100644 index 0000000000..6fd2e62cbd --- /dev/null +++ b/examples/getstarted/src/components/default/car.json @@ -0,0 +1,13 @@ +{ + "collectionName": "components_default_cars", + "info": { + "name": "car", + "icon": "align-right" + }, + "options": {}, + "attributes": { + "name": { + "type": "string" + } + } +} diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/ComponentCard/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/ComponentCard/index.js new file mode 100644 index 0000000000..8890d31009 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/ComponentCard/index.js @@ -0,0 +1,90 @@ +/** + * + * ComponentCard + * + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { Text, Stack, Box } from '@strapi/parts'; +import { pxToRem } from '@strapi/helper-plugin'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import styled from 'styled-components'; +import { useIntl } from 'react-intl'; + +const StyledFontAwesomeIcon = styled(FontAwesomeIcon)` + width: ${pxToRem(32)} !important; + height: ${pxToRem(32)} !important; + padding: ${pxToRem(9)}; + border-radius: ${pxToRem(64)}; + background: ${({ theme }) => theme.colors.neutral150}; + path { + fill: ${({ theme }) => theme.colors.neutral500}; + } +`; + +const ComponentBox = styled(Box)` + flex-shrink: 0; + width: ${pxToRem(140)}; + height: ${pxToRem(84)}; + border: 1px solid ${({ theme }) => theme.colors.neutral200}; + background: ${({ theme }) => theme.colors.neutral100}; + border-radius: ${({ theme }) => theme.borderRadius}; + display: flex; + justify-content: center; + align-items: center; + + &.active, + &:hover { + border: 1px solid ${({ theme }) => theme.colors.primary200}; + background: ${({ theme }) => theme.colors.primary100}; + + ${StyledFontAwesomeIcon} { + background: ${({ theme }) => theme.colors.primary200}; + path { + fill: ${({ theme }) => theme.colors.primary600}; + } + } + + ${Text} { + color: ${({ theme }) => theme.colors.primary600}; + } + } +`; + +function ComponentCard({ componentUid, intlLabel, icon, onClick }) { + const { formatMessage } = useIntl(); + const handleClick = () => { + onClick(componentUid); + }; + + return ( + + + + + + {formatMessage(intlLabel)} + + + + + ); +} + +ComponentCard.defaultProps = { + icon: 'smile', + onClick: () => {}, +}; + +ComponentCard.propTypes = { + componentUid: PropTypes.string.isRequired, + intlLabel: PropTypes.shape({ + id: PropTypes.string.isRequired, + defaultMessage: PropTypes.string.isRequired, + }).isRequired, + icon: PropTypes.string, + onClick: PropTypes.func, +}; + +export default ComponentCard; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/index.js new file mode 100644 index 0000000000..4ee33d4a73 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/index.js @@ -0,0 +1,51 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Accordion, AccordionToggle, AccordionContent } from '@strapi/parts/Accordion'; +import { Box } from '@strapi/parts/Box'; +import { Grid, GridItem } from '@strapi/parts/Grid'; +import ComponentCard from './ComponentCard'; + +const Category = ({ category, components, isOdd, isOpen, onAddComponent, onToggle }) => { + const handleToggle = () => { + onToggle(category); + }; + + return ( + + + + + + {components.map(({ componentUid, info: { label, icon, name } }) => { + return ( + + + + ); + })} + + + + + ); +}; + +Category.propTypes = { + category: PropTypes.string.isRequired, + components: PropTypes.array.isRequired, + isOdd: PropTypes.bool.isRequired, + isOpen: PropTypes.bool.isRequired, + onAddComponent: PropTypes.func.isRequired, + onToggle: PropTypes.func.isRequired, +}; + +export default Category; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/index.js similarity index 61% rename from packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/index.js rename to packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/index.js index 72d01be991..f2a0ceed6b 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/index.js @@ -1,14 +1,17 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; -import { groupBy } from 'lodash'; +import groupBy from 'lodash/groupBy'; import PropTypes from 'prop-types'; -import { Collapse } from 'reactstrap'; -import { FormattedMessage } from 'react-intl'; +import { useIntl } from 'react-intl'; +import { KeyboardNavigable } from '@strapi/parts/KeyboardNavigable'; +import { Box } from '@strapi/parts/Box'; +import { Row } from '@strapi/parts/Row'; +import { Text } from '@strapi/parts/Text'; import { getTrad } from '../../../../utils'; import { useContentTypeLayout } from '../../../../hooks'; import Category from './Category'; -import Wrapper from './Wrapper'; -const Picker = ({ components, isOpen, onClickAddComponent }) => { +const ComponentPicker = ({ components, isOpen, onClickAddComponent }) => { + const { formatMessage } = useIntl(); const { getComponentLayout } = useContentTypeLayout(); const [categoryToOpen, setCategoryToOpen] = useState(''); @@ -51,38 +54,58 @@ const Picker = ({ components, isOpen, onClickAddComponent }) => { [categoryToOpen] ); + if (!isOpen) { + return null; + } + return ( - - - - - - - + + + + + {formatMessage({ + id: getTrad('components.DynamicZone.ComponentPicker-label'), + defaultMessage: 'Pick one component', + })} + + + + {dynamicComponentCategories.map(({ category, components }, index) => { return ( ); })} - - - - + + + + ); }; -Picker.propTypes = { +ComponentPicker.propTypes = { components: PropTypes.array.isRequired, isOpen: PropTypes.bool.isRequired, onClickAddComponent: PropTypes.func.isRequired, }; -export default memo(Picker); +export default memo(ComponentPicker); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/Banner/Wrapper.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/Banner/Wrapper.js deleted file mode 100644 index 8992b2d027..0000000000 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/Banner/Wrapper.js +++ /dev/null @@ -1,78 +0,0 @@ -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; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/Banner/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/Banner/index.js deleted file mode 100644 index 7f55f77fd9..0000000000 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/Banner/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Carret } from '@buffetjs/icons'; -import Wrapper from './Wrapper'; - -/* eslint-disable jsx-a11y/no-static-element-interactions */ - -const Banner = ({ category, isOpen, onToggle, isFirst }) => { - const handleClick = () => { - onToggle(category); - }; - - return ( - - - - - {category} - - ); -}; - -Banner.propTypes = { - category: PropTypes.string.isRequired, - isFirst: PropTypes.bool.isRequired, - isOpen: PropTypes.bool.isRequired, - onToggle: PropTypes.func.isRequired, -}; - -Banner.displayName = 'Banner'; - -export default Banner; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/index.js deleted file mode 100644 index 03cb3dddde..0000000000 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Category/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import React, { memo, useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { Collapse } from 'reactstrap'; -import DynamicComponentCard from '../../../../DynamicComponentCard'; -import Banner from './Banner'; - -const Category = ({ category, components, isFirst, isOpen, onAddComponent, onToggle }) => { - const [showComponents, setShowComponents] = useState(false); - - useEffect(() => { - if (isOpen) { - setShowComponents(true); - } - }, [isOpen]); - - const handleExited = () => setShowComponents(false); - - return ( - <> - - - {showComponents && ( - - {components.map(({ componentUid, info: { name, icon } }) => { - return ( - { - onAddComponent(componentUid); - }} - /> - ); - })} - - )} - - > - ); -}; - -Category.propTypes = { - category: PropTypes.string.isRequired, - components: PropTypes.array.isRequired, - isFirst: PropTypes.bool.isRequired, - isOpen: PropTypes.bool.isRequired, - onAddComponent: PropTypes.func.isRequired, - onToggle: PropTypes.func.isRequired, -}; - -export default memo(Category); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Wrapper.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Wrapper.js deleted file mode 100644 index 5b7c7af03e..0000000000 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/Picker/Wrapper.js +++ /dev/null @@ -1,32 +0,0 @@ -import styled from 'styled-components'; - -const Wrapper = styled.div` - overflow: hidden; - - > div { - margin-top: 15px; - padding: 23px 18px 21px 18px; - background-color: #f2f3f4; - } - - .componentPickerTitle { - margin-bottom: 10px; - color: #919bae; - font-weight: 600; - font-size: 13px; - line-height: normal; - } - .componentsList { - display: flex; - flex-wrap: wrap; - padding-top: 10px; - padding-left: 15px; - padding-right: 15px; - } - - .categoriesList { - padding-bottom: 4px; - } -`; - -export default Wrapper; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js index f979b041c1..3326732aae 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js @@ -4,7 +4,6 @@ import isEqual from 'react-fast-compare'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { Box } from '@strapi/parts/Box'; -import { Stack } from '@strapi/parts/Stack'; import { Flex } from '@buffetjs/core'; import { LabelIconWrapper, NotAllowedInput, useNotification } from '@strapi/helper-plugin'; import { getTrad } from '../../utils'; @@ -19,7 +18,7 @@ import ComponentWrapper from './ComponentWrapper'; import DynamicZoneWrapper from './DynamicZoneWrapper'; import Label from './Label'; import Wrapper from './Wrapper'; -import Picker from './components/Picker'; +import ComponentPicker from './components/ComponentPicker'; /* eslint-disable react/no-array-index-key */ @@ -131,6 +130,11 @@ const DynamicZone = ({ name={name} onClick={handleClickOpenPicker} /> + > )} @@ -215,7 +219,7 @@ const DynamicZone = ({ values={{ componentName: metadatas.label }} /> -
- -