UNPKG

@plone/volto

Version:
188 lines (168 loc) 5.19 kB
/** * IdWidget component. * @module components/manage/Widgets/IdWidget */ import { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { useSelector, useDispatch } from 'react-redux'; import { Input } from 'semantic-ui-react'; import compact from 'lodash/compact'; import concat from 'lodash/concat'; import map from 'lodash/map'; import union from 'lodash/union'; import uniq from 'lodash/uniq'; import { defineMessages, useIntl } from 'react-intl'; import Icon from '@plone/volto/components/theme/Icon/Icon'; import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper'; import config from '@plone/volto/registry'; import { getQuerystring } from '@plone/volto/actions/querystring/querystring'; const messages = defineMessages({ reservedId: { id: "This is a reserved name and can't be used", defaultMessage: "This is a reserved name and can't be used", }, invalidCharacters: { id: 'Only 7-bit bytes characters are allowed. Cannot contain uppercase letters, special characters: <, >, &, #, /, ?, or others that are illegal in URLs. Cannot start with: _, aq_, @@, ++. Cannot end with __. Cannot be: request,contributors, ., .., "". Cannot contain new lines.', defaultMessage: 'Only 7-bit bytes characters are allowed. Cannot contain uppercase letters, special characters: <, >, &, #, /, ?, or others that are illegal in URLs. Cannot start with: _, aq_, @@, ++. Cannot end with __. Cannot be: request,contributors, ., .., "". Cannot contain new lines.', }, }); const IdWidget = (props) => { const { id, onClick, icon, iconAction, minLength, maxLength, onBlur, value, focus, isDisabled, placeholder, onChange, } = props; const intl = useIntl(); const dispatch = useDispatch(); const ref = useRef(); const indexes = useSelector((state) => state.querystring.indexes); const [errors, setError] = useState([]); const [reservedIds] = useState( compact( uniq( union( config.settings.reservedIds, map(config.settings.nonContentRoutes, (route) => String(route).replace(/[^a-z-]/g, ''), ), ), ), ), ); const fieldValidation = (value) => { const error = []; // Check reserved id's if (reservedIds.indexOf(value) !== -1) { error.push(intl.formatMessage(messages.reservedId)); } // Check invalid characters if ( // eslint-disable-next-line no-control-regex !/^(?!.*\\)(?!\+\+)(?!@@)(?!.*request)(?!.*contributors)(?!aq_)(?!.*__)(?!_)(?!((^|\/)\.\.?($|\/)|^"\s*"$))(?!.*[A-Z])(?:(?![\r\n<>/?&#\x00-\x1F\x7F])['\x00-\x7F\u0080-\uFFFF. _])*$/.test( value, ) ) { error.push(intl.formatMessage(messages.invalidCharacters)); } // Check indexes if (value in indexes) { error.push(intl.formatMessage(messages.reservedId)); } setError(error); }; useEffect( () => { if (focus) ref.current.focus(); dispatch(getQuerystring()); fieldValidation(value); }, // eslint-disable-next-line react-hooks/exhaustive-deps [focus, value, dispatch], ); const handleChange = ({ target }) => { fieldValidation(target.value); onChange(id, target.value === '' ? undefined : target.value); }; const handleBlur = ({ target }) => { fieldValidation(target.value); onBlur(id, target.value === '' ? undefined : target.value); }; props = { ...props, error: concat(props.error, errors), }; return ( <FormFieldWrapper {...props} className="text"> <Input id={`field-${id}`} name={id} value={value || ''} disabled={isDisabled} icon={icon || null} placeholder={placeholder} onChange={handleChange} onBlur={handleBlur} onClick={() => onClick()} ref={ref} minLength={minLength || null} maxLength={maxLength || null} /> {icon && iconAction && ( <button className={`field-${id}-action-button`} onClick={iconAction}> <Icon name={icon} size="18px" /> </button> )} </FormFieldWrapper> ); }; export default IdWidget; IdWidget.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, description: PropTypes.string, required: PropTypes.bool, error: PropTypes.arrayOf(PropTypes.string), value: PropTypes.string, focus: PropTypes.bool, onChange: PropTypes.func, onBlur: PropTypes.func, onClick: PropTypes.func, onEdit: PropTypes.func, onDelete: PropTypes.func, icon: PropTypes.shape({ xmlns: PropTypes.string, viewBox: PropTypes.string, content: PropTypes.string, }), iconAction: PropTypes.func, minLength: PropTypes.number, maxLength: PropTypes.number, wrapped: PropTypes.bool, placeholder: PropTypes.string, }; IdWidget.defaultProps = { description: null, required: false, error: [], value: null, onChange: () => {}, onBlur: () => {}, onClick: () => {}, onEdit: null, onDelete: null, focus: false, icon: null, iconAction: null, minLength: null, maxLength: null, };