@primer/react
Version:
An implementation of GitHub's Primer Design System using React
157 lines (154 loc) • 7.75 kB
JavaScript
import React__default, { useContext } from 'react';
import Select from '../Select.js';
import ValidationAnimationContainer from '../_ValidationAnimationContainer.js';
import { get } from '../constants.js';
import InlineAutocomplete from '../drafts/InlineAutocomplete/InlineAutocomplete.js';
import { useSlots } from '../hooks/useSlots.js';
import FormControlCaption from './_FormControlCaption.js';
import FormControlLabel from './_FormControlLabel.js';
import FormControlLeadingVisual from './_FormControlLeadingVisual.js';
import FormControlValidation from './_FormControlValidation.js';
import { CheckboxOrRadioGroupContext } from '../_CheckboxOrRadioGroup/CheckboxOrRadioGroup.js';
import Checkbox from '../Checkbox/Checkbox.js';
import Radio from '../Radio/Radio.js';
import Box from '../Box/Box.js';
import Autocomplete from '../Autocomplete/Autocomplete.js';
import TextInput from '../TextInput/TextInput.js';
import TextInputWithTokens from '../TextInputWithTokens/TextInputWithTokens.js';
import Textarea from '../Textarea/Textarea.js';
import { useSSRSafeId } from '@react-aria/ssr';
const FormControlContext = /*#__PURE__*/React__default.createContext({});
const FormControl = /*#__PURE__*/React__default.forwardRef(({
children,
disabled: disabledProp,
layout = 'vertical',
id: idProp,
required,
sx
}, ref) => {
var _slots$validation, _slots$label, _slots$label2;
const [slots, childrenWithoutSlots] = useSlots(children, {
caption: FormControlCaption,
label: FormControlLabel,
leadingVisual: FormControlLeadingVisual,
validation: FormControlValidation
});
const expectedInputComponents = [Autocomplete, Checkbox, Radio, Select, TextInput, TextInputWithTokens, Textarea, InlineAutocomplete];
const choiceGroupContext = useContext(CheckboxOrRadioGroupContext);
const disabled = choiceGroupContext.disabled || disabledProp;
const id = useSSRSafeId(idProp);
const validationMessageId = slots.validation ? `${id}-validationMessage` : undefined;
const captionId = slots.caption ? `${id}-caption` : undefined;
const validationStatus = (_slots$validation = slots.validation) === null || _slots$validation === void 0 ? void 0 : _slots$validation.props.variant;
const InputComponent = childrenWithoutSlots.find(child => expectedInputComponents.some(inputComponent => /*#__PURE__*/React__default.isValidElement(child) && child.type === inputComponent));
const inputProps = /*#__PURE__*/React__default.isValidElement(InputComponent) && InputComponent.props;
const isChoiceInput = /*#__PURE__*/React__default.isValidElement(InputComponent) && (InputComponent.type === Checkbox || InputComponent.type === Radio);
if (InputComponent) {
if (inputProps !== null && inputProps !== void 0 && inputProps.id) {
// eslint-disable-next-line no-console
console.warn(`instead of passing the 'id' prop directly to the input component, it should be passed to the parent component, <FormControl>`);
}
if (inputProps !== null && inputProps !== void 0 && inputProps.disabled) {
// eslint-disable-next-line no-console
console.warn(`instead of passing the 'disabled' prop directly to the input component, it should be passed to the parent component, <FormControl>`);
}
if (inputProps !== null && inputProps !== void 0 && inputProps.required) {
// eslint-disable-next-line no-console
console.warn(`instead of passing the 'required' prop directly to the input component, it should be passed to the parent component, <FormControl>`);
}
}
if (!slots.label) {
// eslint-disable-next-line no-console
console.error(`The input field with the id ${id} MUST have a FormControl.Label child.\n\nIf you want to hide the label, pass the 'visuallyHidden' prop to the FormControl.Label component.`);
}
if (isChoiceInput) {
if (slots.validation) {
// eslint-disable-next-line no-console
console.warn('Validation messages are not rendered for an individual checkbox or radio. The validation message should be shown for all options.');
}
if (childrenWithoutSlots.find(child => {
var _child$props;
return /*#__PURE__*/React__default.isValidElement(child) && ((_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.required);
})) {
// eslint-disable-next-line no-console
console.warn('An individual checkbox or radio cannot be a required field.');
}
} else {
if (slots.leadingVisual) {
// eslint-disable-next-line no-console
console.warn('A leading visual is only rendered for a checkbox or radio form control. If you want to render a leading visual inside of your input, check if your input supports a leading visual.');
}
}
const isLabelHidden = (_slots$label = slots.label) === null || _slots$label === void 0 ? void 0 : _slots$label.props.visuallyHidden;
return /*#__PURE__*/React__default.createElement(FormControlContext.Provider, {
value: {
captionId,
disabled,
id,
required,
validationMessageId
}
}, isChoiceInput || layout === 'horizontal' ? /*#__PURE__*/React__default.createElement(Box, {
ref: ref,
display: "flex",
alignItems: slots.leadingVisual ? 'center' : undefined,
sx: sx
}, /*#__PURE__*/React__default.createElement(Box, {
sx: {
'> input': {
marginLeft: 0,
marginRight: 0
}
}
}, /*#__PURE__*/React__default.isValidElement(InputComponent) && /*#__PURE__*/React__default.cloneElement(InputComponent, {
id,
disabled,
['aria-describedby']: captionId
}), childrenWithoutSlots.filter(child => /*#__PURE__*/React__default.isValidElement(child) && ![Checkbox, Radio].some(inputComponent => child.type === inputComponent))), slots.leadingVisual && /*#__PURE__*/React__default.createElement(Box, {
color: disabled ? 'fg.muted' : 'fg.default',
sx: {
'> *': {
minWidth: slots.caption ? get('fontSizes.4') : get('fontSizes.2'),
minHeight: slots.caption ? get('fontSizes.4') : get('fontSizes.2'),
fill: 'currentColor'
}
},
ml: 2
}, slots.leadingVisual), !((_slots$label2 = slots.label) !== null && _slots$label2 !== void 0 && _slots$label2.props.visuallyHidden) || slots.caption ? /*#__PURE__*/React__default.createElement(Box, {
display: "flex",
flexDirection: "column",
ml: 2
}, slots.label, slots.caption) : /*#__PURE__*/React__default.createElement(React__default.Fragment, null, slots.label, slots.caption)) : /*#__PURE__*/React__default.createElement(Box, {
ref: ref,
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
sx: {
...(isLabelHidden ? {
'> *:not(label) + *': {
marginTop: 1
}
} : {
'> * + *': {
marginTop: 1
}
}),
...sx
}
}, slots.label, /*#__PURE__*/React__default.isValidElement(InputComponent) && /*#__PURE__*/React__default.cloneElement(InputComponent, Object.assign({
id,
required,
disabled,
validationStatus,
['aria-describedby']: [validationMessageId, captionId].filter(Boolean).join(' ')
}, InputComponent.props)), childrenWithoutSlots.filter(child => /*#__PURE__*/React__default.isValidElement(child) && !expectedInputComponents.some(inputComponent => child.type === inputComponent)), slots.validation ? /*#__PURE__*/React__default.createElement(ValidationAnimationContainer, {
show: true
}, slots.validation) : null, slots.caption));
});
var FormControl$1 = Object.assign(FormControl, {
Caption: FormControlCaption,
Label: FormControlLabel,
LeadingVisual: FormControlLeadingVisual,
Validation: FormControlValidation
});
export { FormControlContext, FormControl$1 as default };