@coreui/react-pro
Version:
UI Components Library for React.js
239 lines (236 loc) • 12.5 kB
JavaScript
import { __rest, __assign, __spreadArray } from '../../node_modules/tslib/tslib.es6.js';
import React, { forwardRef, useRef, useId, useState, useEffect, useCallback, useImperativeHandle } from 'react';
import PropTypes from 'prop-types';
import classNames from '../../_virtual/index.js';
import { CCollapse } from '../collapse/CCollapse.js';
import getNextActiveElement from '../../utils/getNextActiveElement.js';
var CStepper = forwardRef(function (_a, ref) {
var controlledActiveStepNumber = _a.activeStepNumber, controlledActiveStepIndex = _a.activeStepIndex, className = _a.className, defaultActiveStepNumber = _a.defaultActiveStepNumber, _b = _a.defaultActiveStepIndex, defaultActiveStepIndex = _b === void 0 ? 0 : _b, _c = _a.layout, layout = _c === void 0 ? 'horizontal' : _c, _d = _a.linear, linear = _d === void 0 ? true : _d, onFinish = _a.onFinish, onReset = _a.onReset, onStepChange = _a.onStepChange, onStepValidationComplete = _a.onStepValidationComplete, _e = _a.steps, steps = _e === void 0 ? [] : _e, _f = _a.stepButtonLayout, stepButtonLayout = _f === void 0 ? 'horizontal' : _f, _g = _a.validation, validation = _g === void 0 ? true : _g, rest = __rest(_a, ["activeStepNumber", "activeStepIndex", "className", "defaultActiveStepNumber", "defaultActiveStepIndex", "layout", "linear", "onFinish", "onReset", "onStepChange", "onStepValidationComplete", "steps", "stepButtonLayout", "validation"]);
var stepperRef = useRef(null);
var stepsRef = useRef(null);
var stepButtonRefs = useRef([]);
var uniqueId = useId();
// Handle backward compatibility and determine controlled vs uncontrolled mode
var isControlledByNumber = controlledActiveStepNumber !== undefined;
var isControlledByIndex = controlledActiveStepIndex !== undefined;
var isControlled = isControlledByNumber || isControlledByIndex;
// Convert between step numbers (1-based) and indices (0-based)
var getDefaultActiveStepIndex = function () {
if (defaultActiveStepNumber !== undefined) {
return Math.max(0, defaultActiveStepNumber - 1);
}
return defaultActiveStepIndex;
};
var getControlledActiveStepIndex = function () {
if (isControlledByNumber) {
return Math.max(0, controlledActiveStepNumber - 1);
}
if (isControlledByIndex) {
return controlledActiveStepIndex;
}
return 0;
};
var _h = useState(isControlled ? getControlledActiveStepIndex() : getDefaultActiveStepIndex()), internalActiveStepIndex = _h[0], setInternalActiveStepIndex = _h[1];
var _j = useState(false), isFinished = _j[0], setIsFinished = _j[1];
var activeStepIndex = isControlled ? getControlledActiveStepIndex() : internalActiveStepIndex;
// Sync state if controlled prop changes
useEffect(function () {
if (isControlled) {
setInternalActiveStepIndex(getControlledActiveStepIndex());
}
}, [controlledActiveStepNumber, controlledActiveStepIndex, isControlled]);
// Ensure stepButtonRefs array has the correct size
useEffect(function () {
stepButtonRefs.current = stepButtonRefs.current.slice(0, steps.length);
}, [steps.length]);
var isStepValid = useCallback(function (index) {
var _a;
if (!validation) {
return true;
}
var currentStep = steps[index];
var currentStepData = typeof currentStep === 'string' ? { } : currentStep;
var form = (_a = currentStepData === null || currentStepData === void 0 ? void 0 : currentStepData.formRef) === null || _a === void 0 ? void 0 : _a.current;
if (form) {
var isValid = form.checkValidity();
onStepValidationComplete === null || onStepValidationComplete === void 0 ? void 0 : onStepValidationComplete({
stepNumber: index + 1,
isValid: isValid,
});
if (form && !isValid) {
if (!form.noValidate) {
form.reportValidity();
}
return false;
}
}
return true;
}, [steps, validation, onStepValidationComplete]);
var setActiveStep = useCallback(function (index, bypassValidation) {
if (bypassValidation === void 0) { bypassValidation = false; }
if (index < 0 || index >= steps.length || index === activeStepIndex) {
return;
}
// Validate current step before moving forward
if (!bypassValidation && index > activeStepIndex && !isStepValid(activeStepIndex)) {
return;
}
if (!isControlled) {
setInternalActiveStepIndex(index);
}
onStepChange === null || onStepChange === void 0 ? void 0 : onStepChange(index + 1); // Always call with step number (1-based)
}, [steps.length, activeStepIndex, isStepValid, isControlled, onStepChange]);
var handleStepClick = function (index) {
if (linear) {
setActiveStep(index, index <= activeStepIndex);
return;
}
setActiveStep(index, true);
};
var handleNext = function () {
if (activeStepIndex < steps.length - 1) {
setActiveStep(activeStepIndex + 1);
}
else {
handleFinish(); // Attempt finish if already on last step
}
};
var handlePrev = function () {
if (activeStepIndex > 0) {
setActiveStep(activeStepIndex - 1, true); // Bypass validation when going back
}
};
var handleFinish = function () {
if (activeStepIndex === steps.length - 1 && isStepValid(activeStepIndex)) {
setIsFinished(true);
onFinish === null || onFinish === void 0 ? void 0 : onFinish();
}
};
var handleReset = function () {
var _a;
if (validation) {
steps.forEach(function (step) {
var _a, _b;
var stepData = typeof step === 'string' ? { } : step;
(_b = (_a = stepData.formRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.reset();
});
}
var resetIndex = getDefaultActiveStepIndex();
if (!isControlled) {
setInternalActiveStepIndex(resetIndex);
}
setIsFinished(false);
onStepChange === null || onStepChange === void 0 ? void 0 : onStepChange(resetIndex + 1); // Call with step number (1-based)
onReset === null || onReset === void 0 ? void 0 : onReset();
(_a = stepButtonRefs.current[resetIndex]) === null || _a === void 0 ? void 0 : _a.focus();
};
var handleKeyDown = function (event) {
var target = event.target;
var currentButton = target.closest('button.stepper-step-button');
if (!currentButton || !stepsRef.current) {
return;
}
var buttons = __spreadArray([], stepsRef.current.querySelectorAll('button.stepper-step-button'), true);
var nextElement = null;
switch (event.key) {
case 'ArrowRight':
case 'ArrowDown': {
nextElement = getNextActiveElement(buttons, currentButton, true, false);
break;
}
case 'ArrowLeft':
case 'ArrowUp': {
nextElement = getNextActiveElement(buttons, currentButton, false, false);
break;
}
case 'Home': {
nextElement = buttons[0];
break;
}
case 'End': {
nextElement = buttons.at(-1);
break;
}
default: {
return;
}
}
if (nextElement) {
event.preventDefault();
nextElement.focus();
}
};
// Expose methods via ref
useImperativeHandle(ref, function () { return ({
next: handleNext,
prev: handlePrev,
finish: handleFinish,
reset: handleReset,
}); });
var isVertical = layout === 'vertical';
return (React.createElement("div", __assign({ className: classNames('stepper', {
'stepper-vertical': isVertical,
}, className), ref: stepperRef }, rest),
React.createElement("ol", { className: "stepper-steps", "aria-orientation": isVertical ? 'vertical' : 'horizontal', ref: stepsRef, onKeyDown: handleKeyDown, role: "tablist" }, steps.map(function (step, index) {
var _a;
var stepData = typeof step === 'string' ? { label: step } : step;
var isActive = !isFinished && index === activeStepIndex;
var isComplete = isFinished || index < activeStepIndex;
var isDisabled = isFinished || (linear && index > activeStepIndex + 1);
var stepId = "stepper-".concat(rest.id || uniqueId, "-step-").concat(index);
var panelId = "stepper-".concat(rest.id || uniqueId, "-panel-").concat(index);
return (React.createElement("li", { key: index, className: classNames('stepper-step', stepButtonLayout), role: "presentation" },
React.createElement("button", __assign({ type: "button", className: classNames('stepper-step-button', {
active: isActive,
complete: isComplete,
}), disabled: isDisabled, id: stepId, role: "tab", onClick: function () { return handleStepClick(index); }, ref: function (el) {
stepButtonRefs.current[index] = el;
}, "aria-selected": isActive }, (stepData.content && {
'aria-controls': panelId,
}), { tabIndex: isActive ? 0 : -1 }),
React.createElement("span", { className: "stepper-step-indicator" }, isComplete ? (React.createElement("span", { className: "stepper-step-indicator-icon" })) : (React.createElement("span", { className: "stepper-step-indicator-text" }, (_a = stepData.indicator) !== null && _a !== void 0 ? _a : index + 1))),
React.createElement("span", { className: "stepper-step-label" }, stepData.label)),
index < steps.length - 1 && React.createElement("div", { className: "stepper-step-connector" }),
stepData.content && isVertical && (React.createElement(CCollapse, { className: "stepper-step-content", id: panelId, role: "tabpanel", visible: isActive, "aria-hidden": !isActive, "aria-labelledby": stepId, "aria-live": "polite" }, stepData.content))));
})),
!isVertical &&
steps.some(function (step) {
var stepData = typeof step === 'string' ? { } : step;
return stepData.content !== undefined && stepData.content !== null;
}) && (React.createElement("div", { className: "stepper-content" }, steps.map(function (step, index) {
var stepData = typeof step === 'string' ? { } : step;
var isActive = !isFinished && index === activeStepIndex;
var stepId = "stepper-".concat(rest.id || uniqueId, "-step-").concat(index);
var panelId = "stepper-".concat(rest.id || uniqueId, "-panel-").concat(index);
return (React.createElement("div", { key: index, className: classNames('stepper-pane', {
show: isActive,
active: isActive,
}), id: panelId, role: "tabpanel", "aria-hidden": !isActive, "aria-labelledby": stepId, "aria-live": "polite" }, stepData.content));
})))));
});
CStepper.displayName = 'CStepper';
CStepper.propTypes = {
activeStepNumber: PropTypes.number,
activeStepIndex: PropTypes.number,
className: PropTypes.string,
defaultActiveStepNumber: PropTypes.number,
defaultActiveStepIndex: PropTypes.number,
layout: PropTypes.oneOf(['horizontal', 'vertical']),
linear: PropTypes.bool,
onFinish: PropTypes.func,
onReset: PropTypes.func,
onStepChange: PropTypes.func,
onStepValidationComplete: PropTypes.func,
steps: PropTypes.arrayOf(PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
label: PropTypes.node.isRequired,
content: PropTypes.node,
formRef: PropTypes.object, // Check for object shape might be better
}).isRequired,
])).isRequired,
stepButtonLayout: PropTypes.oneOf(['horizontal', 'vertical']),
validation: PropTypes.bool,
};
export { CStepper };
//# sourceMappingURL=CStepper.js.map