@elliemae/ds-controlled-form 2.3.2-next.0 → 2.4.0-rc.0
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.
- package/cjs/autocomplete/parts/controls/useKeyboardNavigation.js +93 -0
- package/cjs/autocomplete/parts/controls/useMaskedOnChange.js +28 -0
- package/cjs/autocomplete/react-desc-prop-types.js +19 -0
- package/cjs/autocomplete/sharedTypes.js +2 -0
- package/cjs/combobox/config/useComboBox.js +30 -6
- package/cjs/combobox/config/useCorrectOptions.js +1 -1
- package/cjs/combobox/parts/container/Container.js +25 -33
- package/cjs/combobox/parts/controls/Controls.js +4 -16
- package/cjs/combobox/parts/controls-input/useControlsInput.js +1 -3
- package/cjs/combobox/parts/controls-input/useKeyboardNavigation.js +36 -30
- package/cjs/combobox/parts/header-list/HeaderList.js +3 -4
- package/cjs/combobox/parts/header-list/useHeaderListHandlers.js +18 -9
- package/cjs/combobox/parts/menu-list/MenuList.js +0 -2
- package/cjs/combobox/parts/menu-list/useItemRenderer.js +9 -14
- package/cjs/combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer.js +6 -17
- package/cjs/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.js +42 -0
- package/cjs/combobox/react-desc-prop-types.js +0 -2
- package/cjs/combobox/sharedTypes.js +3 -1
- package/cjs/combobox/utils/listHelper.js +47 -0
- package/cjs/mask-hook/hooks/useNumberMask.js +21 -17
- package/cjs/mask-hook/hooks/usePhoneMask.js +1 -1
- package/esm/autocomplete/parts/controls/useKeyboardNavigation.js +89 -0
- package/esm/autocomplete/parts/controls/useMaskedOnChange.js +24 -0
- package/esm/autocomplete/react-desc-prop-types.js +14 -0
- package/esm/autocomplete/sharedTypes.js +1 -0
- package/esm/combobox/config/useComboBox.js +31 -7
- package/esm/combobox/config/useCorrectOptions.js +1 -1
- package/esm/combobox/parts/container/Container.js +26 -34
- package/esm/combobox/parts/controls/Controls.js +4 -16
- package/esm/combobox/parts/controls-input/useControlsInput.js +1 -3
- package/esm/combobox/parts/controls-input/useKeyboardNavigation.js +38 -32
- package/esm/combobox/parts/header-list/HeaderList.js +3 -4
- package/esm/combobox/parts/header-list/useHeaderListHandlers.js +19 -10
- package/esm/combobox/parts/menu-list/MenuList.js +0 -2
- package/esm/combobox/parts/menu-list/useItemRenderer.js +10 -15
- package/esm/combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer.js +6 -17
- package/esm/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.js +34 -0
- package/esm/combobox/react-desc-prop-types.js +0 -2
- package/esm/combobox/sharedTypes.js +3 -2
- package/esm/combobox/utils/listHelper.js +45 -1
- package/esm/mask-hook/hooks/useNumberMask.js +21 -17
- package/esm/mask-hook/hooks/usePhoneMask.js +1 -1
- package/package.json +35 -23
- package/types/autocomplete/parts/controls/useKeyboardNavigation.d.ts +4 -0
- package/types/autocomplete/parts/controls/useMaskedOnChange.d.ts +2 -0
- package/types/autocomplete/react-desc-prop-types.d.ts +54 -0
- package/types/autocomplete/sharedTypes.d.ts +20 -0
- package/types/combobox/parts/header-list/useHeaderListHandlers.d.ts +1 -1
- package/types/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.d.ts +5 -0
- package/types/combobox/react-desc-prop-types.d.ts +3 -4
- package/types/combobox/sharedTypes.d.ts +1 -0
- package/types/combobox/tests/{combobox-creatable.test.d.ts → creatable.test.d.ts} +0 -0
- package/types/combobox/tests/{combobox-disable.test.d.ts → disabled.test.d.ts} +0 -0
- package/types/combobox/tests/{combobox-general.test.d.ts → general.test.d.ts} +0 -0
- package/types/combobox/tests/{combobox-multi-select.test.d.ts → multi-select.test.d.ts} +0 -0
- package/types/combobox/tests/{combobox-onlyselectable.test.d.ts → onlyselectable.test.d.ts} +0 -0
- package/types/combobox/tests/{combobox-single-select.test.d.ts → single-select.test.d.ts} +0 -0
- package/types/combobox/tests/{combobox-usemask.test.d.ts → usemask.test.d.ts} +0 -0
- package/types/combobox/utils/listHelper.d.ts +3 -0
- package/cjs/combobox/utils/hooks/useKeyboardNavigation.js +0 -168
- package/cjs/combobox/utils/hooks/useOnElementResize.js +0 -37
- package/esm/combobox/utils/hooks/useKeyboardNavigation.js +0 -164
- package/esm/combobox/utils/hooks/useOnElementResize.js +0 -33
- package/types/combobox/utils/hooks/useKeyboardNavigation.d.ts +0 -4
- package/types/combobox/utils/hooks/useOnElementResize.d.ts +0 -3
|
@@ -10,6 +10,7 @@ var dsPills = require('@elliemae/ds-pills');
|
|
|
10
10
|
var ComboBoxCTX = require('../../ComboBoxCTX.js');
|
|
11
11
|
var useGroupPills = require('./useGroupPills.js');
|
|
12
12
|
var ComboboxDataTestids = require('../../ComboboxDataTestids.js');
|
|
13
|
+
var RemovableSelectedValuePill = require('./RemovableSelectedValuePill.js');
|
|
13
14
|
|
|
14
15
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
15
16
|
|
|
@@ -19,9 +20,7 @@ const MultiSelectedValuesContainer = () => {
|
|
|
19
20
|
const {
|
|
20
21
|
props: {
|
|
21
22
|
selectedValues,
|
|
22
|
-
disabled
|
|
23
|
-
onChange,
|
|
24
|
-
innerRef
|
|
23
|
+
disabled
|
|
25
24
|
},
|
|
26
25
|
pillGroupRef
|
|
27
26
|
} = React.useContext(ComboBoxCTX.ComboBoxContext); // this is always an array because logic in controls component
|
|
@@ -35,25 +34,15 @@ const MultiSelectedValuesContainer = () => {
|
|
|
35
34
|
"data-testid": ComboboxDataTestids.ComboboxDataTestid.SELECTED_VALUES
|
|
36
35
|
}, void 0, /*#__PURE__*/_jsx__default["default"](dsPills.DSPillGroupV2, {
|
|
37
36
|
innerRef: pillGroupRef
|
|
38
|
-
}, void 0, multiSelectedValue.length > 0 && multiSelectedValue.slice(0, correctPillsToShow).map(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
disabled: disabled,
|
|
42
|
-
type: disabled ? 'value' : 'removable',
|
|
43
|
-
tabIndex: -1,
|
|
44
|
-
onRemove: () => {
|
|
45
|
-
var _innerRef$current;
|
|
46
|
-
|
|
47
|
-
onChange(option);
|
|
48
|
-
(_innerRef$current = innerRef.current) === null || _innerRef$current === void 0 ? void 0 : _innerRef$current.focus();
|
|
49
|
-
}
|
|
50
|
-
}, option.value)), multiSelectedValue.length > 0 && correctPillsToShow !== multiSelectedValue.length && /*#__PURE__*/_jsx__default["default"](dsPills.DSPillV2, {
|
|
37
|
+
}, void 0, multiSelectedValue.length > 0 && multiSelectedValue.slice(0, correctPillsToShow).map(pill => /*#__PURE__*/_jsx__default["default"](RemovableSelectedValuePill.RemovableSelectedValuePill, {
|
|
38
|
+
pill: pill
|
|
39
|
+
}, pill.dsId)), multiSelectedValue.length > 0 && correctPillsToShow !== multiSelectedValue.length && /*#__PURE__*/_jsx__default["default"](dsPills.DSPillV2, {
|
|
51
40
|
size: "s",
|
|
52
41
|
labelTruncated: false,
|
|
53
42
|
label: "+".concat(multiSelectedValue.length - correctPillsToShow),
|
|
54
43
|
type: "value",
|
|
55
44
|
disabled: disabled
|
|
56
|
-
}, "grouped"))), [pillGroupRef, multiSelectedValue, correctPillsToShow, disabled
|
|
45
|
+
}, "grouped"))), [pillGroupRef, multiSelectedValue, correctPillsToShow, disabled]);
|
|
57
46
|
};
|
|
58
47
|
|
|
59
48
|
exports.MultiSelectedValuesContainer = MultiSelectedValuesContainer;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _jsx = require('@babel/runtime/helpers/jsx');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var dsPills = require('@elliemae/ds-pills');
|
|
8
|
+
var ComboBoxCTX = require('../../ComboBoxCTX.js');
|
|
9
|
+
var listHelper = require('../../utils/listHelper.js');
|
|
10
|
+
|
|
11
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
var _jsx__default = /*#__PURE__*/_interopDefaultLegacy(_jsx);
|
|
14
|
+
|
|
15
|
+
const RemovableSelectedValuePill = props => {
|
|
16
|
+
const {
|
|
17
|
+
pill
|
|
18
|
+
} = props;
|
|
19
|
+
const {
|
|
20
|
+
props: {
|
|
21
|
+
onChange,
|
|
22
|
+
disabled,
|
|
23
|
+
selectedValues,
|
|
24
|
+
innerRef
|
|
25
|
+
}
|
|
26
|
+
} = React.useContext(ComboBoxCTX.ComboBoxContext);
|
|
27
|
+
return /*#__PURE__*/_jsx__default["default"](dsPills.DSPillV2, {
|
|
28
|
+
size: "s",
|
|
29
|
+
label: pill.label,
|
|
30
|
+
disabled: disabled,
|
|
31
|
+
type: disabled ? 'value' : 'removable',
|
|
32
|
+
tabIndex: -1,
|
|
33
|
+
onRemove: e => {
|
|
34
|
+
var _innerRef$current;
|
|
35
|
+
|
|
36
|
+
onChange(listHelper.getSuggestedValueOnChange(pill, selectedValues), pill, e);
|
|
37
|
+
(_innerRef$current = innerRef.current) === null || _innerRef$current === void 0 ? void 0 : _innerRef$current.focus();
|
|
38
|
+
}
|
|
39
|
+
}, pill.value);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
exports.RemovableSelectedValuePill = RemovableSelectedValuePill;
|
|
@@ -26,10 +26,8 @@ const ComboboxPropTypes = _objectSpread(_objectSpread({}, dsPropsHelpers.globalA
|
|
|
26
26
|
autoFocus: dsPropsHelpers.PropTypes.bool.description('Whether the combo box uses auto focus or not').defaultValue('false'),
|
|
27
27
|
hasError: dsPropsHelpers.PropTypes.bool.description('Whether the combo box has error or not').defaultValue('false'),
|
|
28
28
|
onChange: dsPropsHelpers.PropTypes.func.isRequired.description('function triggered when an option is selected it will send the options selected').defaultValue('() => {}'),
|
|
29
|
-
onSelectAll: dsPropsHelpers.PropTypes.func.description('function triggered when ALL button is selected from the header menu list').defaultValue('() => {}'),
|
|
30
29
|
onFilter: dsPropsHelpers.PropTypes.func.description('function triggered when user type in the combobox input').defaultValue(''),
|
|
31
30
|
onCreate: dsPropsHelpers.PropTypes.func.description('function triggered when user select "create" a new item whenever is not a match in the all option list').defaultValue(''),
|
|
32
|
-
onClearAll: dsPropsHelpers.PropTypes.func.description('function triggered when user select CLEAR button from the header menu list').defaultValue(''),
|
|
33
31
|
onCancel: dsPropsHelpers.PropTypes.func.description('function triggered when user press ESC key').defaultValue('() => {}'),
|
|
34
32
|
onMenuOpen: dsPropsHelpers.PropTypes.func.description('function triggered when user open the menu list').defaultValue('() => {}'),
|
|
35
33
|
onMenuClose: dsPropsHelpers.PropTypes.func.description('function triggered when user close the menu list').defaultValue('() => {}'),
|
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var constants = require('./constants.js');
|
|
6
6
|
|
|
7
|
-
const isSeparator = el => (el === null || el === void 0 ? void 0 : el.type) === constants.MENU_OPTION_TYPES.
|
|
7
|
+
const isSeparator = el => (el === null || el === void 0 ? void 0 : el.type) === constants.MENU_OPTION_TYPES.SEPARATOR;
|
|
8
|
+
const isOption = el => (el === null || el === void 0 ? void 0 : el.type) === constants.MENU_OPTION_TYPES.OPTION;
|
|
8
9
|
|
|
10
|
+
exports.isOption = isOption;
|
|
9
11
|
exports.isSeparator = isSeparator;
|
|
@@ -7,6 +7,9 @@ require('core-js/modules/esnext.iterator.constructor.js');
|
|
|
7
7
|
require('core-js/modules/esnext.iterator.filter.js');
|
|
8
8
|
require('core-js/modules/esnext.async-iterator.some.js');
|
|
9
9
|
require('core-js/modules/esnext.iterator.some.js');
|
|
10
|
+
require('core-js/modules/esnext.async-iterator.find.js');
|
|
11
|
+
require('core-js/modules/esnext.iterator.find.js');
|
|
12
|
+
require('core-js/modules/web.dom-collections.iterator.js');
|
|
10
13
|
var constants = require('../constants.js');
|
|
11
14
|
|
|
12
15
|
const getSelectableOptions = options => options.filter(option => option.type === constants.MENU_OPTION_TYPES.OPTION && !option.disabled);
|
|
@@ -31,10 +34,54 @@ const findInCircularList = function (list, from, criteria) {
|
|
|
31
34
|
|
|
32
35
|
return from; // return same item
|
|
33
36
|
};
|
|
37
|
+
const getFirstOption = (options, selectedValues, inputValue) => {
|
|
38
|
+
if (!inputValue) {
|
|
39
|
+
if (Array.isArray(selectedValues) && selectedValues.length > 0) {
|
|
40
|
+
return selectedValues[selectedValues.length - 1].dsId;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (selectedValues !== null && selectedValues !== void 0 && selectedValues.dsId) {
|
|
44
|
+
return selectedValues.dsId;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < options.length; i += 1) if (!['section', 'separator'].includes(options[i].type) && !options[i].disabled) {
|
|
49
|
+
return options[i].dsId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return '';
|
|
53
|
+
};
|
|
54
|
+
const getSuggestedValueOnChange = (selectedOption, selectedValues) => {
|
|
55
|
+
if (Array.isArray(selectedValues)) {
|
|
56
|
+
const isFound = selectedValues.find(item => item.type === 'option' && item.value === selectedOption.value);
|
|
57
|
+
|
|
58
|
+
if (isFound) {
|
|
59
|
+
return selectedValues.filter(item => item.type === 'option' && item.value !== selectedOption.value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [...selectedValues, selectedOption];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (selectedValues && selectedValues.dsId === selectedOption.dsId) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return selectedOption;
|
|
70
|
+
};
|
|
71
|
+
const getLastValueSelected = selectedValues => {
|
|
72
|
+
if (Array.isArray(selectedValues)) {
|
|
73
|
+
return selectedValues[selectedValues.length - 1];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return selectedValues;
|
|
77
|
+
};
|
|
34
78
|
|
|
35
79
|
exports.findInCircularList = findInCircularList;
|
|
80
|
+
exports.getFirstOption = getFirstOption;
|
|
81
|
+
exports.getLastValueSelected = getLastValueSelected;
|
|
36
82
|
exports.getOptions = getOptions;
|
|
37
83
|
exports.getSelectableOptions = getSelectableOptions;
|
|
84
|
+
exports.getSuggestedValueOnChange = getSuggestedValueOnChange;
|
|
38
85
|
exports.isSelected = isSelected;
|
|
39
86
|
exports.isSelectedValueEmpty = isSelectedValueEmpty;
|
|
40
87
|
exports.isSelectedValueMultiple = isSelectedValueMultiple;
|
|
@@ -34,24 +34,16 @@ const conformValue = (rawValue, cursorPos, lastKeyCode, opts) => {
|
|
|
34
34
|
decimalRequired
|
|
35
35
|
} = opts;
|
|
36
36
|
const THOUSANDSSEPARATOR = ',';
|
|
37
|
-
let dotIdx = -1;
|
|
38
|
-
let isNegative = false;
|
|
39
|
-
let finalPrefix = ''; // Work with digits and dots only
|
|
37
|
+
let dotIdx = -1; // allow minus sign before and after the preffix
|
|
40
38
|
|
|
39
|
+
let finalPrefix = [prefix.length, 0].includes(rawValue.indexOf('-')) && allowNegative ? '-' : '';
|
|
41
40
|
let maskedValue = rawValue.split('').filter((char, idx) => {
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
// we take only the first dot
|
|
42
|
+
const validChar = char >= '0' && char <= '9' || char === '.' && dotIdx === -1 && decimalPlaces;
|
|
44
43
|
if (char === '.' && validChar) dotIdx = idx;
|
|
45
|
-
|
|
46
|
-
if (char === '-' && validChar) {
|
|
47
|
-
isNegative = true;
|
|
48
|
-
finalPrefix += '-';
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
44
|
return validChar;
|
|
53
45
|
});
|
|
54
|
-
if (prefix) finalPrefix += prefix; // REMOVE FOLLOWING ZEROS IN INTEGER PART AFTER LEADER ZERO NUMBER
|
|
46
|
+
if (prefix && maskedValue.length) finalPrefix += prefix; // REMOVE FOLLOWING ZEROS IN INTEGER PART AFTER LEADER ZERO NUMBER
|
|
55
47
|
|
|
56
48
|
while (maskedValue.length >= 2 && maskedValue[0] === '0' && maskedValue[1] !== '.') {
|
|
57
49
|
maskedValue = maskedValue.splice(1);
|
|
@@ -67,18 +59,26 @@ const conformValue = (rawValue, cursorPos, lastKeyCode, opts) => {
|
|
|
67
59
|
|
|
68
60
|
|
|
69
61
|
if (dotIdx !== -1 && !(lastKeyCode === 'Backspace' && decimal.length === 1)) {
|
|
70
|
-
|
|
62
|
+
if (integer.length) {
|
|
63
|
+
maskedValue = [...integer, ...decimal];
|
|
64
|
+
} else {
|
|
65
|
+
// if theres no integer add a zero to the left of the dot to cover .23 => 0.23 case
|
|
66
|
+
maskedValue = ['0', ...decimal];
|
|
67
|
+
}
|
|
71
68
|
} else {
|
|
72
69
|
// If decimal is required we add a dot after integer if exists
|
|
73
70
|
maskedValue = decimalRequired && integer.length ? [...integer, '.'] : integer;
|
|
74
|
-
}
|
|
71
|
+
} // remove orphans prefix for example "-$"
|
|
72
|
+
|
|
75
73
|
|
|
76
74
|
if (lastKeyCode === 'Backspace' && maskedValue.length === 0) {
|
|
77
75
|
finalPrefix = '';
|
|
78
|
-
} // SET REAL CURSOR POSITION AFTER ADDING
|
|
76
|
+
} // SET REAL CURSOR POSITION AFTER ADDING DOT and COMMAS SEPARATORS
|
|
77
|
+
|
|
79
78
|
|
|
79
|
+
let maskedPos = 0; // 0 leading case we move the cursor to continue typing
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
if (maskedValue[maskedPos] === '0' && cursorPos === 1 + prefix.length) maskedPos += 1;
|
|
82
82
|
|
|
83
83
|
for (let i = 0; i < cursorPos; i += 1) {
|
|
84
84
|
if (maskedValue[maskedPos] === THOUSANDSSEPARATOR) maskedPos += 1;
|
|
@@ -114,6 +114,7 @@ const useNumberMask = props => {
|
|
|
114
114
|
const onBlur = React.useCallback(e => {
|
|
115
115
|
const maskedValue = e.target.value;
|
|
116
116
|
const dotIdx = maskedValue.indexOf('.');
|
|
117
|
+
const minusIndex = maskedValue.indexOf('-');
|
|
117
118
|
const {
|
|
118
119
|
decimalRequired,
|
|
119
120
|
decimalPlaces,
|
|
@@ -134,6 +135,9 @@ const useNumberMask = props => {
|
|
|
134
135
|
} else if (maskedValue.length - suffix.length - 1 === dotIdx) {
|
|
135
136
|
// removing orphans dots
|
|
136
137
|
valueSetter(maskedValue.replace('.', ''));
|
|
138
|
+
} else if (maskedValue.length - 1 === minusIndex) {
|
|
139
|
+
// removing orphans minus
|
|
140
|
+
valueSetter(maskedValue.replace('-', ''));
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
if (userOnBlur) userOnBlur(e);
|
|
@@ -21,7 +21,7 @@ var _objectWithoutProperties__default = /*#__PURE__*/_interopDefaultLegacy(_obje
|
|
|
21
21
|
|
|
22
22
|
const _excluded = ["valueSetter", "onChange", "onKeyDown", "onBlur"];
|
|
23
23
|
const phoneSpecialChars = [['(', 0], [') ', 4], ['-', 9]];
|
|
24
|
-
const internationalPhoneSpecialChars = [['+1 (', 0], [') ', 7], ['-',
|
|
24
|
+
const internationalPhoneSpecialChars = [['+1 (', 0], [') ', 7], ['-', 12]];
|
|
25
25
|
|
|
26
26
|
const conformValue = (rawValue, cursorPos, lastKeyCode, opts) => {
|
|
27
27
|
const characters = rawValue.split(''); // Work with digits only
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import 'core-js/modules/esnext.async-iterator.find.js';
|
|
2
|
+
import 'core-js/modules/esnext.iterator.constructor.js';
|
|
3
|
+
import 'core-js/modules/esnext.iterator.find.js';
|
|
4
|
+
import { useContext, useCallback } from 'react';
|
|
5
|
+
import { AutocompleteContext } from '../../AutocompleteCTX.js';
|
|
6
|
+
import { findInCircularList } from '../../utils/listHelper.js';
|
|
7
|
+
|
|
8
|
+
const isOptionFocuseable = opt => !['section', 'separator'].includes(opt.type);
|
|
9
|
+
|
|
10
|
+
const useKeyboardNavigation = () => {
|
|
11
|
+
const {
|
|
12
|
+
props: {
|
|
13
|
+
onValueChange,
|
|
14
|
+
options
|
|
15
|
+
},
|
|
16
|
+
setShowPopover,
|
|
17
|
+
showPopover,
|
|
18
|
+
focusOptionIdx,
|
|
19
|
+
setCurrentOption,
|
|
20
|
+
scrollOptionIntoView
|
|
21
|
+
} = useContext(AutocompleteContext);
|
|
22
|
+
const currentItemIndex = options.findIndex(opt => opt.dsId === focusOptionIdx);
|
|
23
|
+
const currentItem = options.find(item => item.type === 'option' && item.dsId === focusOptionIdx);
|
|
24
|
+
const selectOption = useCallback(e => {
|
|
25
|
+
if (onValueChange) onValueChange(currentItem.label, e);
|
|
26
|
+
setShowPopover(false);
|
|
27
|
+
}, [currentItem, setShowPopover, onValueChange]);
|
|
28
|
+
const onInputKeyDown = useCallback(e => {
|
|
29
|
+
const targetElement = e.target;
|
|
30
|
+
|
|
31
|
+
if (['ArrowDown', 'ArrowUp', 'Enter'].includes(e.key) || e.keyCode >= 48 && e.keyCode <= 90) {
|
|
32
|
+
setShowPopover(true);
|
|
33
|
+
} // =============================================================================
|
|
34
|
+
// ENTER AND SPACE KEY SELECT OPTIONS AND OPEN CLOSE THE POPOVER
|
|
35
|
+
// =============================================================================
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if (e.keyCode === 32 && targetElement.value === '' || e.key === 'Enter') {
|
|
39
|
+
if (showPopover) {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
selectOption(e);
|
|
42
|
+
}
|
|
43
|
+
} // =============================================================================
|
|
44
|
+
// ARROWS UP AND DOWN
|
|
45
|
+
// =============================================================================
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if (e.key === 'ArrowDown') {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
|
|
51
|
+
if (showPopover) {
|
|
52
|
+
const nextItemIndex = findInCircularList(options, currentItemIndex, isOptionFocuseable);
|
|
53
|
+
setCurrentOption(options[nextItemIndex].dsId);
|
|
54
|
+
scrollOptionIntoView(options[nextItemIndex].dsId);
|
|
55
|
+
} else {
|
|
56
|
+
setShowPopover(true);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (e.key === 'ArrowUp') {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
|
|
63
|
+
if (showPopover) {
|
|
64
|
+
const nextItemIndex = findInCircularList(options, currentItemIndex, isOptionFocuseable, -1);
|
|
65
|
+
setCurrentOption(options[nextItemIndex].dsId);
|
|
66
|
+
scrollOptionIntoView(options[nextItemIndex].dsId);
|
|
67
|
+
} else {
|
|
68
|
+
setShowPopover(true);
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
const nextItemIndex = findInCircularList(options, 0, isOptionFocuseable, -1);
|
|
71
|
+
setCurrentOption(options[nextItemIndex].dsId);
|
|
72
|
+
scrollOptionIntoView(options[nextItemIndex].dsId);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
} // =============================================================================
|
|
76
|
+
// ESCAPE
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if (e.key === 'Escape') {
|
|
81
|
+
setShowPopover(false);
|
|
82
|
+
}
|
|
83
|
+
}, [scrollOptionIntoView, currentItemIndex, options, showPopover, setCurrentOption, setShowPopover, selectOption]);
|
|
84
|
+
return {
|
|
85
|
+
onInputKeyDown
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export { useKeyboardNavigation };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useContext, useCallback } from 'react';
|
|
2
|
+
import { AutocompleteContext } from '../../AutocompleteCTX.js';
|
|
3
|
+
|
|
4
|
+
const useMaskedOnChange = () => {
|
|
5
|
+
const {
|
|
6
|
+
props: {
|
|
7
|
+
useMask,
|
|
8
|
+
onChange
|
|
9
|
+
},
|
|
10
|
+
setShowPopover
|
|
11
|
+
} = useContext(AutocompleteContext); // default onchange when no mask provided
|
|
12
|
+
|
|
13
|
+
const handleOnChange = useCallback(e => {
|
|
14
|
+
setShowPopover(true);
|
|
15
|
+
onChange(e.target.value);
|
|
16
|
+
}, [onChange, setShowPopover]);
|
|
17
|
+
const onChangeMask = useMask({
|
|
18
|
+
valueSetter: setInputValue,
|
|
19
|
+
onChange: handleOnChange
|
|
20
|
+
});
|
|
21
|
+
return (onChangeMask === null || onChangeMask === void 0 ? void 0 : onChangeMask.onChange) || handleOnChange;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { useMaskedOnChange };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PropTypes } from '@elliemae/ds-props-helpers';
|
|
2
|
+
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4
|
+
function noop() {}
|
|
5
|
+
const propTypes = {
|
|
6
|
+
placeholder: PropTypes.string.description('input s placeholder value').defaultValue(''),
|
|
7
|
+
options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).description('List of options').defaultValue([]),
|
|
8
|
+
value: PropTypes.oneOfType([PropTypes.string]).description('Array of option or single option selected'),
|
|
9
|
+
autoFocus: PropTypes.bool.description('Whether the combo box uses auto focus or not').defaultValue('false'),
|
|
10
|
+
hasError: PropTypes.bool.description('Whether the combo box has error or not').defaultValue('false'),
|
|
11
|
+
onChange: PropTypes.func.description('function triggered when an option is selected it will send the options selected').defaultValue('')
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { noop, propTypes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -5,12 +5,13 @@ import 'core-js/modules/esnext.async-iterator.for-each.js';
|
|
|
5
5
|
import 'core-js/modules/esnext.iterator.for-each.js';
|
|
6
6
|
import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
|
|
7
7
|
import 'core-js/modules/web.dom-collections.iterator.js';
|
|
8
|
-
import { useRef, useState, useCallback, useMemo } from 'react';
|
|
8
|
+
import { useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
9
9
|
import { useVirtual } from 'react-virtual';
|
|
10
10
|
import { useMemoMergePropsWithDefault, useValidateTypescriptPropTypes } from '@elliemae/ds-props-helpers';
|
|
11
11
|
import { defaultProps } from '../ComboBoxCTX.js';
|
|
12
12
|
import { ComboboxPropTypes } from '../react-desc-prop-types.js';
|
|
13
13
|
import { useCorrectOptions } from './useCorrectOptions.js';
|
|
14
|
+
import { getFirstOption } from '../utils/listHelper.js';
|
|
14
15
|
|
|
15
16
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
16
17
|
|
|
@@ -27,15 +28,17 @@ const useComboBox = props => {
|
|
|
27
28
|
const [showSelectedOptions, setShowSelectedOptions] = useState(false);
|
|
28
29
|
const [inputValue, setInputValue] = useState('');
|
|
29
30
|
const [hasFocus, setHasFocus] = useState(false);
|
|
30
|
-
const [focusOptionIdx, setFocusOptionIdx] = useState('');
|
|
31
31
|
const listRef = useRef(null);
|
|
32
32
|
const wrapperListRef = useRef(null);
|
|
33
33
|
const selectedOptionsRef = useRef(null);
|
|
34
34
|
const controlsWrapperRef = useRef(null);
|
|
35
35
|
const selectAllCheckboxRef = useRef(null);
|
|
36
36
|
const toggleSelectionButtonRef = useRef(null);
|
|
37
|
-
const pillGroupRef = useRef(null);
|
|
38
|
-
|
|
37
|
+
const pillGroupRef = useRef(null);
|
|
38
|
+
const {
|
|
39
|
+
selectedValues
|
|
40
|
+
} = propsWithDefaults; // ---------------------------------------------------------------------------
|
|
41
|
+
// Menu Options with/out selected filter and creatable functionality
|
|
39
42
|
// ---------------------------------------------------------------------------
|
|
40
43
|
|
|
41
44
|
const correctOptions = useCorrectOptions(propsWithDefaults, inputValue, showSelectedOptions); // ===========================================================================
|
|
@@ -51,9 +54,30 @@ const useComboBox = props => {
|
|
|
51
54
|
// Scroll into view function
|
|
52
55
|
// ===========================================================================
|
|
53
56
|
|
|
54
|
-
const scrollOptionIntoView = useCallback(dsId
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
const scrollOptionIntoView = useCallback(function (dsId) {
|
|
58
|
+
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
|
59
|
+
align: 'center'
|
|
60
|
+
};
|
|
61
|
+
virtualListHelpers.scrollToIndex(correctOptions.findIndex(opt => opt.dsId === dsId), // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
62
|
+
opts);
|
|
63
|
+
}, [correctOptions, virtualListHelpers]); // ===========================================================================
|
|
64
|
+
// Init focused option when opening the menu list
|
|
65
|
+
// ===========================================================================
|
|
66
|
+
|
|
67
|
+
const [focusOptionIdx, setFocusOptionIdx] = useState('');
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
// we are updating the focus option only when we have focus on the input and
|
|
70
|
+
// we are in the selected option view or we are typing to force first option always
|
|
71
|
+
if (hasFocus && (focusOptionIdx === '' || inputValue !== '' || showSelectedOptions)) {
|
|
72
|
+
const focusedValue = getFirstOption(correctOptions, selectedValues, inputValue);
|
|
73
|
+
setFocusOptionIdx(focusedValue);
|
|
74
|
+
scrollOptionIntoView(focusedValue, {
|
|
75
|
+
align: 'center'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!hasFocus) setFocusOptionIdx(''); // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
80
|
+
}, [showPopover, hasFocus, correctOptions, selectedValues, inputValue]);
|
|
57
81
|
const ctx = useMemo(() => ({
|
|
58
82
|
props: _objectSpread(_objectSpread({}, propsWithDefaults), {}, {
|
|
59
83
|
options: correctOptions
|
|
@@ -49,7 +49,7 @@ const useCorrectOptions = (propsWithDefaults, inputValue, showSelectedOptions) =
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (onCreate && inputValue && options.findIndex(option => option.type === MENU_OPTION_TYPES.OPTION && option.label === inputValue) === -1) {
|
|
52
|
-
const creatableUuid = uid(
|
|
52
|
+
const creatableUuid = uid();
|
|
53
53
|
const creatableItem = {
|
|
54
54
|
dsId: "creatable-".concat(creatableUuid),
|
|
55
55
|
label: inputValue,
|
|
@@ -14,7 +14,7 @@ import { StyledContainerInline, StyledContainerWithPopper, StyledPopperWrapper }
|
|
|
14
14
|
import { Controls } from '../controls/Controls.js';
|
|
15
15
|
import { MenuList } from '../menu-list/MenuList.js';
|
|
16
16
|
import { ComboboxDataTestid } from '../../ComboboxDataTestids.js';
|
|
17
|
-
import {
|
|
17
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
18
18
|
|
|
19
19
|
var _Controls, _MenuList, _Controls2, _MenuList2;
|
|
20
20
|
|
|
@@ -46,7 +46,7 @@ const Container = () => {
|
|
|
46
46
|
const StyledContainer = inline ? StyledContainerInline : StyledContainerWithPopper;
|
|
47
47
|
const globalAttributes = useGetGlobalAttributes(props); // Removing possible collisionable props
|
|
48
48
|
|
|
49
|
-
const
|
|
49
|
+
const globalsAttrs = _objectWithoutProperties(globalAttributes, _excluded);
|
|
50
50
|
|
|
51
51
|
const handleMouseDown = useCallback(e => {
|
|
52
52
|
e.preventDefault();
|
|
@@ -66,38 +66,30 @@ const Container = () => {
|
|
|
66
66
|
useEffect(() => {
|
|
67
67
|
if (onMenuOpen && showPopover) onMenuOpen();else if (onMenuClose && !showPopover) onMenuClose();
|
|
68
68
|
}, [showPopover, onMenuOpen, onMenuClose]);
|
|
69
|
-
return useMemo(() => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
zIndex: zIndex
|
|
94
|
-
}, void 0, _StyledPopperWrapper || (_StyledPopperWrapper = /*#__PURE__*/_jsx(StyledPopperWrapper, {
|
|
95
|
-
tabIndex: -1,
|
|
96
|
-
onMouseDown: handleMouseDown
|
|
97
|
-
}, void 0, _MenuList2 || (_MenuList2 = /*#__PURE__*/_jsx(MenuList, {})))))]
|
|
98
|
-
}))
|
|
99
|
-
});
|
|
100
|
-
}, [inline, showPopover, StyledContainer, placementOrderPreference, referenceElement, rest, startPlacementPreference, withoutPortal, zIndex, handleCloseMenu, handleMouseDown, setReferenceElement]);
|
|
69
|
+
return useMemo(() => /*#__PURE__*/jsx(StyledContainer, _objectSpread(_objectSpread({
|
|
70
|
+
"data-testid": ComboboxDataTestid.CONTAINER,
|
|
71
|
+
ref: setReferenceElement
|
|
72
|
+
}, globalsAttrs), {}, {
|
|
73
|
+
children: inline ? /*#__PURE__*/jsxs(Fragment, {
|
|
74
|
+
children: [_Controls || (_Controls = /*#__PURE__*/_jsx(Controls, {})), _MenuList || (_MenuList = /*#__PURE__*/_jsx(MenuList, {}))]
|
|
75
|
+
}) : /*#__PURE__*/jsxs(Fragment, {
|
|
76
|
+
children: [_Controls2 || (_Controls2 = /*#__PURE__*/_jsx(Controls, {})), /*#__PURE__*/_jsx(DSPopperJS, {
|
|
77
|
+
customOffset: [0, 5],
|
|
78
|
+
referenceElement: referenceElement,
|
|
79
|
+
showPopover: showPopover,
|
|
80
|
+
closeContextMenu: handleCloseMenu,
|
|
81
|
+
startPlacementPreference: startPlacementPreference,
|
|
82
|
+
placementOrderPreference: placementOrderPreference,
|
|
83
|
+
withoutPortal: withoutPortal,
|
|
84
|
+
withoutArrow: true,
|
|
85
|
+
withoutAnimation: true,
|
|
86
|
+
zIndex: zIndex
|
|
87
|
+
}, void 0, _StyledPopperWrapper || (_StyledPopperWrapper = /*#__PURE__*/_jsx(StyledPopperWrapper, {
|
|
88
|
+
tabIndex: -1,
|
|
89
|
+
onMouseDown: handleMouseDown
|
|
90
|
+
}, void 0, _MenuList2 || (_MenuList2 = /*#__PURE__*/_jsx(MenuList, {})))))]
|
|
91
|
+
})
|
|
92
|
+
})), [inline, StyledContainer, setReferenceElement, globalsAttrs, referenceElement, showPopover, handleCloseMenu, startPlacementPreference, placementOrderPreference, withoutPortal, zIndex, handleMouseDown]);
|
|
101
93
|
};
|
|
102
94
|
|
|
103
95
|
export { Container };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import _jsx from '@babel/runtime/helpers/esm/jsx';
|
|
2
2
|
import { useContext, useCallback, useMemo } from 'react';
|
|
3
|
-
import { DSPillV2 } from '@elliemae/ds-pills';
|
|
4
3
|
import { DropdownIndicator } from '../DropdownIndicator.js';
|
|
5
4
|
import { ComboboxDataTestid } from '../../ComboboxDataTestids.js';
|
|
6
5
|
import { ComboBoxContext } from '../../ComboBoxCTX.js';
|
|
@@ -10,6 +9,7 @@ import { ControlsInput } from '../controls-input/ControlsInput.js';
|
|
|
10
9
|
import { useOnPillsNavigation } from './useOnPillsNavigation.js';
|
|
11
10
|
import { A11yFocusedOption } from '../A11yFocusedOption.js';
|
|
12
11
|
import { A11ySelectedValues } from '../A11ySelectedValues.js';
|
|
12
|
+
import { RemovableSelectedValuePill } from '../multi-selected-values-container/RemovableSelectedValuePill.js';
|
|
13
13
|
import { jsxs } from 'react/jsx-runtime';
|
|
14
14
|
|
|
15
15
|
var _A11ySelectedValues, _A11yFocusedOption, _MultiSelectedValuesC, _ControlsInput, _DropdownIndicator;
|
|
@@ -17,7 +17,6 @@ const Controls = () => {
|
|
|
17
17
|
const {
|
|
18
18
|
props: {
|
|
19
19
|
inline,
|
|
20
|
-
onChange,
|
|
21
20
|
disabled,
|
|
22
21
|
inputMinWidth,
|
|
23
22
|
hasError,
|
|
@@ -83,20 +82,9 @@ const Controls = () => {
|
|
|
83
82
|
onClick: handleOnPillsClick,
|
|
84
83
|
children: [_A11ySelectedValues || (_A11ySelectedValues = /*#__PURE__*/_jsx(A11ySelectedValues, {})), _A11yFocusedOption || (_A11yFocusedOption = /*#__PURE__*/_jsx(A11yFocusedOption, {})), Array.isArray(selectedValues) ? _MultiSelectedValuesC || (_MultiSelectedValuesC = /*#__PURE__*/_jsx(MultiSelectedValuesContainer, {})) : /*#__PURE__*/_jsx("div", {
|
|
85
84
|
"data-testid": ComboboxDataTestid.SELECTED_VALUES
|
|
86
|
-
}, void 0, selectedValues && /*#__PURE__*/_jsx(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
tabIndex: -1,
|
|
90
|
-
disabled: disabled,
|
|
91
|
-
label: selectedValues.label,
|
|
92
|
-
type: disabled ? 'value' : 'removable',
|
|
93
|
-
onRemove: () => {
|
|
94
|
-
var _innerRef$current4;
|
|
95
|
-
|
|
96
|
-
onChange(null);
|
|
97
|
-
(_innerRef$current4 = innerRef.current) === null || _innerRef$current4 === void 0 ? void 0 : _innerRef$current4.focus();
|
|
98
|
-
}
|
|
99
|
-
}, selectedValues.value))]
|
|
85
|
+
}, void 0, selectedValues && /*#__PURE__*/_jsx(RemovableSelectedValuePill, {
|
|
86
|
+
pill: selectedValues
|
|
87
|
+
}))]
|
|
100
88
|
}), _ControlsInput || (_ControlsInput = /*#__PURE__*/_jsx(ControlsInput, {})), !inline && /*#__PURE__*/_jsx(StyledHeaderActionsWrapper, {
|
|
101
89
|
justifyContent: "center",
|
|
102
90
|
cols: ['auto', 'auto']
|
|
@@ -13,7 +13,6 @@ const useControlsInput = () => {
|
|
|
13
13
|
onFilter
|
|
14
14
|
},
|
|
15
15
|
setHasFocus,
|
|
16
|
-
setFocusOptionIdx,
|
|
17
16
|
inputValue,
|
|
18
17
|
setInputValue,
|
|
19
18
|
setShowSelectedOptions
|
|
@@ -34,10 +33,9 @@ const useControlsInput = () => {
|
|
|
34
33
|
const handleOnBlur = useCallback(() => {
|
|
35
34
|
setInputValue('');
|
|
36
35
|
if (onFilter) onFilter('');
|
|
37
|
-
setFocusOptionIdx('');
|
|
38
36
|
setHasFocus(false);
|
|
39
37
|
setShowSelectedOptions(false);
|
|
40
|
-
}, [setInputValue, setShowSelectedOptions, onFilter,
|
|
38
|
+
}, [setInputValue, setShowSelectedOptions, onFilter, setHasFocus]);
|
|
41
39
|
const handleOnFocus = useCallback(() => {
|
|
42
40
|
setHasFocus(true);
|
|
43
41
|
}, [setHasFocus]);
|