@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.
Files changed (65) hide show
  1. package/cjs/autocomplete/parts/controls/useKeyboardNavigation.js +93 -0
  2. package/cjs/autocomplete/parts/controls/useMaskedOnChange.js +28 -0
  3. package/cjs/autocomplete/react-desc-prop-types.js +19 -0
  4. package/cjs/autocomplete/sharedTypes.js +2 -0
  5. package/cjs/combobox/config/useComboBox.js +30 -6
  6. package/cjs/combobox/config/useCorrectOptions.js +1 -1
  7. package/cjs/combobox/parts/container/Container.js +25 -33
  8. package/cjs/combobox/parts/controls/Controls.js +4 -16
  9. package/cjs/combobox/parts/controls-input/useControlsInput.js +1 -3
  10. package/cjs/combobox/parts/controls-input/useKeyboardNavigation.js +36 -30
  11. package/cjs/combobox/parts/header-list/HeaderList.js +3 -4
  12. package/cjs/combobox/parts/header-list/useHeaderListHandlers.js +18 -9
  13. package/cjs/combobox/parts/menu-list/MenuList.js +0 -2
  14. package/cjs/combobox/parts/menu-list/useItemRenderer.js +9 -14
  15. package/cjs/combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer.js +6 -17
  16. package/cjs/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.js +42 -0
  17. package/cjs/combobox/react-desc-prop-types.js +0 -2
  18. package/cjs/combobox/sharedTypes.js +3 -1
  19. package/cjs/combobox/utils/listHelper.js +47 -0
  20. package/cjs/mask-hook/hooks/useNumberMask.js +21 -17
  21. package/cjs/mask-hook/hooks/usePhoneMask.js +1 -1
  22. package/esm/autocomplete/parts/controls/useKeyboardNavigation.js +89 -0
  23. package/esm/autocomplete/parts/controls/useMaskedOnChange.js +24 -0
  24. package/esm/autocomplete/react-desc-prop-types.js +14 -0
  25. package/esm/autocomplete/sharedTypes.js +1 -0
  26. package/esm/combobox/config/useComboBox.js +31 -7
  27. package/esm/combobox/config/useCorrectOptions.js +1 -1
  28. package/esm/combobox/parts/container/Container.js +26 -34
  29. package/esm/combobox/parts/controls/Controls.js +4 -16
  30. package/esm/combobox/parts/controls-input/useControlsInput.js +1 -3
  31. package/esm/combobox/parts/controls-input/useKeyboardNavigation.js +38 -32
  32. package/esm/combobox/parts/header-list/HeaderList.js +3 -4
  33. package/esm/combobox/parts/header-list/useHeaderListHandlers.js +19 -10
  34. package/esm/combobox/parts/menu-list/MenuList.js +0 -2
  35. package/esm/combobox/parts/menu-list/useItemRenderer.js +10 -15
  36. package/esm/combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer.js +6 -17
  37. package/esm/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.js +34 -0
  38. package/esm/combobox/react-desc-prop-types.js +0 -2
  39. package/esm/combobox/sharedTypes.js +3 -2
  40. package/esm/combobox/utils/listHelper.js +45 -1
  41. package/esm/mask-hook/hooks/useNumberMask.js +21 -17
  42. package/esm/mask-hook/hooks/usePhoneMask.js +1 -1
  43. package/package.json +35 -23
  44. package/types/autocomplete/parts/controls/useKeyboardNavigation.d.ts +4 -0
  45. package/types/autocomplete/parts/controls/useMaskedOnChange.d.ts +2 -0
  46. package/types/autocomplete/react-desc-prop-types.d.ts +54 -0
  47. package/types/autocomplete/sharedTypes.d.ts +20 -0
  48. package/types/combobox/parts/header-list/useHeaderListHandlers.d.ts +1 -1
  49. package/types/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.d.ts +5 -0
  50. package/types/combobox/react-desc-prop-types.d.ts +3 -4
  51. package/types/combobox/sharedTypes.d.ts +1 -0
  52. package/types/combobox/tests/{combobox-creatable.test.d.ts → creatable.test.d.ts} +0 -0
  53. package/types/combobox/tests/{combobox-disable.test.d.ts → disabled.test.d.ts} +0 -0
  54. package/types/combobox/tests/{combobox-general.test.d.ts → general.test.d.ts} +0 -0
  55. package/types/combobox/tests/{combobox-multi-select.test.d.ts → multi-select.test.d.ts} +0 -0
  56. package/types/combobox/tests/{combobox-onlyselectable.test.d.ts → onlyselectable.test.d.ts} +0 -0
  57. package/types/combobox/tests/{combobox-single-select.test.d.ts → single-select.test.d.ts} +0 -0
  58. package/types/combobox/tests/{combobox-usemask.test.d.ts → usemask.test.d.ts} +0 -0
  59. package/types/combobox/utils/listHelper.d.ts +3 -0
  60. package/cjs/combobox/utils/hooks/useKeyboardNavigation.js +0 -168
  61. package/cjs/combobox/utils/hooks/useOnElementResize.js +0 -37
  62. package/esm/combobox/utils/hooks/useKeyboardNavigation.js +0 -164
  63. package/esm/combobox/utils/hooks/useOnElementResize.js +0 -33
  64. package/types/combobox/utils/hooks/useKeyboardNavigation.d.ts +0 -4
  65. 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(option => /*#__PURE__*/_jsx__default["default"](dsPills.DSPillV2, {
39
- size: "s",
40
- label: option.label,
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, onChange, innerRef]);
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.separator;
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
- const validChar = char >= '0' && char <= '9' || char === '.' && dotIdx === -1 && idx > prefix.length + (isNegative ? 1 : 0) && decimalPlaces || char === '-' && (idx === 0 || prefix.length && idx === 1 && !isNegative) && allowNegative; // Allow first dot only
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
- maskedValue = [...integer, ...decimal];
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 SIMBOLS
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
- let maskedPos = 0;
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], ['-', 13]];
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
- // Menu Options with/out selected filter
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
- virtualListHelpers.scrollToIndex(correctOptions.findIndex(opt => opt.dsId === dsId));
56
- }, [correctOptions, virtualListHelpers]);
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(6);
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 { jsxs, jsx, Fragment } from 'react/jsx-runtime';
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 rest = _objectWithoutProperties(globalAttributes, _excluded);
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
- if (inline) {
71
- return /*#__PURE__*/jsxs(StyledContainer, _objectSpread(_objectSpread({
72
- ref: setReferenceElement
73
- }, rest), {}, {
74
- children: [_Controls || (_Controls = /*#__PURE__*/_jsx(Controls, {})), _MenuList || (_MenuList = /*#__PURE__*/_jsx(MenuList, {}))]
75
- }));
76
- }
77
-
78
- return /*#__PURE__*/jsx(Fragment, {
79
- children: /*#__PURE__*/jsxs(StyledContainer, _objectSpread(_objectSpread({
80
- "data-testid": ComboboxDataTestid.CONTAINER,
81
- ref: setReferenceElement
82
- }, rest), {}, {
83
- children: [_Controls2 || (_Controls2 = /*#__PURE__*/_jsx(Controls, {})), /*#__PURE__*/_jsx(DSPopperJS, {
84
- customOffset: [0, 5],
85
- referenceElement: referenceElement,
86
- showPopover: showPopover,
87
- closeContextMenu: handleCloseMenu,
88
- startPlacementPreference: startPlacementPreference,
89
- placementOrderPreference: placementOrderPreference,
90
- withoutPortal: withoutPortal,
91
- withoutArrow: true,
92
- withoutAnimation: true,
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(DSPillV2, {
87
- size: "s",
88
- labelTruncated: true,
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, setFocusOptionIdx, setHasFocus]);
38
+ }, [setInputValue, setShowSelectedOptions, onFilter, setHasFocus]);
41
39
  const handleOnFocus = useCallback(() => {
42
40
  setHasFocus(true);
43
41
  }, [setHasFocus]);