kwikid-components-react
Version:
KwikID's Component Library in React
241 lines (234 loc) • 9.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _kwikidToolkit = require("kwikid-toolkit");
var _react = _interopRequireWildcard(require("react"));
var _Messages = _interopRequireDefault(require("../../messages/Messages"));
var _InputOtp = require("./InputOtp.definition");
var _InputOtp2 = require("./InputOtp.style");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
// Constants for magic numbers moved to the top of the file
const DEFAULT_OTP_LENGTH = 6; // Default OTP length
const BASE_36 = 36; // For the random string conversion
const RANDOM_STRING_START = 2; // Starting index for the random string
const RANDOM_STRING_LENGTH = 9; // Length of the random ID suffix
const KwikUIInputOtp = _ref => {
let {
customStyles = {
wrapper: {},
container: {},
label: {},
input: {}
},
disabled = false,
id = "input_otp",
label = undefined,
otpLength = DEFAULT_OTP_LENGTH,
messages = [],
placeholder = undefined,
required = true,
shape = _InputOtp.IKwikUIInputOtpShape.CURVED,
size = _InputOtp.IKwikUIInputOtpSize.M,
updateOn = _InputOtp.IKwikUIInputOtpUpdateOn.CHANGE,
value = undefined,
onInput = undefined,
onInputValidate = undefined,
isInputCopyDisabled = false,
isInputPasteDisabled = false,
onInputCopyDisabled = undefined,
onInputPasteDisabled = undefined,
mode = _InputOtp.IKwikUIInputOtpMode.MULTIPLE
} = _ref;
// Create a unique ID prefix for this component instance
const instanceIdPrefix = _react.default.useMemo(() => "".concat(id, "-").concat(Math.random().toString(BASE_36).substr(RANDOM_STRING_START, RANDOM_STRING_LENGTH)), [id]);
const [valid, setValid] = _react.default.useState(undefined);
const [inputFocused, setInputFocused] = _react.default.useState(undefined);
const [otpValues, setOtpValues] = _react.default.useState(Array(otpLength || DEFAULT_OTP_LENGTH).fill(""));
const [finalOtpValue, setFinalOtpValue] = _react.default.useState(value);
// Create a ref for the container element to scope our DOM queries
const containerRef = _react.default.useRef(null);
// Define handleOnInput at the top to avoid "used before defined" error
const handleOnInput = newValue => {
if (onInputValidate) {
const validation = onInputValidate(id, newValue);
setValid(validation === null || validation === void 0 ? void 0 : validation.isValid);
} else {
setValid(true);
}
// Use Number.isNaN instead of isNaN to fix linter error
if (Number.isNaN(Number(newValue))) {
newValue = "";
}
if (onInput && !disabled) {
onInput(id, newValue);
}
};
(0, _react.useEffect)(() => {
if (value) {
const updatedValue = value.split("");
setOtpValues(updatedValue);
} else {
setOtpValues(new Array(otpLength || DEFAULT_OTP_LENGTH).fill(""));
}
}, [otpLength]);
(0, _react.useEffect)(() => {
if (mode === "multiple" && (value === null || value === void 0 ? void 0 : value.length) === otpLength) {
const updatedValue = value.split("");
if (updatedValue.length) {
setOtpValues(updatedValue);
}
} else {
setFinalOtpValue(value);
}
}, []);
(0, _react.useEffect)(() => {
const updatedValue = (finalOtpValue === null || finalOtpValue === void 0 ? void 0 : finalOtpValue.split("")) || [];
if (updatedValue.length !== otpLength) {
while (updatedValue.length !== otpLength) {
updatedValue.push("");
}
}
setOtpValues(updatedValue);
}, [mode]);
const handleSingleChange = e => {
if (/^\d*$/.test(e.target.value)) {
setFinalOtpValue(e.target.value);
handleOnInput(e.target.value);
}
};
const handleChange = (e, index) => {
if (/^\d*$/.test(e.target.value)) {
const newOtpValues = [...otpValues];
newOtpValues[index] = e.target.value;
setOtpValues(newOtpValues);
const newValue = newOtpValues.join("");
setFinalOtpValue(newValue);
handleOnInput(newValue);
if (e.target.value && index < otpLength - 1) {
if (containerRef.current) {
const nextField = containerRef.current.querySelector("#".concat(instanceIdPrefix, "-box-").concat(index + 1));
if (nextField) {
nextField.focus();
}
}
}
}
};
const handleOnKeyDown = (e, index) => {
if (e.key === "Backspace" && otpValues[index] === "") {
if (index > 0) {
if (containerRef.current) {
const prevField = containerRef.current.querySelector("#".concat(instanceIdPrefix, "-box-").concat(index - 1));
if (prevField) {
prevField.focus();
}
}
}
}
};
const handleOnInputCopy = e => {
if (isInputCopyDisabled) {
e.preventDefault();
if (onInputCopyDisabled) {
onInputCopyDisabled();
}
}
};
const handleOnInputPaste = e => {
if (isInputPasteDisabled) {
e.preventDefault();
if (onInputPasteDisabled) {
onInputPasteDisabled();
}
}
};
const handleOnClick = (_e, _index) => {
// Find the first empty input in this OTP component and focus it
if (containerRef.current && mode === "multiple") {
// Find the index of the first empty value
const firstEmptyIndex = otpValues.findIndex(value => value === "");
// Only proceed if there's an empty input and we're not at the end
if (firstEmptyIndex !== -1) {
// Find the input element for this index within this component instance
const firstEmptyInput = containerRef.current.querySelector("#".concat(instanceIdPrefix, "-box-").concat(firstEmptyIndex));
// Focus the first empty input if found
if (firstEmptyInput) {
firstEmptyInput.focus();
}
}
}
};
return /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpWrapper, {
style: customStyles.wrapper
}, label && mode === "multiple" && /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpOutsideLabel, {
size: size,
disabled: disabled,
style: customStyles.label
}, label, required && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "\xA0*")), /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpContainer, {
ref: containerRef,
size: size,
shape: shape,
disabled: disabled,
className: "\n input input-number\n ".concat(valid === false ? "input-error" : "", "\n ").concat(valid !== false && inputFocused ? "input-focused" : "", "\n "),
style: customStyles.container
}, mode === "multiple" ? /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpBoxesContainer, null, otpValues.map((value, index) => /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpBox, {
key: index,
size: size,
shape: shape,
disabled: disabled,
style: customStyles.input
}, /*#__PURE__*/_react.default.createElement("input", {
disabled: disabled,
id: "".concat(instanceIdPrefix, "-box-").concat(index),
type: "text",
maxLength: 1,
value: value,
placeholder: placeholder ? placeholder.substring(0, 1) : "",
onChange: e => handleChange(e, index),
onFocus: () => setInputFocused(true),
onBlur: () => {
if (updateOn === "blur") {
handleOnInput(otpValues.join(""));
}
setInputFocused(false);
},
onKeyDown: e => handleOnKeyDown(e, index),
onCopy: handleOnInputCopy,
onPaste: handleOnInputPaste,
onClick: e => handleOnClick(e, index),
style: customStyles.input
})))) : /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpSingleContainer, {
shape: shape,
size: size,
disabled: disabled
}, label && /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpLabel, {
size: size,
disabled: disabled,
style: customStyles.label
}, label, required && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "\xA0*")), /*#__PURE__*/_react.default.createElement("input", {
disabled: disabled,
id: id,
type: "text",
maxLength: otpLength,
value: finalOtpValue,
placeholder: placeholder,
onChange: handleSingleChange,
onFocus: () => setInputFocused(true),
onBlur: e => {
if (updateOn === "blur") {
handleOnInput(e.target.value);
}
setInputFocused(false);
},
onCopy: handleOnInputCopy,
onPaste: handleOnInputPaste,
style: customStyles.input
}))), (0, _kwikidToolkit.isNotEmptyValue)(messages) && /*#__PURE__*/_react.default.createElement(_InputOtp2.KwikUIStyleInputOtpMessagesContainer, null, /*#__PURE__*/_react.default.createElement(_Messages.default, {
messages: messages,
size: size
})));
};
var _default = exports.default = KwikUIInputOtp;