@carbon/react
Version:
React components for the Carbon Design System
263 lines (256 loc) • 7.79 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 { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import cx from 'classnames';
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { Enter, Space } from '../../internal/keyboard/keys.js';
import { matches } from '../../internal/keyboard/match.js';
import { Warning, Incomplete, CheckmarkOutline, CircleDash } from '@carbon/icons-react';
import { usePrefix } from '../../internal/usePrefix.js';
import '../Text/index.js';
import { Text } from '../Text/Text.js';
const defaultTranslations = {
'carbon.progress-step.complete': 'Complete',
'carbon.progress-step.incomplete': 'Incomplete',
'carbon.progress-step.current': 'Current',
'carbon.progress-step.invalid': 'Invalid'
};
/**
* Message ids that will be passed to translateWithId().
*/
function translateWithId(messageId) {
return defaultTranslations[messageId];
}
function ProgressIndicator({
children,
className: customClassName,
currentIndex: controlledIndex = 0,
onChange,
spaceEqually,
vertical,
...rest
}) {
const prefix = usePrefix();
const [currentIndex, setCurrentIndex] = useState(controlledIndex);
const [prevControlledIndex, setPrevControlledIndex] = useState(controlledIndex);
const className = cx({
[`${prefix}--progress`]: true,
[`${prefix}--progress--vertical`]: vertical,
[`${prefix}--progress--space-equal`]: spaceEqually && !vertical,
[customClassName ?? '']: customClassName
});
if (controlledIndex !== prevControlledIndex) {
setCurrentIndex(controlledIndex);
setPrevControlledIndex(controlledIndex);
}
return /*#__PURE__*/React.createElement("ul", _extends({
className: className
}, rest), React.Children.map(children, (child, index) => {
if (! /*#__PURE__*/React.isValidElement(child)) {
return null;
}
// only setup click handlers if onChange event is passed
const onClick = onChange ? () => onChange(index) : undefined;
if (index === currentIndex) {
return /*#__PURE__*/React.cloneElement(child, {
complete: child.props.complete,
current: child.props.complete ? false : true,
index,
onClick
});
}
if (index < currentIndex) {
return /*#__PURE__*/React.cloneElement(child, {
complete: true,
index,
onClick
});
}
if (index > currentIndex) {
return /*#__PURE__*/React.cloneElement(child, {
complete: child.props.complete || false,
index,
onClick
});
}
return null;
}));
}
ProgressIndicator.propTypes = {
/**
* Provide `<ProgressStep>` components to be rendered in the
* `<ProgressIndicator>`
*/
children: PropTypes.node,
/**
* Provide an optional className to be applied to the containing node
*/
className: PropTypes.string,
/**
* Optionally specify the current step array index
*/
currentIndex: PropTypes.number,
/**
* Optional callback called if a ProgressStep is clicked on. Returns the index of the step.
*/
onChange: PropTypes.func,
/**
* Specify whether the progress steps should be split equally in size in the div
*/
spaceEqually: PropTypes.bool,
/**
* Determines whether or not the ProgressIndicator should be rendered vertically.
*/
vertical: PropTypes.bool
};
function ProgressStep({
label,
description,
className,
current,
complete,
invalid,
secondaryLabel,
disabled,
onClick,
translateWithId: t = translateWithId,
...rest
}) {
const prefix = usePrefix();
const classes = cx({
[`${prefix}--progress-step`]: true,
[`${prefix}--progress-step--current`]: current,
[`${prefix}--progress-step--complete`]: complete,
[`${prefix}--progress-step--incomplete`]: !complete && !current,
[`${prefix}--progress-step--disabled`]: disabled,
[className ?? '']: className
});
const handleKeyDown = e => {
if (matches(e, [Enter, Space]) && onClick) {
onClick(e);
}
};
const SVGIcon = ({
complete,
current,
description,
invalid,
prefix
}) => {
if (invalid) {
return /*#__PURE__*/React.createElement(Warning, {
className: `${prefix}--progress__warning`
}, /*#__PURE__*/React.createElement("title", null, description));
}
if (current) {
return /*#__PURE__*/React.createElement(Incomplete, null, /*#__PURE__*/React.createElement("title", null, description));
}
if (complete) {
return /*#__PURE__*/React.createElement(CheckmarkOutline, null, /*#__PURE__*/React.createElement("title", null, description));
}
return /*#__PURE__*/React.createElement(CircleDash, null, /*#__PURE__*/React.createElement("title", null, description));
};
let message = t('carbon.progress-step.incomplete');
if (current) {
message = t('carbon.progress-step.current');
}
if (complete) {
message = t('carbon.progress-step.complete');
}
if (invalid) {
message = t('carbon.progress-step.invalid');
}
return /*#__PURE__*/React.createElement("li", {
className: classes
}, /*#__PURE__*/React.createElement("button", _extends({
type: "button",
className: cx(`${prefix}--progress-step-button`, {
[`${prefix}--progress-step-button--unclickable`]: !onClick || current
}),
disabled: disabled,
"aria-disabled": disabled,
tabIndex: !current && onClick && !disabled ? 0 : -1,
onClick: !current ? onClick : undefined,
onKeyDown: handleKeyDown,
title: label
}, rest), /*#__PURE__*/React.createElement(SVGIcon, {
complete: complete,
current: current,
description: description,
invalid: invalid,
prefix: prefix
}), /*#__PURE__*/React.createElement("div", {
className: `${prefix}--progress-text`
}, /*#__PURE__*/React.createElement(Text, {
as: "p",
className: `${prefix}--progress-label`
}, label), secondaryLabel !== null && secondaryLabel !== undefined ? /*#__PURE__*/React.createElement(Text, {
as: "p",
className: `${prefix}--progress-optional`
}, secondaryLabel) : null), /*#__PURE__*/React.createElement("span", {
className: `${prefix}--assistive-text`
}, message), /*#__PURE__*/React.createElement("span", {
className: `${prefix}--progress-line`
})));
}
ProgressStep.propTypes = {
/**
* Provide an optional className to be applied to the containing `<li>` node
*/
className: PropTypes.string,
/**
* Specify whether the step has been completed
*/
complete: PropTypes.bool,
/**
* Specify whether the step is the current step
*/
current: PropTypes.bool,
/**
* Provide a description for the `<ProgressStep>`
*/
description: PropTypes.string,
/**
* Specify whether the step is disabled
*/
disabled: PropTypes.bool,
/**
* Index of the current step within the ProgressIndicator
*/
index: PropTypes.number,
/**
* Specify whether the step is invalid
*/
invalid: PropTypes.bool,
/**
* Provide the label for the `<ProgressStep>`
*/
label: PropTypes.node.isRequired,
/**
* A callback called if the step is clicked or the enter key is pressed
*/
onClick: PropTypes.func,
/**
* Provide the props that describe a progress step tooltip
*/
overflowTooltipProps: PropTypes.object,
/**
* Provide an optional secondary label
*/
secondaryLabel: PropTypes.string,
/**
* The ID of the tooltip content.
*/
tooltipId: PropTypes.string,
/**
* Optional method that takes in a message id and returns an
* internationalized string.
*/
translateWithId: PropTypes.func
};
export { ProgressIndicator, ProgressStep };