@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
@@ -1,57 +1,50 @@
1
1
  import 'core-js/modules/esnext.async-iterator.find.js';
2
2
  import 'core-js/modules/esnext.iterator.constructor.js';
3
3
  import 'core-js/modules/esnext.iterator.find.js';
4
+ import 'core-js/modules/esnext.async-iterator.filter.js';
5
+ import 'core-js/modules/esnext.iterator.filter.js';
4
6
  import { useContext, useCallback } from 'react';
5
7
  import { ComboBoxContext } from '../../ComboBoxCTX.js';
6
- import { getOptions, findInCircularList } from '../../utils/listHelper.js';
7
- import { INTERNAL_MENU_OPTION_TYPES } from '../../constants.js';
8
+ import { getOptions, getSuggestedValueOnChange, findInCircularList, getLastValueSelected } from '../../utils/listHelper.js';
9
+ import { MENU_OPTION_TYPES, INTERNAL_MENU_OPTION_TYPES } from '../../constants.js';
8
10
 
9
11
  const isOptionFocuseable = opt => !['section', 'separator'].includes(opt.type) && !opt.disabled;
10
12
 
11
13
  const useKeyboardNavigation = () => {
12
14
  const {
13
15
  props: {
14
- inline,
15
- onSelectAll,
16
16
  onCancel,
17
17
  onKeyDown,
18
18
  onChange,
19
19
  onCreate,
20
+ onFilter,
21
+ onSelectAll,
22
+ inline,
20
23
  options,
21
24
  selectedValues,
22
- onFilter,
23
25
  withoutPortal
24
26
  },
25
- setShowPopover,
26
- setInputValue,
27
27
  inputValue,
28
28
  showPopover,
29
29
  focusOptionIdx,
30
- setFocusOptionIdx,
31
30
  selectAllCheckboxRef,
32
- toggleSelectionButtonRef
31
+ toggleSelectionButtonRef,
32
+ setShowPopover,
33
+ setInputValue,
34
+ scrollOptionIntoView,
35
+ setFocusOptionIdx
33
36
  } = useContext(ComboBoxContext);
34
37
  const multiple = Array.isArray(selectedValues);
35
38
  const selectableOptions = getOptions(options);
36
39
  const currentItemIndex = options.findIndex(opt => opt.dsId === focusOptionIdx);
37
40
  const currentItem = options.find(item => item.dsId === focusOptionIdx);
38
- const selectOption = useCallback(() => {
39
- const selectedOption = options.find(item => item.dsId === focusOptionIdx);
40
-
41
- if (!selectedOption.disabled) {
42
- if (onFilter) onFilter('');
43
- setInputValue('');
44
- onChange(selectedOption);
45
- if (!multiple) setShowPopover(false);
46
- }
47
- }, [multiple, options, setInputValue, focusOptionIdx, setShowPopover, onFilter, onChange]);
48
41
  const onInputKeyDown = useCallback(e => {
49
42
  // =============================================================================
50
43
  // CUSTOM KEYS
51
44
  // =============================================================================
52
- if (onKeyDown) onKeyDown(e, currentItem);
45
+ if (onKeyDown && (currentItem === null || currentItem === void 0 ? void 0 : currentItem.type) === MENU_OPTION_TYPES.OPTION) onKeyDown(e, currentItem);
53
46
 
54
- if (!['Escape', 'Tab', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
47
+ if (['ArrowDown', 'ArrowUp', 'Enter', 'Spacebar'].includes(e.key) || e.keyCode >= 48 && e.keyCode <= 90) {
55
48
  setShowPopover(true);
56
49
  } // =============================================================================
57
50
  // ESCAPE
@@ -86,8 +79,17 @@ const useKeyboardNavigation = () => {
86
79
  e.preventDefault();
87
80
  e.stopPropagation();
88
81
 
89
- if (focusOptionIdx !== '' && showPopover) {
90
- selectOption();
82
+ if (focusOptionIdx !== '' && showPopover && (currentItem === null || currentItem === void 0 ? void 0 : currentItem.type) === MENU_OPTION_TYPES.OPTION) {
83
+ if ((currentItem === null || currentItem === void 0 ? void 0 : currentItem.type) === 'option' && !currentItem.disabled) {
84
+ if (onFilter) onFilter('');
85
+ setInputValue('');
86
+
87
+ if (!multiple) {
88
+ setShowPopover(false);
89
+ }
90
+
91
+ onChange(getSuggestedValueOnChange(currentItem, selectedValues), currentItem, e);
92
+ }
91
93
  }
92
94
  }
93
95
 
@@ -95,8 +97,8 @@ const useKeyboardNavigation = () => {
95
97
  setShowPopover(false);
96
98
  }
97
99
 
98
- if (e.key === 'a' && e.ctrlKey && multiple) {
99
- if (onSelectAll) onSelectAll();
100
+ if (e.key === 'a' && e.ctrlKey && multiple && onSelectAll) {
101
+ onSelectAll(options.filter(option => option.type === 'option' && !option.disabled), e);
100
102
  } // =============================================================================
101
103
  // ARROWS UP AND DOWN: LOGIC TO CALCULATE NEXT OR PREV ITEM TO PSEUDOFOCUS FROM INPUT
102
104
  // =============================================================================
@@ -109,6 +111,7 @@ const useKeyboardNavigation = () => {
109
111
  if ((showPopover || inline) && selectableOptions.length) {
110
112
  const nextItemIndex = findInCircularList(options, currentItemIndex, isOptionFocuseable);
111
113
  setFocusOptionIdx(options[nextItemIndex].dsId);
114
+ scrollOptionIntoView(options[nextItemIndex].dsId);
112
115
  }
113
116
  }
114
117
 
@@ -120,11 +123,13 @@ const useKeyboardNavigation = () => {
120
123
 
121
124
  if (showPopover || inline) {
122
125
  setFocusOptionIdx(options[prevItemIndex].dsId);
126
+ scrollOptionIntoView(options[prevItemIndex].dsId);
123
127
  } else {
124
128
  setShowPopover(true);
125
129
  setTimeout(() => {
126
- const nextItemIndex = findInCircularList(options, 0, isOptionFocuseable, -1);
127
- setFocusOptionIdx(options[nextItemIndex].dsId);
130
+ const prevItem = findInCircularList(options, 0, isOptionFocuseable, -1);
131
+ setFocusOptionIdx(options[prevItem].dsId);
132
+ scrollOptionIntoView(options[prevItem].dsId);
128
133
  });
129
134
  }
130
135
  } // =============================================================================
@@ -133,10 +138,10 @@ const useKeyboardNavigation = () => {
133
138
 
134
139
 
135
140
  if (e.key === 'Backspace' && inputValue.length <= 0 && !inline) {
136
- if (multiple && !inputValue) {
137
- if (selectedValues.length > 0) onChange(selectedValues[selectedValues.length - 1]);
138
- } else {
139
- onChange(null);
141
+ const lastValue = getLastValueSelected(selectedValues);
142
+
143
+ if (!inputValue && lastValue) {
144
+ onChange(getSuggestedValueOnChange(lastValue, selectedValues), lastValue, e);
140
145
  }
141
146
 
142
147
  if (onFilter) onFilter('');
@@ -155,7 +160,8 @@ const useKeyboardNavigation = () => {
155
160
  // if (e.key === 'Tab' && multiple) {
156
161
  // }
157
162
 
158
- }, [onKeyDown, currentItem, onCreate, multiple, inputValue, inline, withoutPortal, setShowPopover, onCancel, onFilter, setInputValue, focusOptionIdx, showPopover, selectOption, onSelectAll, selectableOptions.length, options, currentItemIndex, setFocusOptionIdx, selectedValues, onChange, selectAllCheckboxRef, toggleSelectionButtonRef]);
163
+ }, // eslint-disable-next-line react-hooks/exhaustive-deps
164
+ [onKeyDown, currentItem, onCreate, multiple, inputValue, inline, withoutPortal, setShowPopover, onCancel, onFilter, setInputValue, focusOptionIdx, showPopover, selectableOptions.length, options, currentItemIndex, setFocusOptionIdx, selectedValues, onChange, selectAllCheckboxRef, toggleSelectionButtonRef]);
159
165
  return {
160
166
  onInputKeyDown
161
167
  };
@@ -13,9 +13,8 @@ var _StyledNoOptionsSelec;
13
13
  const HeaderList = () => {
14
14
  const {
15
15
  props: {
16
- onSelectAll,
17
- onClearAll,
18
- selectedValues
16
+ selectedValues,
17
+ onSelectAll
19
18
  },
20
19
  showSelectedOptions,
21
20
  selectAllCheckboxRef,
@@ -34,7 +33,7 @@ const HeaderList = () => {
34
33
  return /*#__PURE__*/_jsx(StyledHeaderListWrapper, {
35
34
  onKeyDown: handleKeyDown,
36
35
  onMouseDown: handleOnMouseDown
37
- }, void 0, /*#__PURE__*/_jsx(StyledSelectAllCheckbox, {}, void 0, onSelectAll && onClearAll && /*#__PURE__*/_jsx(DSControlledCheckbox, {
36
+ }, void 0, /*#__PURE__*/_jsx(StyledSelectAllCheckbox, {}, void 0, onSelectAll && /*#__PURE__*/_jsx(DSControlledCheckbox, {
38
37
  checked: checkboxStatus,
39
38
  "aria-controls": checkboxStatus === 'mixed' ? multiSelectedValues.map(item => item.dsId).join(' ') : '',
40
39
  name: "select-all-checkbox",
@@ -1,4 +1,8 @@
1
- import { useContext, useMemo, useCallback, useEffect } from 'react';
1
+ import 'core-js/modules/web.dom-collections.iterator.js';
2
+ import 'core-js/modules/esnext.async-iterator.filter.js';
3
+ import 'core-js/modules/esnext.iterator.constructor.js';
4
+ import 'core-js/modules/esnext.iterator.filter.js';
5
+ import { useContext, useMemo, useState, useEffect, useCallback } from 'react';
2
6
  import { getSelectableOptions } from '../../utils/listHelper.js';
3
7
  import { ComboBoxContext } from '../../ComboBoxCTX.js';
4
8
 
@@ -8,7 +12,6 @@ const useHeaderListHandlers = () => {
8
12
  selectedValues,
9
13
  options,
10
14
  onSelectAll,
11
- onClearAll,
12
15
  innerRef
13
16
  },
14
17
  showSelectedOptions,
@@ -17,8 +20,8 @@ const useHeaderListHandlers = () => {
17
20
  } = useContext(ComboBoxContext);
18
21
  const multiSelectedValues = selectedValues;
19
22
  const selectableOptions = getSelectableOptions(options);
20
- const checkboxStatus = useMemo(() => {
21
- if ((selectableOptions === null || selectableOptions === void 0 ? void 0 : selectableOptions.length) === (multiSelectedValues === null || multiSelectedValues === void 0 ? void 0 : multiSelectedValues.length)) {
23
+ const getCheckboxStatus = useMemo(() => {
24
+ if (multiSelectedValues.length === selectableOptions.length) {
22
25
  return true;
23
26
  }
24
27
 
@@ -27,17 +30,23 @@ const useHeaderListHandlers = () => {
27
30
  }
28
31
 
29
32
  return 'mixed';
30
- }, [multiSelectedValues, selectableOptions]);
33
+ }, [multiSelectedValues.length, selectableOptions.length]);
34
+ const [checkboxStatus, setCheckboxStatus] = useState(false);
35
+ useEffect(() => {
36
+ setCheckboxStatus(getCheckboxStatus);
37
+ }, [getCheckboxStatus, multiSelectedValues]);
31
38
  const handleOnMouseDown = useCallback(e => {
32
39
  e.preventDefault(); // this prevent the input control to lose focus
33
40
  }, []);
34
- const handleSelectAllCheckboxChange = useCallback(() => {
41
+ const handleSelectAllCheckboxChange = useCallback(e => {
35
42
  if ((!checkboxStatus || checkboxStatus === 'mixed') && onSelectAll) {
36
- onSelectAll();
37
- } else if (onClearAll) {
38
- onClearAll();
43
+ onSelectAll(options.filter(option => option.type === 'option' && !option.disabled), e);
44
+ setCheckboxStatus(true);
45
+ } else if (onSelectAll) {
46
+ onSelectAll([], e);
47
+ setCheckboxStatus(false);
39
48
  }
40
- }, [checkboxStatus, onSelectAll, onClearAll]);
49
+ }, [checkboxStatus, onSelectAll, options]);
41
50
  const handleToggleSelectedValuesFilter = useCallback(e => {
42
51
  e.preventDefault();
43
52
  setShowSelectedOptions(!showSelectedOptions);
@@ -5,7 +5,6 @@ import { ComboboxDataTestid } from '../../ComboboxDataTestids.js';
5
5
  import { ComboBoxContext } from '../../ComboBoxCTX.js';
6
6
  import { HeaderList } from '../header-list/HeaderList.js';
7
7
  import { useItemRenderer } from './useItemRenderer.js';
8
- import { useMenuListSetFocusOption } from './useMenuListSetFocusOption.js';
9
8
  import { jsxs, jsx } from 'react/jsx-runtime';
10
9
 
11
10
  var _HeaderList, _StyledNoResultsWrapp;
@@ -28,7 +27,6 @@ const MenuList = () => {
28
27
  const multiple = Array.isArray(selectedValues);
29
28
  const withHeader = !inline && multiple && options.length > 0 && inputValue === '';
30
29
  const ItemRenderer = useItemRenderer();
31
- useMenuListSetFocusOption();
32
30
  const preventLoseInputFocus = useCallback(e => {
33
31
  e.preventDefault();
34
32
  }, []);
@@ -12,7 +12,7 @@ import { Grid } from '@elliemae/ds-grid';
12
12
  import { Section, Separator, SingleMenuItem, MultiMenuItem } from '@elliemae/ds-form';
13
13
  import { ComboBoxContext } from '../../ComboBoxCTX.js';
14
14
  import { StyledCreatableLabel } from './styled.js';
15
- import { isSelected } from '../../utils/listHelper.js';
15
+ import { getSuggestedValueOnChange, isSelected } from '../../utils/listHelper.js';
16
16
  import { ComboboxDataTestid } from '../../ComboboxDataTestids.js';
17
17
  import { INTERNAL_MENU_OPTION_TYPES, MENU_OPTION_TYPES } from '../../constants.js';
18
18
  import { jsx, Fragment } from 'react/jsx-runtime';
@@ -47,28 +47,23 @@ const useItemRenderer = () => {
47
47
  setInputValue('');
48
48
  onFilter('');
49
49
  }
50
+ } else if (option.type === MENU_OPTION_TYPES.OPTION) {
51
+ if (!option.disabled) {
52
+ if (onFilter) onFilter('');
53
+ setInputValue('');
50
54
 
51
- return;
52
- }
53
-
54
- if (!option.disabled) {
55
- var _innerRef$current;
55
+ if (!multiple) {
56
+ setShowPopover(false);
57
+ }
56
58
 
57
- if (!multiple) {
58
- setShowPopover(false);
59
+ onChange(getSuggestedValueOnChange(option, selectedValues), option, e);
59
60
  }
60
-
61
- if (onFilter) onFilter('');
62
- setInputValue('');
63
- onChange(option); // if we are focus in the header list we go back to the input
64
-
65
- (_innerRef$current = innerRef.current) === null || _innerRef$current === void 0 ? void 0 : _innerRef$current.focus();
66
61
  } // prevent for loosing focus on input control
67
62
 
68
63
 
69
64
  e.stopPropagation();
70
65
  e.preventDefault();
71
- }, [onFilter, onChange, setShowPopover, multiple, innerRef]); // prevent blur from controls input
66
+ }, [onFilter, selectedValues, onChange, setShowPopover, multiple, innerRef]); // prevent blur from controls input
72
67
 
73
68
  const handleOnMouseDown = useCallback(e => {
74
69
  e.preventDefault();
@@ -6,14 +6,13 @@ import { DSPillGroupV2, DSPillV2 } from '@elliemae/ds-pills';
6
6
  import { ComboBoxContext } from '../../ComboBoxCTX.js';
7
7
  import { useGroupPills } from './useGroupPills.js';
8
8
  import { ComboboxDataTestid } from '../../ComboboxDataTestids.js';
9
+ import { RemovableSelectedValuePill } from './RemovableSelectedValuePill.js';
9
10
 
10
11
  const MultiSelectedValuesContainer = () => {
11
12
  const {
12
13
  props: {
13
14
  selectedValues,
14
- disabled,
15
- onChange,
16
- innerRef
15
+ disabled
17
16
  },
18
17
  pillGroupRef
19
18
  } = useContext(ComboBoxContext); // this is always an array because logic in controls component
@@ -27,25 +26,15 @@ const MultiSelectedValuesContainer = () => {
27
26
  "data-testid": ComboboxDataTestid.SELECTED_VALUES
28
27
  }, void 0, /*#__PURE__*/_jsx(DSPillGroupV2, {
29
28
  innerRef: pillGroupRef
30
- }, void 0, multiSelectedValue.length > 0 && multiSelectedValue.slice(0, correctPillsToShow).map(option => /*#__PURE__*/_jsx(DSPillV2, {
31
- size: "s",
32
- label: option.label,
33
- disabled: disabled,
34
- type: disabled ? 'value' : 'removable',
35
- tabIndex: -1,
36
- onRemove: () => {
37
- var _innerRef$current;
38
-
39
- onChange(option);
40
- (_innerRef$current = innerRef.current) === null || _innerRef$current === void 0 ? void 0 : _innerRef$current.focus();
41
- }
42
- }, option.value)), multiSelectedValue.length > 0 && correctPillsToShow !== multiSelectedValue.length && /*#__PURE__*/_jsx(DSPillV2, {
29
+ }, void 0, multiSelectedValue.length > 0 && multiSelectedValue.slice(0, correctPillsToShow).map(pill => /*#__PURE__*/_jsx(RemovableSelectedValuePill, {
30
+ pill: pill
31
+ }, pill.dsId)), multiSelectedValue.length > 0 && correctPillsToShow !== multiSelectedValue.length && /*#__PURE__*/_jsx(DSPillV2, {
43
32
  size: "s",
44
33
  labelTruncated: false,
45
34
  label: "+".concat(multiSelectedValue.length - correctPillsToShow),
46
35
  type: "value",
47
36
  disabled: disabled
48
- }, "grouped"))), [pillGroupRef, multiSelectedValue, correctPillsToShow, disabled, onChange, innerRef]);
37
+ }, "grouped"))), [pillGroupRef, multiSelectedValue, correctPillsToShow, disabled]);
49
38
  };
50
39
 
51
40
  export { MultiSelectedValuesContainer };
@@ -0,0 +1,34 @@
1
+ import _jsx from '@babel/runtime/helpers/esm/jsx';
2
+ import { useContext } from 'react';
3
+ import { DSPillV2 } from '@elliemae/ds-pills';
4
+ import { ComboBoxContext } from '../../ComboBoxCTX.js';
5
+ import { getSuggestedValueOnChange } from '../../utils/listHelper.js';
6
+
7
+ const RemovableSelectedValuePill = props => {
8
+ const {
9
+ pill
10
+ } = props;
11
+ const {
12
+ props: {
13
+ onChange,
14
+ disabled,
15
+ selectedValues,
16
+ innerRef
17
+ }
18
+ } = useContext(ComboBoxContext);
19
+ return /*#__PURE__*/_jsx(DSPillV2, {
20
+ size: "s",
21
+ label: pill.label,
22
+ disabled: disabled,
23
+ type: disabled ? 'value' : 'removable',
24
+ tabIndex: -1,
25
+ onRemove: e => {
26
+ var _innerRef$current;
27
+
28
+ onChange(getSuggestedValueOnChange(pill, selectedValues), pill, e);
29
+ (_innerRef$current = innerRef.current) === null || _innerRef$current === void 0 ? void 0 : _innerRef$current.focus();
30
+ }
31
+ }, pill.value);
32
+ };
33
+
34
+ export { RemovableSelectedValuePill };
@@ -18,10 +18,8 @@ const ComboboxPropTypes = _objectSpread(_objectSpread({}, globalAttributesPropTy
18
18
  autoFocus: PropTypes.bool.description('Whether the combo box uses auto focus or not').defaultValue('false'),
19
19
  hasError: PropTypes.bool.description('Whether the combo box has error or not').defaultValue('false'),
20
20
  onChange: PropTypes.func.isRequired.description('function triggered when an option is selected it will send the options selected').defaultValue('() => {}'),
21
- onSelectAll: PropTypes.func.description('function triggered when ALL button is selected from the header menu list').defaultValue('() => {}'),
22
21
  onFilter: PropTypes.func.description('function triggered when user type in the combobox input').defaultValue(''),
23
22
  onCreate: PropTypes.func.description('function triggered when user select "create" a new item whenever is not a match in the all option list').defaultValue(''),
24
- onClearAll: PropTypes.func.description('function triggered when user select CLEAR button from the header menu list').defaultValue(''),
25
23
  onCancel: PropTypes.func.description('function triggered when user press ESC key').defaultValue('() => {}'),
26
24
  onMenuOpen: PropTypes.func.description('function triggered when user open the menu list').defaultValue('() => {}'),
27
25
  onMenuClose: PropTypes.func.description('function triggered when user close the menu list').defaultValue('() => {}'),
@@ -1,5 +1,6 @@
1
1
  import { MENU_OPTION_TYPES } from './constants.js';
2
2
 
3
- const isSeparator = el => (el === null || el === void 0 ? void 0 : el.type) === MENU_OPTION_TYPES.separator;
3
+ const isSeparator = el => (el === null || el === void 0 ? void 0 : el.type) === MENU_OPTION_TYPES.SEPARATOR;
4
+ const isOption = el => (el === null || el === void 0 ? void 0 : el.type) === MENU_OPTION_TYPES.OPTION;
4
5
 
5
- export { isSeparator };
6
+ export { isOption, isSeparator };
@@ -3,6 +3,9 @@ import 'core-js/modules/esnext.iterator.constructor.js';
3
3
  import 'core-js/modules/esnext.iterator.filter.js';
4
4
  import 'core-js/modules/esnext.async-iterator.some.js';
5
5
  import 'core-js/modules/esnext.iterator.some.js';
6
+ import 'core-js/modules/esnext.async-iterator.find.js';
7
+ import 'core-js/modules/esnext.iterator.find.js';
8
+ import 'core-js/modules/web.dom-collections.iterator.js';
6
9
  import { MENU_OPTION_TYPES } from '../constants.js';
7
10
 
8
11
  const getSelectableOptions = options => options.filter(option => option.type === MENU_OPTION_TYPES.OPTION && !option.disabled);
@@ -27,5 +30,46 @@ const findInCircularList = function (list, from, criteria) {
27
30
 
28
31
  return from; // return same item
29
32
  };
33
+ const getFirstOption = (options, selectedValues, inputValue) => {
34
+ if (!inputValue) {
35
+ if (Array.isArray(selectedValues) && selectedValues.length > 0) {
36
+ return selectedValues[selectedValues.length - 1].dsId;
37
+ }
30
38
 
31
- export { findInCircularList, getOptions, getSelectableOptions, isSelected, isSelectedValueEmpty, isSelectedValueMultiple };
39
+ if (selectedValues !== null && selectedValues !== void 0 && selectedValues.dsId) {
40
+ return selectedValues.dsId;
41
+ }
42
+ }
43
+
44
+ for (let i = 0; i < options.length; i += 1) if (!['section', 'separator'].includes(options[i].type) && !options[i].disabled) {
45
+ return options[i].dsId;
46
+ }
47
+
48
+ return '';
49
+ };
50
+ const getSuggestedValueOnChange = (selectedOption, selectedValues) => {
51
+ if (Array.isArray(selectedValues)) {
52
+ const isFound = selectedValues.find(item => item.type === 'option' && item.value === selectedOption.value);
53
+
54
+ if (isFound) {
55
+ return selectedValues.filter(item => item.type === 'option' && item.value !== selectedOption.value);
56
+ }
57
+
58
+ return [...selectedValues, selectedOption];
59
+ }
60
+
61
+ if (selectedValues && selectedValues.dsId === selectedOption.dsId) {
62
+ return null;
63
+ }
64
+
65
+ return selectedOption;
66
+ };
67
+ const getLastValueSelected = selectedValues => {
68
+ if (Array.isArray(selectedValues)) {
69
+ return selectedValues[selectedValues.length - 1];
70
+ }
71
+
72
+ return selectedValues;
73
+ };
74
+
75
+ export { findInCircularList, getFirstOption, getLastValueSelected, getOptions, getSelectableOptions, getSuggestedValueOnChange, isSelected, isSelectedValueEmpty, isSelectedValueMultiple };
@@ -26,24 +26,16 @@ const conformValue = (rawValue, cursorPos, lastKeyCode, opts) => {
26
26
  decimalRequired
27
27
  } = opts;
28
28
  const THOUSANDSSEPARATOR = ',';
29
- let dotIdx = -1;
30
- let isNegative = false;
31
- let finalPrefix = ''; // Work with digits and dots only
29
+ let dotIdx = -1; // allow minus sign before and after the preffix
32
30
 
31
+ let finalPrefix = [prefix.length, 0].includes(rawValue.indexOf('-')) && allowNegative ? '-' : '';
33
32
  let maskedValue = rawValue.split('').filter((char, idx) => {
34
- 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
35
-
33
+ // we take only the first dot
34
+ const validChar = char >= '0' && char <= '9' || char === '.' && dotIdx === -1 && decimalPlaces;
36
35
  if (char === '.' && validChar) dotIdx = idx;
37
-
38
- if (char === '-' && validChar) {
39
- isNegative = true;
40
- finalPrefix += '-';
41
- return false;
42
- }
43
-
44
36
  return validChar;
45
37
  });
46
- if (prefix) finalPrefix += prefix; // REMOVE FOLLOWING ZEROS IN INTEGER PART AFTER LEADER ZERO NUMBER
38
+ if (prefix && maskedValue.length) finalPrefix += prefix; // REMOVE FOLLOWING ZEROS IN INTEGER PART AFTER LEADER ZERO NUMBER
47
39
 
48
40
  while (maskedValue.length >= 2 && maskedValue[0] === '0' && maskedValue[1] !== '.') {
49
41
  maskedValue = maskedValue.splice(1);
@@ -59,18 +51,26 @@ const conformValue = (rawValue, cursorPos, lastKeyCode, opts) => {
59
51
 
60
52
 
61
53
  if (dotIdx !== -1 && !(lastKeyCode === 'Backspace' && decimal.length === 1)) {
62
- maskedValue = [...integer, ...decimal];
54
+ if (integer.length) {
55
+ maskedValue = [...integer, ...decimal];
56
+ } else {
57
+ // if theres no integer add a zero to the left of the dot to cover .23 => 0.23 case
58
+ maskedValue = ['0', ...decimal];
59
+ }
63
60
  } else {
64
61
  // If decimal is required we add a dot after integer if exists
65
62
  maskedValue = decimalRequired && integer.length ? [...integer, '.'] : integer;
66
- }
63
+ } // remove orphans prefix for example "-$"
64
+
67
65
 
68
66
  if (lastKeyCode === 'Backspace' && maskedValue.length === 0) {
69
67
  finalPrefix = '';
70
- } // SET REAL CURSOR POSITION AFTER ADDING SIMBOLS
68
+ } // SET REAL CURSOR POSITION AFTER ADDING DOT and COMMAS SEPARATORS
69
+
71
70
 
71
+ let maskedPos = 0; // 0 leading case we move the cursor to continue typing
72
72
 
73
- let maskedPos = 0;
73
+ if (maskedValue[maskedPos] === '0' && cursorPos === 1 + prefix.length) maskedPos += 1;
74
74
 
75
75
  for (let i = 0; i < cursorPos; i += 1) {
76
76
  if (maskedValue[maskedPos] === THOUSANDSSEPARATOR) maskedPos += 1;
@@ -106,6 +106,7 @@ const useNumberMask = props => {
106
106
  const onBlur = useCallback(e => {
107
107
  const maskedValue = e.target.value;
108
108
  const dotIdx = maskedValue.indexOf('.');
109
+ const minusIndex = maskedValue.indexOf('-');
109
110
  const {
110
111
  decimalRequired,
111
112
  decimalPlaces,
@@ -126,6 +127,9 @@ const useNumberMask = props => {
126
127
  } else if (maskedValue.length - suffix.length - 1 === dotIdx) {
127
128
  // removing orphans dots
128
129
  valueSetter(maskedValue.replace('.', ''));
130
+ } else if (maskedValue.length - 1 === minusIndex) {
131
+ // removing orphans minus
132
+ valueSetter(maskedValue.replace('-', ''));
129
133
  }
130
134
 
131
135
  if (userOnBlur) userOnBlur(e);
@@ -13,7 +13,7 @@ import { DSMaskPhoneHookPropsTypes, DSMaskPhoneHookDefaultProps } from '../react
13
13
 
14
14
  const _excluded = ["valueSetter", "onChange", "onKeyDown", "onBlur"];
15
15
  const phoneSpecialChars = [['(', 0], [') ', 4], ['-', 9]];
16
- const internationalPhoneSpecialChars = [['+1 (', 0], [') ', 7], ['-', 13]];
16
+ const internationalPhoneSpecialChars = [['+1 (', 0], [') ', 7], ['-', 12]];
17
17
 
18
18
  const conformValue = (rawValue, cursorPos, lastKeyCode, opts) => {
19
19
  const characters = rawValue.split(''); // Work with digits only
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-controlled-form",
3
- "version": "2.3.2-next.0",
3
+ "version": "2.4.0-rc.0",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Controlled Form Controllers",
6
6
  "module": "./esm/index.js",
@@ -499,14 +499,6 @@
499
499
  "import": "./esm/combobox/utils/listHelper.js",
500
500
  "require": "./cjs/combobox/utils/listHelper.js"
501
501
  },
502
- "./combobox/utils/hooks/useOnElementResize": {
503
- "import": "./esm/combobox/utils/hooks/useOnElementResize.js",
504
- "require": "./cjs/combobox/utils/hooks/useOnElementResize.js"
505
- },
506
- "./combobox/utils/hooks/useKeyboardNavigation": {
507
- "import": "./esm/combobox/utils/hooks/useKeyboardNavigation.js",
508
- "require": "./cjs/combobox/utils/hooks/useKeyboardNavigation.js"
509
- },
510
502
  "./combobox/tests/utils": {
511
503
  "import": "./esm/combobox/tests/utils.js",
512
504
  "require": "./cjs/combobox/tests/utils.js"
@@ -527,6 +519,10 @@
527
519
  "import": "./esm/combobox/parts/multi-selected-values-container/useGroupPills.js",
528
520
  "require": "./cjs/combobox/parts/multi-selected-values-container/useGroupPills.js"
529
521
  },
522
+ "./combobox/parts/multi-selected-values-container/RemovableSelectedValuePill": {
523
+ "import": "./esm/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.js",
524
+ "require": "./cjs/combobox/parts/multi-selected-values-container/RemovableSelectedValuePill.js"
525
+ },
530
526
  "./combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer": {
531
527
  "import": "./esm/combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer.js",
532
528
  "require": "./cjs/combobox/parts/multi-selected-values-container/MultiSelectedValuesContainer.js"
@@ -707,6 +703,14 @@
707
703
  "import": "./esm/autocomplete/utils/hooks/useKeyboardNavigation.js",
708
704
  "require": "./cjs/autocomplete/utils/hooks/useKeyboardNavigation.js"
709
705
  },
706
+ "./autocomplete/sharedTypes": {
707
+ "import": "./esm/autocomplete/sharedTypes.js",
708
+ "require": "./cjs/autocomplete/sharedTypes.js"
709
+ },
710
+ "./autocomplete/react-desc-prop-types": {
711
+ "import": "./esm/autocomplete/react-desc-prop-types.js",
712
+ "require": "./cjs/autocomplete/react-desc-prop-types.js"
713
+ },
710
714
  "./autocomplete/propTypes": {
711
715
  "import": "./esm/autocomplete/propTypes.js",
712
716
  "require": "./cjs/autocomplete/propTypes.js"
@@ -731,6 +735,14 @@
731
735
  "import": "./esm/autocomplete/parts/menu-list/index.js",
732
736
  "require": "./cjs/autocomplete/parts/menu-list/index.js"
733
737
  },
738
+ "./autocomplete/parts/controls/useMaskedOnChange": {
739
+ "import": "./esm/autocomplete/parts/controls/useMaskedOnChange.js",
740
+ "require": "./cjs/autocomplete/parts/controls/useMaskedOnChange.js"
741
+ },
742
+ "./autocomplete/parts/controls/useKeyboardNavigation": {
743
+ "import": "./esm/autocomplete/parts/controls/useKeyboardNavigation.js",
744
+ "require": "./cjs/autocomplete/parts/controls/useKeyboardNavigation.js"
745
+ },
734
746
  "./autocomplete/parts/controls/useControlsInput": {
735
747
  "import": "./esm/autocomplete/parts/controls/useControlsInput.js",
736
748
  "require": "./cjs/autocomplete/parts/controls/useControlsInput.js"
@@ -824,20 +836,20 @@
824
836
  "build": "node ../../scripts/build/build.js"
825
837
  },
826
838
  "dependencies": {
827
- "@elliemae/ds-button": "2.3.2-next.0",
828
- "@elliemae/ds-circular-progress-indicator": "2.3.2-next.0",
829
- "@elliemae/ds-controlled-form": "2.3.2-next.0",
830
- "@elliemae/ds-form": "2.3.2-next.0",
831
- "@elliemae/ds-form-layout-blocks": "2.3.2-next.0",
832
- "@elliemae/ds-grid": "2.3.2-next.0",
833
- "@elliemae/ds-icon": "2.3.2-next.0",
834
- "@elliemae/ds-icons": "2.3.2-next.0",
835
- "@elliemae/ds-pills": "2.3.2-next.0",
836
- "@elliemae/ds-popperjs": "2.3.2-next.0",
837
- "@elliemae/ds-props-helpers": "2.3.2-next.0",
838
- "@elliemae/ds-system": "2.3.2-next.0",
839
- "@elliemae/ds-tooltip": "2.3.2-next.0",
840
- "@elliemae/ds-truncated-tooltip-text": "2.3.2-next.0",
839
+ "@elliemae/ds-button": "2.4.0-rc.0",
840
+ "@elliemae/ds-circular-progress-indicator": "2.4.0-rc.0",
841
+ "@elliemae/ds-controlled-form": "2.4.0-rc.0",
842
+ "@elliemae/ds-form": "2.4.0-rc.0",
843
+ "@elliemae/ds-form-layout-blocks": "2.4.0-rc.0",
844
+ "@elliemae/ds-grid": "2.4.0-rc.0",
845
+ "@elliemae/ds-icon": "2.4.0-rc.0",
846
+ "@elliemae/ds-icons": "2.4.0-rc.0",
847
+ "@elliemae/ds-pills": "2.4.0-rc.0",
848
+ "@elliemae/ds-popperjs": "2.4.0-rc.0",
849
+ "@elliemae/ds-props-helpers": "2.4.0-rc.0",
850
+ "@elliemae/ds-system": "2.4.0-rc.0",
851
+ "@elliemae/ds-tooltip": "2.4.0-rc.0",
852
+ "@elliemae/ds-truncated-tooltip-text": "2.4.0-rc.0",
841
853
  "prop-types": "~15.7.2",
842
854
  "react-desc": "~4.1.3",
843
855
  "react-popper": "~2.2.5",
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const useKeyboardNavigation: () => {
3
+ onInputKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
4
+ };