From c9758d888acfa163d9f158cbcbb60761be2008c1 Mon Sep 17 00:00:00 2001 From: Leonardo Dino Date: Fri, 30 Nov 2018 03:15:46 -0200 Subject: [PATCH] Improve SlugRelation control based on new SelectControl --- .../package.json | 4 +- .../src/SlugRelationControl.js | 143 +++++++++--------- .../src/SlugRelationPreview.js | 4 +- 3 files changed, 78 insertions(+), 73 deletions(-) diff --git a/packages/netlify-cms-widget-slug-relation/package.json b/packages/netlify-cms-widget-slug-relation/package.json index c9adf29fb1f5..62aba35339c6 100644 --- a/packages/netlify-cms-widget-slug-relation/package.json +++ b/packages/netlify-cms-widget-slug-relation/package.json @@ -29,13 +29,11 @@ "webpack-cli": "^3.1.0" }, "peerDependencies": { - "emotion": "^9.2.6", "immutable": "^3.7.6", "lodash": "^4.17.10", "netlify-cms-ui-default": "^2.0.0", "prop-types": "^15.5.10", "react": "^16.4.1", - "react-emotion": "^9.2.5", - "uuid": "^3.1.0" + "react-immutable-proptypes": "^2.1.0" } } diff --git a/packages/netlify-cms-widget-slug-relation/src/SlugRelationControl.js b/packages/netlify-cms-widget-slug-relation/src/SlugRelationControl.js index 32f0d619ce34..783d9135ef15 100644 --- a/packages/netlify-cms-widget-slug-relation/src/SlugRelationControl.js +++ b/packages/netlify-cms-widget-slug-relation/src/SlugRelationControl.js @@ -1,16 +1,15 @@ -import React, { createRef } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import uuid from 'uuid/v4'; import { List, Map } from 'immutable'; -import { debounce } from 'lodash'; -import styled from 'react-emotion'; -import AsyncSelect from 'react-select/lib/Async'; +import { debounce, castArray } from 'lodash'; +import Select from 'react-select/lib/Async'; import BaseOption from 'react-select/lib/components/Option'; import BaseSingleValue from 'react-select/lib/components/SingleValue'; +import { colors } from 'netlify-cms-ui-default'; import EntryLoader from './EntryLoader' -const getOptionValue = option => option.value || option.slug || option +const getOptionValue = option => option && (option.value || option.slug || option) // TODO: add support for setting custom identifier field #1543 // TODO: export a factory to create custom Option UI @@ -28,8 +27,8 @@ const toJS = object => { // TODO: export a factory to create custom Value UI const ValueRenderer = props => ( - {({isLoading, title, error}) => { - if(isLoading) return '...' + {({isLoading, isFetching, title, error}) => { + if(isLoading || isFetching) return '...' if(error) return error return title || 'Unknown Entity' }} @@ -56,29 +55,53 @@ const SingleValue = ({children: entry, ...props}) => ( ) SingleValue.propTypes = {children: PropTypes.object} - -const Select = styled(AsyncSelect)`z-index: 2;` -const Wrapper = styled.div`padding: 0 !important;` +const components = {SingleValue, MultiValueLabel, Option} const styles = { - control: (provided) => ({ - ...provided, - border: '0px solid transparent', - borderRadius: '3px', - borderTopLeftRadius: '0', + control: styles => ({ + ...styles, + border: 0, boxShadow: 'none', - minHeight: '54px', + padding: '9px 0 9px 12px', + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isSelected + ? `${colors.active}` + : state.isFocused + ? `${colors.activeBackground}` + : 'transparent', + paddingLeft: '22px', + }), + menu: styles => ({ ...styles, right: 0, zIndex: 2 }), + container: styles => ({ ...styles, padding: '0 !important' }), + indicatorSeparator: (styles, state) => + state.hasValue && state.selectProps.isClearable + ? { ...styles, backgroundColor: `${colors.textFieldBorder}` } + : { display: 'none' }, + dropdownIndicator: styles => ({ ...styles, color: `${colors.controlLabel}` }), + clearIndicator: styles => ({ ...styles, color: `${colors.controlLabel}` }), + multiValue: styles => ({ + ...styles, + backgroundColor: colors.background, }), - valueContainer: (provided) => ({ - ...provided, - padding: '8px 14px', + multiValueLabel: styles => ({ + ...styles, + color: colors.textLead, + fontWeight: 500, + }), + multiValueRemove: styles => ({ + ...styles, + color: colors.controlLabel, + ':hover': { + color: colors.errorText, + backgroundColor: colors.errorBackground, + }, }), } -const IndicatorSeparator = () => null -const DropdownIndicator = () => null const entryMapper = ({data, slug}) => ({...data, value: slug, type: 'option'}) -const getSlug = value => (value.value || value) +const getSlug = value => value && (value.value || value) export default class RelationControl extends React.Component { static defaultProps = { @@ -86,7 +109,7 @@ export default class RelationControl extends React.Component { } static propTypes = { onChange: PropTypes.func.isRequired, - forID: PropTypes.string, + forID: PropTypes.string.isRequired, queryHits: ImmutablePropTypes.map.isRequired, value: PropTypes.oneOfType([ ImmutablePropTypes.list.isRequired, @@ -101,16 +124,13 @@ export default class RelationControl extends React.Component { setInactiveStyle: PropTypes.func.isRequired, } - controlID = uuid(); - select = createRef(); - componentDidUpdate(prevProps){ if(!this.callback) return if( this.props.queryHits !== prevProps.queryHits && - this.props.queryHits.get(this.controlID) + this.props.queryHits.get(this.props.forID) ){ - const queryHits = this.props.queryHits.get(this.controlID, []) + const queryHits = this.props.queryHits.get(this.props.forID, []) return this.callback(queryHits.map(entryMapper)) } } @@ -120,14 +140,14 @@ export default class RelationControl extends React.Component { ) loadOptions = debounce((term, callback) => { - const { field } = this.props; + const { field, forID } = this.props; const collection = field.get('collection'); const searchFields = field.get('searchFields').toJS(); this.callback = value => { callback(value) this.callback = null } - this.props.query(this.controlID, collection, searchFields, term) + this.props.query(forID, collection, searchFields, term) }, 250); handleMenuClose = () => { @@ -145,48 +165,35 @@ export default class RelationControl extends React.Component { return {entry: value} } - shouldComponentUpdate(prevProps){ - const current = this.props.queryHits.get(this.controlID); - const previous = prevProps.queryHits.get(this.controlID); - if(current !== previous) return true - if(prevProps.forID !== this.props.forID) return true - if(prevProps.classNameWrapper !== this.props.classNameWrapper) return true - return false + shouldComponentUpdate(){ + return true } render() { - const {forID, classNameWrapper, value} = this.props; - const {setActiveStyle, setInactiveStyle} = this.props; - const multiple = !!this.props.field.get('multiple'); - const defaultValue = toJS(value) || multiple ? [] : ''; + const { field, value, forID, classNameWrapper, setActiveStyle, setInactiveStyle } = this.props; + const isMultiple = field.get('multiple', false); + const isClearable = !field.get('required', true) || isMultiple; + const defaultValue = castArray(toJS(value)) return ( - - ); } } diff --git a/packages/netlify-cms-widget-slug-relation/src/SlugRelationPreview.js b/packages/netlify-cms-widget-slug-relation/src/SlugRelationPreview.js index 8138518a646b..ab7a5130143e 100644 --- a/packages/netlify-cms-widget-slug-relation/src/SlugRelationPreview.js +++ b/packages/netlify-cms-widget-slug-relation/src/SlugRelationPreview.js @@ -2,8 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { WidgetPreviewContainer } from 'netlify-cms-ui-default'; -const stringify = value => Array.isArray(value) ? value.join(', ') : value -const SlugRelationPreview = ({ value }) => ( +const stringify = (value = null) => Array.isArray(value) ? value.join(', ') : value +const SlugRelationPreview = ({ value } = {}) => ( {stringify(value)} );