@carbon/react
Version:
React components for the Carbon Design System
287 lines (282 loc) • 9.54 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { WarningFilled, WarningAltFilled, Calendar } from '@carbon/icons-react';
import { warning } from '../../internal/warning.js';
import cx from 'classnames';
import PropTypes from 'prop-types';
import React, { useContext, cloneElement } from 'react';
import { usePrefix } from '../../internal/usePrefix.js';
import '../FluidForm/FluidForm.js';
import { FormContext } from '../FluidForm/FormContext.js';
import { useId } from '../../internal/useId.js';
import '../Text/index.js';
import { deprecate } from '../../prop-types/deprecate.js';
import { AILabel } from '../AILabel/index.js';
import { isComponentElement } from '../../internal/utils.js';
import { Text } from '../Text/Text.js';
let didWarnAboutDatePickerInputValue = false;
const DatePickerInput = /*#__PURE__*/React.forwardRef(function DatePickerInput(props, ref) {
const {
datePickerType,
decorator,
disabled = false,
helperText,
hideLabel,
id,
invalid = false,
invalidText,
labelText,
onClick = () => {},
onChange = () => {},
pattern = '\\d{1,2}\\/\\d{1,2}\\/\\d{4}',
placeholder,
size = 'md',
slug,
type = 'text',
warn,
warnText,
...rest
} = props;
const prefix = usePrefix();
const {
isFluid
} = useContext(FormContext);
const datePickerInputInstanceId = useId();
const datePickerInputProps = {
id,
onChange: event => {
if (!disabled) {
onChange(event);
}
},
onClick: event => {
if (!disabled) {
onClick(event);
}
},
pattern,
placeholder,
type
};
if (process.env.NODE_ENV !== 'production' && 'value' in rest && !didWarnAboutDatePickerInputValue) {
process.env.NODE_ENV !== "production" ? warning(false, `The 'value' prop is not supported on the DatePickerInput component. ` + `For DatePicker components with 'datePickerType="range"', please ` + `pass the 'value' prop (as an array of dates) to the parent ` + `DatePicker component instead.`) : void 0;
didWarnAboutDatePickerInputValue = true;
}
const wrapperClasses = cx(`${prefix}--date-picker-input__wrapper`, {
[`${prefix}--date-picker-input__wrapper--invalid`]: invalid,
[`${prefix}--date-picker-input__wrapper--warn`]: warn,
[`${prefix}--date-picker-input__wrapper--slug`]: slug,
[`${prefix}--date-picker-input__wrapper--decorator`]: decorator
});
const labelClasses = cx(`${prefix}--label`, {
[`${prefix}--visually-hidden`]: hideLabel,
[`${prefix}--label--disabled`]: disabled,
[`${prefix}--label--readonly`]: rest.readOnly
});
const helperTextClasses = cx(`${prefix}--form__helper-text`, {
[`${prefix}--form__helper-text--disabled`]: disabled
});
const inputClasses = cx(`${prefix}--date-picker__input`, {
[`${prefix}--date-picker__input--${size}`]: size,
[`${prefix}--date-picker__input--invalid`]: invalid,
[`${prefix}--date-picker__input--warn`]: warn
});
const containerClasses = cx(`${prefix}--date-picker-container`, {
[`${prefix}--date-picker--nolabel`]: !labelText,
[`${prefix}--date-picker--fluid--invalid`]: isFluid && invalid,
[`${prefix}--date-picker--fluid--warn`]: isFluid && warn
});
const datePickerInputHelperId = !helperText ? undefined : `datepicker-input-helper-text-${datePickerInputInstanceId}`;
const inputProps = {
...rest,
...datePickerInputProps,
className: inputClasses,
disabled,
ref,
['aria-describedby']: helperText ? datePickerInputHelperId : undefined
};
if (invalid) {
inputProps['data-invalid'] = true;
}
const input = /*#__PURE__*/React.createElement("input", inputProps);
// AILabel always size `mini`
const candidate = slug ?? decorator;
const candidateIsAILabel = isComponentElement(candidate, AILabel);
const normalizedDecorator = candidateIsAILabel ? /*#__PURE__*/cloneElement(candidate, {
size: 'mini'
}) : null;
return /*#__PURE__*/React.createElement("div", {
className: containerClasses
}, labelText && /*#__PURE__*/React.createElement(Text, {
as: "label",
htmlFor: id,
className: labelClasses
}, labelText), /*#__PURE__*/React.createElement("div", {
className: wrapperClasses
}, /*#__PURE__*/React.createElement("span", null, input, slug ? normalizedDecorator : decorator ? /*#__PURE__*/React.createElement("div", {
className: `${prefix}--date-picker-input-inner-wrapper--decorator`
}, normalizedDecorator) : '', isFluid && /*#__PURE__*/React.createElement(DatePickerIcon, {
datePickerType: datePickerType
}), /*#__PURE__*/React.createElement(DatePickerIcon, {
datePickerType: datePickerType,
invalid: invalid,
warn: warn
}))), invalid && /*#__PURE__*/React.createElement(React.Fragment, null, isFluid && /*#__PURE__*/React.createElement("hr", {
className: `${prefix}--date-picker__divider`
}), /*#__PURE__*/React.createElement(Text, {
as: "div",
className: `${prefix}--form-requirement`
}, invalidText)), warn && /*#__PURE__*/React.createElement(React.Fragment, null, isFluid && /*#__PURE__*/React.createElement("hr", {
className: `${prefix}--date-picker__divider`
}), /*#__PURE__*/React.createElement(Text, {
as: "div",
className: `${prefix}--form-requirement`
}, warnText)), helperText && !invalid && /*#__PURE__*/React.createElement(Text, {
as: "div",
id: datePickerInputHelperId,
className: helperTextClasses
}, helperText));
});
DatePickerInput.propTypes = {
/**
* The type of the date picker:
*
* * `simple` - Without calendar dropdown.
* * `single` - With calendar dropdown and single date.
* * `range` - With calendar dropdown and a date range.
*/
datePickerType: PropTypes.oneOf(['simple', 'single', 'range']),
/**
* **Experimental**: Provide a decorator component to be rendered inside the `RadioButton` component
*/
decorator: PropTypes.node,
/**
* Specify whether or not the input should be disabled
*/
disabled: PropTypes.bool,
/**
* Provide text that is used alongside the control label for additional help
*/
helperText: PropTypes.node,
/**
* Specify if the label should be hidden
*/
hideLabel: PropTypes.bool,
/**
* Specify an id that uniquely identifies the `<input>`
*/
id: PropTypes.string.isRequired,
/**
* Specify whether or not the input should be invalid
*/
invalid: PropTypes.bool,
/**
* Specify the text to be rendered when the input is invalid
*/
invalidText: PropTypes.node,
/**
* Provide the text that will be read by a screen reader when visiting this
* control
*/
labelText: PropTypes.node.isRequired,
/**
* Specify an `onChange` handler that is called whenever a change in the
* input field has occurred
*/
onChange: PropTypes.func,
/**
* Provide a function to be called when the input field is clicked
*/
onClick: PropTypes.func,
/**
* Provide a regular expression that the input value must match
*/
pattern: (props, propName, componentName) => {
if (props[propName] === undefined) {
return;
}
try {
new RegExp(props[propName]);
} catch (e) {
return new Error(`Invalid value of prop '${propName}' supplied to '${componentName}', it should be a valid regular expression`);
}
},
/**
* Specify the placeholder text
*/
placeholder: PropTypes.string,
/**
* whether the DatePicker is to be readOnly
*/
readOnly: PropTypes.bool,
/**
* Specify the size of the Date Picker Input. Currently supports either `sm`, `md`, or `lg` as an option.
*/
size: PropTypes.oneOf(['sm', 'md', 'lg']),
slug: deprecate(PropTypes.node, 'The `slug` prop has been deprecated and will be removed in the next major version. Use the decorator prop instead.'),
/**
* Specify the type of the `<input>`
*/
type: PropTypes.string,
/**
* Specify whether the control is currently in warning state
*/
warn: PropTypes.bool,
/**
* Provide the text that is displayed when the control is in warning state
*/
warnText: PropTypes.node
};
function DatePickerIcon({
datePickerType,
invalid,
warn
}) {
const prefix = usePrefix();
const {
isFluid
} = useContext(FormContext);
if (datePickerType === 'simple' && !invalid && !warn) {
if (!isFluid) {
return null;
}
}
if (invalid) {
return /*#__PURE__*/React.createElement(WarningFilled, {
className: `${prefix}--date-picker__icon ${prefix}--date-picker__icon--invalid`
});
}
if (!invalid && warn) {
return /*#__PURE__*/React.createElement(WarningAltFilled, {
className: `${prefix}--date-picker__icon ${prefix}--date-picker__icon--warn`
});
}
return /*#__PURE__*/React.createElement(Calendar, {
className: `${prefix}--date-picker__icon`,
role: "img",
"aria-hidden": "true"
});
}
DatePickerIcon.propTypes = {
/**
* The type of the date picker:
*
* * `simple` - Without calendar dropdown.
* * `single` - With calendar dropdown and single date.
* * `range` - With calendar dropdown and a date range.
*/
datePickerType: PropTypes.oneOf(['simple', 'single', 'range']),
/**
* Specify whether or not the input should be invalid
*/
invalid: PropTypes.bool,
/**
* Specify whether the control is currently in warning state
*/
warn: PropTypes.bool
};
export { DatePickerInput as default };