@mskcc/carbon-react
Version:
Carbon react components for the MSKCC DSM
233 lines (229 loc) • 7.55 kB
JavaScript
/**
* MSKCC 2021, 2024
*/
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import PropTypes from 'prop-types';
import React__default, { useContext, useState, useRef } from 'react';
import cx from 'classnames';
import deprecate from '../../prop-types/deprecate.js';
import { usePrefix } from '../../internal/usePrefix.js';
import '../FluidForm/FluidForm.js';
import { FormContext } from '../FluidForm/FormContext.js';
import setupGetInstanceId from '../../tools/setupGetInstanceId.js';
import { Icon } from '../Icon/MskIcon.js';
const getInstanceId = setupGetInstanceId();
const Select = /*#__PURE__*/React__default.forwardRef(function Select(_ref, ref) {
let {
className,
id,
inline = false,
labelText,
disabled = false,
children,
noLabel = false,
hideLabel = false,
invalid = false,
invalidText = '',
helperText = '',
light = false,
readOnly,
size,
warn = false,
warnText,
value,
placeholder,
...other
} = _ref;
const prefix = usePrefix();
const {
isFluid
} = useContext(FormContext);
const [isFocused, setIsFocused] = useState(false);
const {
current: selectInstanceId
} = useRef(getInstanceId());
const selectClasses = cx({
[`${prefix}--select`]: true,
[`${prefix}--select--inline`]: inline,
[`${prefix}--select--light`]: light,
[`${prefix}--select--invalid`]: invalid,
[`${prefix}--select--disabled`]: disabled,
[`${prefix}--select--readonly`]: readOnly,
[`${prefix}--select--warning`]: warn,
[`${prefix}--select--fluid--invalid`]: isFluid && invalid,
[`${prefix}--select--fluid--focus`]: isFluid && isFocused
});
const labelClasses = cx(`${prefix}--label`, {
[`${prefix}--visually-hidden`]: hideLabel,
[`${prefix}--label--disabled`]: disabled
});
const inputClasses = cx({
[`${prefix}--select-input`]: true,
[`${prefix}--select-input--${size}`]: size
});
const errorId = `${id}-error-msg`;
const errorText = invalid ? invalidText : warn ? warnText : null;
const error = invalid || warn ? /*#__PURE__*/React__default.createElement("div", {
className: `msk-validation-msg`
}, /*#__PURE__*/React__default.createElement("span", {
className: `msk-validation-msg--icon msk-icon`
}, invalid ? 'error' : 'warning'), /*#__PURE__*/React__default.createElement("div", {
id: errorId,
className: `${prefix}--form-requirement`
}, errorText)) : null;
const helperTextClasses = cx(`${prefix}--form__helper-text`, {
[`${prefix}--form__helper-text--disabled`]: disabled
});
const helperId = helperText ? `select-helper-text-${selectInstanceId}` : undefined;
const helper = helperText ? /*#__PURE__*/React__default.createElement("div", {
id: helperId,
className: helperTextClasses
}, helperText) : null;
const ariaProps = {
...(invalid ? {
'aria-describedby': errorId
} : {}),
...(!inline && !isFluid || invalid ? {} : {
'aria-describedby': helperId
})
};
const handleFocus = evt => {
setIsFocused(evt.type === 'focus');
};
const readOnlyEventHandlers = {
onMouseDown: evt => {
if (readOnly) {
evt.preventDefault();
evt.currentTarget.focus();
}
},
onKeyDown: evt => {
const selectAccessKeys = ['ArrowDown', 'ArrowUp', ' '];
if (readOnly && selectAccessKeys.includes(evt.key)) {
evt.preventDefault();
}
}
};
const input = /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("select", _extends({}, other, ariaProps, {
id: id,
className: inputClasses,
disabled: disabled,
"aria-invalid": invalid,
"aria-readonly": readOnly,
value: value,
placeholder: placeholder
}, readOnlyEventHandlers, {
ref: ref
}), children), /*#__PURE__*/React__default.createElement(Icon, {
icon: "expand_more",
className: `${prefix}--select__arrow`
}));
return /*#__PURE__*/React__default.createElement("div", {
className: cx(`${prefix}--form-item`, className)
}, /*#__PURE__*/React__default.createElement("div", {
className: selectClasses
}, !noLabel && /*#__PURE__*/React__default.createElement("label", {
htmlFor: id,
className: labelClasses
}, labelText), inline ? /*#__PURE__*/React__default.createElement("div", {
className: `${prefix}--select-input--inline__wrapper`
}, /*#__PURE__*/React__default.createElement("div", {
className: `${prefix}--select-input__wrapper`,
"data-invalid": invalid || null
}, input), error) : /*#__PURE__*/React__default.createElement("div", {
className: `${prefix}--select-input__wrapper`,
"data-invalid": invalid || null,
onFocus: handleFocus,
onBlur: handleFocus
}, input, isFluid && /*#__PURE__*/React__default.createElement("hr", {
className: `${prefix}--select__divider`
}), isFluid && error ? error : null), !inline && !isFluid && error ? error : helper));
});
Select.displayName = 'Select';
Select.propTypes = {
/**
* Provide the contents of your Select
*/
children: PropTypes.node,
/**
* Specify an optional className to be applied to the node containing the label and the select box
*/
className: PropTypes.string,
/**
* Optionally provide the default value of the `<select>`
*/
defaultValue: PropTypes.any,
/**
* Specify whether the control is disabled
*/
disabled: PropTypes.bool,
/**
* Provide text that is used alongside the control label for additional help
*/
helperText: PropTypes.node,
/**
* Specify whether the label should be hidden, or not
*/
hideLabel: PropTypes.bool,
/**
* Specify a custom `id` for the `<select>`
*/
id: PropTypes.string.isRequired,
/**
* Specify whether you want the inline version of this control
*/
inline: PropTypes.bool,
/**
* Specify if the currently value is invalid.
*/
invalid: PropTypes.bool,
/**
* Message which is displayed if the value is invalid.
*/
invalidText: PropTypes.node,
/**
* Provide label text to be read by screen readers when interacting with the
* control
*/
labelText: PropTypes.node,
/**
* `true` to use the light version. For use on $ui-01 backgrounds only.
* Don't use this to make tile background color same as container background color.
*/
light: deprecate(PropTypes.bool, 'The `light` prop for `Select` is no longer needed and has ' + 'been deprecated in v11 in favor of the new `Layer` component. It will be moved in the next major release.'),
/**
* Reserved for use with <Pagination> component. Will not render a label for the
* select since Pagination renders one for us.
*/
noLabel: PropTypes.bool,
/**
* Provide an optional `onChange` hook that is called each time the value of
* the underlying `<input>` changes
*/
onChange: PropTypes.func,
/**
* Whether the select should be read-only
*/
readOnly: PropTypes.bool,
/**
* Specify the size of the Select Input.
*/
size: PropTypes.oneOf(['sm', 'md', 'lg']),
/**
* 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,
/**
* Specify the value of the select input.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/**
* Placeholder for the select input.
*/
placeholder: PropTypes.string
};
export { Select as default };