@coreui/react-pro 5.22.0 → 5.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/autocomplete/CAutocomplete.js +2 -2
  3. package/dist/cjs/components/autocomplete/CAutocomplete.js.map +1 -1
  4. package/dist/cjs/components/focus-trap/CFocusTrap.js +23 -6
  5. package/dist/cjs/components/focus-trap/CFocusTrap.js.map +1 -1
  6. package/dist/cjs/components/focus-trap/utils.d.ts +10 -0
  7. package/dist/cjs/components/focus-trap/utils.js +25 -0
  8. package/dist/cjs/components/focus-trap/utils.js.map +1 -1
  9. package/dist/cjs/components/index.d.ts +1 -0
  10. package/dist/cjs/components/modal/CModal.js +10 -11
  11. package/dist/cjs/components/modal/CModal.js.map +1 -1
  12. package/dist/cjs/components/one-time-password-input/COneTimePassword.d.ts +73 -0
  13. package/dist/cjs/components/one-time-password-input/COneTimePassword.js +178 -0
  14. package/dist/cjs/components/one-time-password-input/COneTimePassword.js.map +1 -0
  15. package/dist/cjs/components/one-time-password-input/COneTimePasswordInput.d.ts +8 -0
  16. package/dist/cjs/components/one-time-password-input/COneTimePasswordInput.js +14 -0
  17. package/dist/cjs/components/one-time-password-input/COneTimePasswordInput.js.map +1 -0
  18. package/dist/cjs/components/one-time-password-input/index.d.ts +5 -0
  19. package/dist/cjs/components/one-time-password-input/utils.d.ts +2 -0
  20. package/dist/cjs/components/one-time-password-input/utils.js +28 -0
  21. package/dist/cjs/components/one-time-password-input/utils.js.map +1 -0
  22. package/dist/cjs/index.js +4 -0
  23. package/dist/cjs/index.js.map +1 -1
  24. package/dist/esm/components/autocomplete/CAutocomplete.js +2 -2
  25. package/dist/esm/components/autocomplete/CAutocomplete.js.map +1 -1
  26. package/dist/esm/components/focus-trap/CFocusTrap.js +24 -7
  27. package/dist/esm/components/focus-trap/CFocusTrap.js.map +1 -1
  28. package/dist/esm/components/focus-trap/utils.d.ts +10 -0
  29. package/dist/esm/components/focus-trap/utils.js +25 -1
  30. package/dist/esm/components/focus-trap/utils.js.map +1 -1
  31. package/dist/esm/components/index.d.ts +1 -0
  32. package/dist/esm/components/modal/CModal.js +10 -11
  33. package/dist/esm/components/modal/CModal.js.map +1 -1
  34. package/dist/esm/components/one-time-password-input/COneTimePassword.d.ts +73 -0
  35. package/dist/esm/components/one-time-password-input/COneTimePassword.js +176 -0
  36. package/dist/esm/components/one-time-password-input/COneTimePassword.js.map +1 -0
  37. package/dist/esm/components/one-time-password-input/COneTimePasswordInput.d.ts +8 -0
  38. package/dist/esm/components/one-time-password-input/COneTimePasswordInput.js +12 -0
  39. package/dist/esm/components/one-time-password-input/COneTimePasswordInput.js.map +1 -0
  40. package/dist/esm/components/one-time-password-input/index.d.ts +5 -0
  41. package/dist/esm/components/one-time-password-input/utils.d.ts +2 -0
  42. package/dist/esm/components/one-time-password-input/utils.js +25 -0
  43. package/dist/esm/components/one-time-password-input/utils.js.map +1 -0
  44. package/dist/esm/index.js +2 -0
  45. package/dist/esm/index.js.map +1 -1
  46. package/package.json +10 -10
  47. package/src/components/autocomplete/CAutocomplete.tsx +18 -16
  48. package/src/components/focus-trap/CFocusTrap.tsx +33 -8
  49. package/src/components/focus-trap/utils.ts +24 -0
  50. package/src/components/index.ts +1 -0
  51. package/src/components/modal/CModal.tsx +30 -31
  52. package/src/components/one-time-password-input/COneTimePassword.tsx +462 -0
  53. package/src/components/one-time-password-input/COneTimePasswordInput.tsx +25 -0
  54. package/src/components/one-time-password-input/__tests__/COneTimePassword.spec.tsx +361 -0
  55. package/src/components/one-time-password-input/__tests__/__snapshots__/COneTimePassword.spec.tsx.snap +130 -0
  56. package/src/components/one-time-password-input/index.ts +6 -0
  57. package/src/components/one-time-password-input/utils.ts +23 -0
@@ -0,0 +1,176 @@
1
+ import { __rest } from '../../node_modules/tslib/tslib.es6.js';
2
+ import React, { forwardRef, useRef, useMemo, Children, useState, useEffect, useCallback, isValidElement, cloneElement } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import classNames from '../../_virtual/index.js';
5
+ import { CFormControlWrapper } from '../form/CFormControlWrapper.js';
6
+ import { isValidInput, extractValidChars } from './utils.js';
7
+ import getNextActiveElement from '../../utils/getNextActiveElement.js';
8
+ import isRTL from '../../utils/isRTL.js';
9
+
10
+ const COneTimePassword = forwardRef((_a, ref) => {
11
+ var { ariaLabel = (index, total) => `Digit ${index + 1} of ${total}`, autoSubmit = false, children, className, defaultValue, disabled = false, feedback, feedbackInvalid, feedbackValid, floatingClassName, floatingLabel, id, invalid, label, linear = true, masked = false, name, onChange, onComplete, placeholder, readOnly = false, required = false, size, text, tooltipFeedback, type = 'number', valid, value } = _a, rest = __rest(_a, ["ariaLabel", "autoSubmit", "children", "className", "defaultValue", "disabled", "feedback", "feedbackInvalid", "feedbackValid", "floatingClassName", "floatingLabel", "id", "invalid", "label", "linear", "masked", "name", "onChange", "onComplete", "placeholder", "readOnly", "required", "size", "text", "tooltipFeedback", "type", "valid", "value"]);
12
+ const inputRefs = useRef([]);
13
+ const hiddenInputRef = useRef(null);
14
+ // Count valid OTP input children
15
+ const inputCount = useMemo(() => {
16
+ return Children.count(children);
17
+ }, [children]);
18
+ const [inputValues, setInputValues] = useState(() => {
19
+ var _a;
20
+ const initialValue = String((_a = value !== null && value !== void 0 ? value : defaultValue) !== null && _a !== void 0 ? _a : '');
21
+ return Array.from({ length: inputCount }, (_, i) => initialValue[i] || '');
22
+ });
23
+ // Update input values when value prop changes (controlled mode)
24
+ useEffect(() => {
25
+ if (value !== undefined) {
26
+ const valueString = String(value);
27
+ setInputValues(Array.from({ length: inputCount }, (_, i) => valueString[i] || ''));
28
+ }
29
+ }, [value, inputCount]);
30
+ // Update hidden input and trigger events
31
+ const updateValue = useCallback((newValues) => {
32
+ var _a;
33
+ const newValue = newValues.join('');
34
+ if (hiddenInputRef.current) {
35
+ hiddenInputRef.current.value = newValue;
36
+ }
37
+ onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
38
+ if (newValue.length === inputCount) {
39
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete(newValue);
40
+ if (autoSubmit) {
41
+ const form = (_a = hiddenInputRef.current) === null || _a === void 0 ? void 0 : _a.closest('form');
42
+ if (form && typeof form.requestSubmit === 'function') {
43
+ form.requestSubmit();
44
+ }
45
+ }
46
+ }
47
+ }, [autoSubmit, onChange, onComplete, inputCount]);
48
+ const handleInputChange = useCallback((index, event) => {
49
+ const inputValue = event.target.value;
50
+ if (inputValue.length === 1 && !isValidInput(inputValue, type)) {
51
+ return;
52
+ }
53
+ const newValues = [...inputValues];
54
+ newValues[index] = inputValue.length === 1 ? inputValue : '';
55
+ setInputValues(newValues);
56
+ updateValue(newValues);
57
+ if (inputValue.length === 1) {
58
+ const nextInput = getNextActiveElement(inputRefs.current, event.target, true, false);
59
+ nextInput === null || nextInput === void 0 ? void 0 : nextInput.focus();
60
+ }
61
+ }, [inputValues, type, updateValue]);
62
+ const handleInputFocus = useCallback((event) => {
63
+ const { target } = event;
64
+ if (target.value) {
65
+ setTimeout(() => {
66
+ target.select();
67
+ }, 0);
68
+ return;
69
+ }
70
+ if (linear) {
71
+ const firstEmptyInput = inputRefs.current.find((input) => !(input === null || input === void 0 ? void 0 : input.value));
72
+ if (firstEmptyInput && firstEmptyInput !== target) {
73
+ firstEmptyInput.focus();
74
+ }
75
+ }
76
+ }, [linear]);
77
+ const handleKeyDown = useCallback((event) => {
78
+ const { key, target } = event;
79
+ if (key === 'Backspace' && target.value === '') {
80
+ const newValues = [...inputValues];
81
+ const prevInput = getNextActiveElement(inputRefs.current, target, false, false);
82
+ if (prevInput) {
83
+ const prevIndex = inputRefs.current.indexOf(prevInput);
84
+ if (prevIndex !== -1) {
85
+ newValues[prevIndex] = '';
86
+ setInputValues(newValues);
87
+ updateValue(newValues);
88
+ prevInput.focus();
89
+ }
90
+ }
91
+ return;
92
+ }
93
+ if (key === 'ArrowRight') {
94
+ if (linear && target.value === '') {
95
+ return;
96
+ }
97
+ const shouldMoveNext = !isRTL(target);
98
+ const nextInput = getNextActiveElement(inputRefs.current, target, shouldMoveNext, false);
99
+ nextInput === null || nextInput === void 0 ? void 0 : nextInput.focus();
100
+ return;
101
+ }
102
+ if (key === 'ArrowLeft') {
103
+ const shouldMoveNext = isRTL(target);
104
+ const prevInput = getNextActiveElement(inputRefs.current, target, shouldMoveNext, false);
105
+ prevInput === null || prevInput === void 0 ? void 0 : prevInput.focus();
106
+ }
107
+ }, [inputValues, linear, updateValue]);
108
+ const handlePaste = useCallback((index, event) => {
109
+ var _a, _b;
110
+ event.preventDefault();
111
+ const pastedData = event.clipboardData.getData('text');
112
+ const validChars = extractValidChars(pastedData, type);
113
+ if (!validChars)
114
+ return;
115
+ const newValues = [...inputValues];
116
+ const startIndex = index;
117
+ for (let i = 0; i < validChars.length && startIndex + i < inputCount; i++) {
118
+ newValues[startIndex + i] = validChars[i];
119
+ }
120
+ setInputValues(newValues);
121
+ updateValue(newValues);
122
+ // Focus the next empty input or the last filled input
123
+ const nextEmptyIndex = startIndex + validChars.length;
124
+ if (nextEmptyIndex < inputCount) {
125
+ (_a = inputRefs.current[nextEmptyIndex]) === null || _a === void 0 ? void 0 : _a.focus();
126
+ }
127
+ else {
128
+ (_b = inputRefs.current.at(-1)) === null || _b === void 0 ? void 0 : _b.focus();
129
+ }
130
+ }, [inputValues, type, updateValue, inputCount]);
131
+ let inputIndex = 0;
132
+ return (React.createElement(CFormControlWrapper, { describedby: rest['aria-describedby'], feedback: feedback, feedbackInvalid: feedbackInvalid, feedbackValid: feedbackValid, floatingClassName: floatingClassName, floatingLabel: floatingLabel, id: id, invalid: invalid, label: label, text: text, tooltipFeedback: tooltipFeedback, valid: valid },
133
+ React.createElement("div", Object.assign({ className: classNames('form-otp', {
134
+ [`form-otp-${size}`]: size,
135
+ }, className), role: "group", ref: ref }, rest),
136
+ Children.map(children, (child, index) => {
137
+ var _a;
138
+ if (!isValidElement(child) ||
139
+ ((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) !== 'COneTimePasswordInput') {
140
+ return child;
141
+ }
142
+ const inputChild = child;
143
+ const currentInputIndex = inputIndex++;
144
+ return cloneElement(inputChild, Object.assign(Object.assign({}, inputChild.props), { type: masked ? 'password' : 'text', className: classNames({
145
+ 'is-invalid': invalid,
146
+ 'is-valid': valid,
147
+ }, inputChild.props.className), id: inputChild.props.id || (id ? `${id}-${currentInputIndex}` : undefined), name: inputChild.props.name || (name ? `${name}-${currentInputIndex}` : undefined), placeholder: inputChild.props.placeholder ||
148
+ (placeholder && placeholder.length > 1
149
+ ? placeholder[currentInputIndex]
150
+ : placeholder), value: inputValues[currentInputIndex] || '', tabIndex: currentInputIndex === 0 ? 0 : inputValues[currentInputIndex - 1] ? 0 : -1, disabled: disabled || inputChild.props.disabled, readOnly: readOnly || inputChild.props.readOnly, required: required || inputChild.props.required, 'aria-label': inputChild.props['aria-label'] || ariaLabel(currentInputIndex, inputCount), inputMode: type === 'number' ? 'numeric' : 'text', pattern: type === 'number' ? '[0-9]*' : '.*', onInput: (event) => {
151
+ var _a, _b;
152
+ handleInputChange(currentInputIndex, event);
153
+ (_b = (_a = inputChild.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, event);
154
+ }, onFocus: (event) => {
155
+ var _a, _b;
156
+ handleInputFocus(event);
157
+ (_b = (_a = inputChild.props).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, event);
158
+ }, onKeyDown: (event) => {
159
+ var _a, _b;
160
+ handleKeyDown(currentInputIndex, event);
161
+ (_b = (_a = inputChild.props).onKeyDown) === null || _b === void 0 ? void 0 : _b.call(_a, event);
162
+ }, onPaste: (event) => {
163
+ var _a, _b;
164
+ handlePaste(index, event);
165
+ (_b = (_a = inputChild.props).onPaste) === null || _b === void 0 ? void 0 : _b.call(_a, event);
166
+ }, ref: (el) => {
167
+ inputRefs.current[currentInputIndex] = el;
168
+ } }));
169
+ }),
170
+ React.createElement("input", { type: "hidden", id: id, name: name, value: inputValues.join(''), disabled: disabled, ref: hiddenInputRef }))));
171
+ });
172
+ COneTimePassword.propTypes = Object.assign({ ariaLabel: PropTypes.func, autoSubmit: PropTypes.bool, children: PropTypes.node, className: PropTypes.string, defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), disabled: PropTypes.bool, id: PropTypes.string, linear: PropTypes.bool, masked: PropTypes.bool, name: PropTypes.string, onChange: PropTypes.func, onComplete: PropTypes.func, placeholder: PropTypes.string, readOnly: PropTypes.bool, required: PropTypes.bool, size: PropTypes.oneOf(['sm', 'lg']), type: PropTypes.oneOf(['number', 'text']), value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) }, CFormControlWrapper.propTypes);
173
+ COneTimePassword.displayName = 'COneTimePassword';
174
+
175
+ export { COneTimePassword };
176
+ //# sourceMappingURL=COneTimePassword.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COneTimePassword.js","sources":["../../../../src/components/one-time-password-input/COneTimePassword.tsx"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import React, { InputHTMLAttributes } from 'react';
2
+ export interface COneTimePasswordInputProps extends InputHTMLAttributes<HTMLInputElement> {
3
+ /**
4
+ * A string of all className you want applied to the component.
5
+ */
6
+ className?: string;
7
+ }
8
+ export declare const COneTimePasswordInput: React.ForwardRefExoticComponent<COneTimePasswordInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,12 @@
1
+ import { __rest } from '../../node_modules/tslib/tslib.es6.js';
2
+ import React, { forwardRef } from 'react';
3
+ import classNames from '../../_virtual/index.js';
4
+
5
+ const COneTimePasswordInput = forwardRef((_a, ref) => {
6
+ var { className } = _a, rest = __rest(_a, ["className"]);
7
+ return (React.createElement("input", Object.assign({ className: classNames('form-otp-control', className), maxLength: 1, autoComplete: "off", ref: ref }, rest)));
8
+ });
9
+ COneTimePasswordInput.displayName = 'COneTimePasswordInput';
10
+
11
+ export { COneTimePasswordInput };
12
+ //# sourceMappingURL=COneTimePasswordInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"COneTimePasswordInput.js","sources":["../../../../src/components/one-time-password-input/COneTimePasswordInput.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;AAUO,MAAM,qBAAqB,GAAG,UAAU,CAC7C,CAAC,EAAsB,EAAE,GAAG,KAAI;AAA/B,IAAA,IAAA,EAAE,SAAS,EAAA,GAAA,EAAW,EAAN,IAAI,GAAA,MAAA,CAAA,EAAA,EAApB,aAAsB,CAAF;IACnB,QACE,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,MAAA,CAAA,MAAA,CAAA,EACE,SAAS,EAAE,UAAU,CAAC,kBAAkB,EAAE,SAAS,CAAC,EACpD,SAAS,EAAE,CAAC,EACZ,YAAY,EAAC,KAAK,EAClB,GAAG,EAAE,GAAG,EAAA,EACJ,IAAI,CAAA,CACR;AAEN,CAAC;AAGH,qBAAqB,CAAC,WAAW,GAAG,uBAAuB;;;;"}
@@ -0,0 +1,5 @@
1
+ import { COneTimePassword } from './COneTimePassword';
2
+ import { COneTimePasswordInput } from './COneTimePasswordInput';
3
+ export { COneTimePassword, COneTimePasswordInput };
4
+ export type { COneTimePasswordProps } from './COneTimePassword';
5
+ export type { COneTimePasswordInputProps } from './COneTimePasswordInput';
@@ -0,0 +1,2 @@
1
+ export declare const isValidInput: (inputValue: string, type: "number" | "text") => boolean;
2
+ export declare const extractValidChars: (text: string, type: "number" | "text") => string;
@@ -0,0 +1,25 @@
1
+ const isValidInput = (inputValue, type) => {
2
+ if (inputValue.length !== 1)
3
+ return false;
4
+ switch (type) {
5
+ case 'number': {
6
+ return /^\d$/.test(inputValue);
7
+ }
8
+ default: {
9
+ return /^.$/.test(inputValue);
10
+ }
11
+ }
12
+ };
13
+ const extractValidChars = (text, type) => {
14
+ switch (type) {
15
+ case 'number': {
16
+ return text.replaceAll(/\D/g, '');
17
+ }
18
+ default: {
19
+ return text;
20
+ }
21
+ }
22
+ };
23
+
24
+ export { extractValidChars, isValidInput };
25
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../../../src/components/one-time-password-input/utils.ts"],"sourcesContent":[null],"names":[],"mappings":"MAAa,YAAY,GAAG,CAAC,UAAkB,EAAE,IAAuB,KAAa;AACnF,IAAA,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,KAAK;IAEzC,QAAQ,IAAI;QACV,KAAK,QAAQ,EAAE;AACb,YAAA,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QAChC;QACA,SAAS;AACP,YAAA,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;QAC/B;;AAEJ;MAEa,iBAAiB,GAAG,CAAC,IAAY,EAAE,IAAuB,KAAY;IACjF,QAAQ,IAAI;QACV,KAAK,QAAQ,EAAE;YACb,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;QACnC;QACA,SAAS;AACP,YAAA,OAAO,IAAI;QACb;;AAEJ;;;;"}
package/dist/esm/index.js CHANGED
@@ -98,6 +98,8 @@ export { COffcanvas } from './components/offcanvas/COffcanvas.js';
98
98
  export { COffcanvasBody } from './components/offcanvas/COffcanvasBody.js';
99
99
  export { COffcanvasHeader } from './components/offcanvas/COffcanvasHeader.js';
100
100
  export { COffcanvasTitle } from './components/offcanvas/COffcanvasTitle.js';
101
+ export { COneTimePassword } from './components/one-time-password-input/COneTimePassword.js';
102
+ export { COneTimePasswordInput } from './components/one-time-password-input/COneTimePasswordInput.js';
101
103
  export { CPasswordInput } from './components/password-input/CPasswordInput.js';
102
104
  export { CPagination } from './components/pagination/CPagination.js';
103
105
  export { CPaginationItem } from './components/pagination/CPaginationItem.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react-pro",
3
- "version": "5.22.0",
3
+ "version": "5.23.1",
4
4
  "description": "UI Components Library for React.js",
5
5
  "keywords": [
6
6
  "react",
@@ -41,34 +41,34 @@
41
41
  "test:update": "jest --coverage --updateSnapshot"
42
42
  },
43
43
  "dependencies": {
44
- "@coreui/coreui-pro": "^5.20.0",
44
+ "@coreui/coreui-pro": "^5.21.0",
45
45
  "@popperjs/core": "^2.11.8",
46
46
  "prop-types": "^15.8.1"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@rollup/plugin-commonjs": "^28.0.6",
50
- "@rollup/plugin-node-resolve": "^16.0.1",
50
+ "@rollup/plugin-node-resolve": "^16.0.2",
51
51
  "@rollup/plugin-typescript": "^12.1.4",
52
52
  "@testing-library/dom": "^10.4.1",
53
- "@testing-library/jest-dom": "^6.8.0",
53
+ "@testing-library/jest-dom": "^6.9.1",
54
54
  "@testing-library/react": "^16.3.0",
55
55
  "@types/jest": "^29.5.14",
56
56
  "@types/prop-types": "15.7.15",
57
- "@types/react": "^19.1.13",
58
- "@types/react-dom": "^19.1.9",
57
+ "@types/react": "^19.2.0",
58
+ "@types/react-dom": "^19.2.0",
59
59
  "@types/react-transition-group": "^4.4.12",
60
60
  "classnames": "^2.5.1",
61
- "cross-env": "^10.0.0",
61
+ "cross-env": "^10.1.0",
62
62
  "jest": "^29.7.0",
63
63
  "jest-environment-jsdom": "^29.7.0",
64
64
  "react": "^18.3.1",
65
65
  "react-device-detect": "^2.2.3",
66
66
  "react-dom": "^18.3.1",
67
67
  "react-transition-group": "^4.4.5",
68
- "rollup": "^4.50.2",
69
- "ts-jest": "^29.4.2",
68
+ "rollup": "^4.52.4",
69
+ "ts-jest": "^29.4.4",
70
70
  "tslib": "^2.8.1",
71
- "typescript": "^5.9.2"
71
+ "typescript": "^5.9.3"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "react": ">=17",
@@ -749,23 +749,25 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
749
749
  }}
750
750
  ></button>
751
751
  )}
752
- <button
753
- type="button"
754
- className="autocomplete-indicator"
755
- disabled={
756
- !(searchNoResultsLabel || filteredOptions.length > 0) &&
757
- isExternalSearch(search)
758
- }
759
- onClick={(event) => {
760
- event.preventDefault()
761
- event.stopPropagation()
762
- if (_visible) {
763
- handleDropdownHide()
764
- } else {
765
- handleDropdownShow()
752
+ {indicator && (
753
+ <button
754
+ type="button"
755
+ className="autocomplete-indicator"
756
+ disabled={
757
+ !(searchNoResultsLabel || filteredOptions.length > 0) &&
758
+ isExternalSearch(search)
766
759
  }
767
- }}
768
- ></button>
760
+ onClick={(event) => {
761
+ event.preventDefault()
762
+ event.stopPropagation()
763
+ if (_visible) {
764
+ handleDropdownHide()
765
+ } else {
766
+ handleDropdownShow()
767
+ }
768
+ }}
769
+ ></button>
770
+ )}
769
771
  </div>
770
772
  )}
771
773
  </div>
@@ -1,5 +1,5 @@
1
1
  import React, { FC, ReactElement, cloneElement, useEffect, useRef } from 'react'
2
- import { mergeRefs, focusableChildren } from './utils'
2
+ import { mergeRefs, focusableChildren, getChildRef } from './utils'
3
3
 
4
4
  export interface CFocusTrapProps {
5
5
  /**
@@ -146,7 +146,10 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
146
146
 
147
147
  if (elements.length === 0) {
148
148
  container.focus({ preventScroll: true })
149
- } else if (lastTabNavDirectionRef.current === 'backward') {
149
+ return
150
+ }
151
+
152
+ if (lastTabNavDirectionRef.current === 'backward') {
150
153
  elements.at(-1)?.focus({ preventScroll: true })
151
154
  } else {
152
155
  elements[0].focus({ preventScroll: true })
@@ -161,12 +164,8 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
161
164
  tabEventSourceRef.current = container
162
165
  lastTabNavDirectionRef.current = event.shiftKey ? 'backward' : 'forward'
163
166
 
164
- if (!_additionalContainer) {
165
- return
166
- }
167
-
168
167
  const containerElements = focusableChildren(container)
169
- const additionalElements = focusableChildren(_additionalContainer)
168
+ const additionalElements = _additionalContainer ? focusableChildren(_additionalContainer) : []
170
169
 
171
170
  if (containerElements.length === 0 && additionalElements.length === 0) {
172
171
  // No focusable elements, prevent tab
@@ -174,7 +173,28 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
174
173
  return
175
174
  }
176
175
 
176
+ const focusableElements = [...containerElements, ...additionalElements]
177
+
178
+ const firstFocusableElement = focusableElements[0] as HTMLElement
179
+ const lastFocusableElement = focusableElements.at(-1) as HTMLElement
177
180
  const activeElement = document.activeElement as HTMLElement
181
+
182
+ if (event.shiftKey && activeElement === firstFocusableElement) {
183
+ event.preventDefault()
184
+ lastFocusableElement.focus()
185
+ return
186
+ }
187
+
188
+ if (!event.shiftKey && activeElement === lastFocusableElement) {
189
+ event.preventDefault()
190
+ firstFocusableElement.focus()
191
+ return
192
+ }
193
+
194
+ if (!_additionalContainer) {
195
+ return
196
+ }
197
+
178
198
  const isInContainer = containerElements.includes(activeElement)
179
199
  const isInAdditional = additionalElements.includes(activeElement)
180
200
 
@@ -245,7 +265,12 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
245
265
 
246
266
  // Attach our ref to the ONLY child — no extra wrappers
247
267
  const onlyChild = React.Children.only(children)
248
- const childRef = (onlyChild as React.ReactElement & { ref?: React.Ref<HTMLElement> }).ref
268
+
269
+ // Handle different ref access patterns between React versions
270
+ // React 19+: ref is accessed via element.props.ref
271
+ // React 18 and earlier: ref is accessed via element.ref
272
+ const childRef: React.Ref<HTMLElement> | undefined = getChildRef(onlyChild)
273
+
249
274
  const mergedRef = mergeRefs(childRef, (node: HTMLElement | null) => {
250
275
  containerRef.current = node
251
276
  })
@@ -23,6 +23,30 @@ export const focusableChildren = (element: HTMLElement): HTMLElement[] => {
23
23
  return elements.filter((el) => !isDisabled(el) && isVisible(el))
24
24
  }
25
25
 
26
+ /**
27
+ * Extracts the ref from a React element, handling version differences between React 18 and 19+.
28
+ *
29
+ * In React 18 and earlier, refs are stored directly on the element object.
30
+ * In React 19+, refs are stored in the element's props object due to changes in React's internals.
31
+ * This function automatically detects the React version and uses the appropriate access pattern.
32
+ * @param child - The React element to extract the ref from
33
+ * @returns The ref attached to the element, or undefined if no ref is present
34
+ */
35
+ export const getChildRef = (child: React.ReactElement): React.Ref<HTMLElement> | undefined => {
36
+ const major = Number(React.version?.split?.('.')[0] ?? 18)
37
+ // React 18 stores ref directly on the element
38
+ if (major <= 18 && 'ref' in child && child.ref !== undefined) {
39
+ return (child as React.ReactElement & { ref?: React.Ref<HTMLElement> }).ref
40
+ }
41
+
42
+ // React 19 stores ref in props
43
+ if (child.props && typeof child.props === 'object' && 'ref' in child.props) {
44
+ return (child.props as { ref?: React.Ref<HTMLElement> }).ref
45
+ }
46
+
47
+ return undefined
48
+ }
49
+
26
50
  /**
27
51
  * Checks if an element is disabled.
28
52
  * Considers various ways an element can be disabled including CSS classes and attributes.
@@ -32,6 +32,7 @@ export * from './multi-select'
32
32
  export * from './nav'
33
33
  export * from './navbar'
34
34
  export * from './offcanvas'
35
+ export * from './one-time-password-input'
35
36
  export * from './password-input'
36
37
  export * from './pagination'
37
38
  export * from './placeholder'
@@ -120,7 +120,6 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
120
120
  ref
121
121
  ) => {
122
122
  const modalRef = useRef<HTMLDivElement>(null)
123
- const modalContentRef = useRef<HTMLDivElement>(null)
124
123
  const forkedRef = useForkedRef(ref, modalRef)
125
124
 
126
125
  const [_visible, setVisible] = useState(visible)
@@ -213,37 +212,37 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
213
212
  {(state) => (
214
213
  <CConditionalPortal container={container} portal={portal}>
215
214
  <CModalContext.Provider value={contextValues}>
216
- <div
217
- className={classNames(
218
- 'modal',
219
- {
220
- 'modal-static': staticBackdrop,
221
- fade: transition,
222
- show: state === 'entered',
223
- },
224
- className
225
- )}
226
- tabIndex={-1}
227
- {...(_visible
228
- ? { 'aria-modal': true, role: 'dialog' }
229
- : { 'aria-hidden': 'true' })}
230
- style={{
231
- ...(state !== 'exited' && { display: 'block' }),
232
- }}
233
- {...rest}
234
- ref={forkedRef}
235
- >
236
- <CModalDialog
237
- alignment={alignment}
238
- fullscreen={fullscreen}
239
- scrollable={scrollable}
240
- size={size}
215
+ <CFocusTrap active={focus && state === 'entered'}>
216
+ <div
217
+ className={classNames(
218
+ 'modal',
219
+ {
220
+ 'modal-static': staticBackdrop,
221
+ fade: transition,
222
+ show: state === 'entered',
223
+ },
224
+ className
225
+ )}
226
+ tabIndex={-1}
227
+ {...(_visible
228
+ ? { 'aria-modal': true, role: 'dialog' }
229
+ : { 'aria-hidden': 'true' })}
230
+ style={{
231
+ ...(state !== 'exited' && { display: 'block' }),
232
+ }}
233
+ {...rest}
234
+ ref={forkedRef}
241
235
  >
242
- <CFocusTrap active={focus && state === 'entered'} restoreFocus>
243
- <CModalContent ref={modalContentRef}>{children}</CModalContent>
244
- </CFocusTrap>
245
- </CModalDialog>
246
- </div>
236
+ <CModalDialog
237
+ alignment={alignment}
238
+ fullscreen={fullscreen}
239
+ scrollable={scrollable}
240
+ size={size}
241
+ >
242
+ <CModalContent>{children}</CModalContent>
243
+ </CModalDialog>
244
+ </div>
245
+ </CFocusTrap>
247
246
  </CModalContext.Provider>
248
247
  </CConditionalPortal>
249
248
  )}