@pushwoosh/dumb-components 2.9.15 → 2.9.17

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.
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useId, useMemo, useRef, useState } from 'react';
2
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
3
3
  import { DEFAULT_DEBOUNCE_MS, FieldInput, FieldShell, defaultGetKey, resolveDropdownText, useActiveIndex, useDropdownItems, useFieldOpenEffects, useListNavigation, useShellMouseDown, useToggleOpen } from '../shared/dropdownField';
4
4
  function FreeCombobox({
5
5
  items,
@@ -101,6 +101,7 @@ function FreeCombobox({
101
101
  onChange(event.target.value, {
102
102
  source: 'input'
103
103
  });
104
+ setActiveIndex(-1);
104
105
  if (!isOpen) open();
105
106
  };
106
107
  return _jsx(FieldShell, {
@@ -120,6 +121,7 @@ function FreeCombobox({
120
121
  onChange('', {
121
122
  source: 'input'
122
123
  });
124
+ setActiveIndex(-1);
123
125
  inputRef.current?.focus();
124
126
  },
125
127
  dropdownItems: dropdownItems,
@@ -198,13 +200,28 @@ function SelectCombobox({
198
200
  const valueLabel = hasValue ? getLabel(value) : '';
199
201
  const selectedKey = hasValue ? getKey(value) : null;
200
202
  const inputValue = isOpen ? query : valueLabel;
203
+ const userMovedRef = useRef(false);
201
204
  const open = useCallback(() => {
202
205
  if (isOpen) return;
203
206
  setIsOpen(true);
204
207
  setQuery('');
208
+ userMovedRef.current = false;
205
209
  const idx = sourceItems.findIndex(it => getKey(it) === selectedKey);
206
210
  setActiveIndex(idx >= 0 ? idx : 0);
207
211
  }, [isOpen, sourceItems, getKey, selectedKey, setActiveIndex]);
212
+ useEffect(() => {
213
+ if (!isOpen || query || userMovedRef.current) return;
214
+ const idx = sourceItems.findIndex(it => getKey(it) === selectedKey);
215
+ if (idx >= 0) setActiveIndex(idx);
216
+ }, [isOpen, query, sourceItems, getKey, selectedKey, setActiveIndex]);
217
+ const moveActive = useCallback(next => {
218
+ userMovedRef.current = true;
219
+ setActiveIndex(next);
220
+ }, [setActiveIndex]);
221
+ const search = event => {
222
+ setQuery(event.target.value);
223
+ moveActive(0);
224
+ };
208
225
  const close = useCallback(() => {
209
226
  setIsOpen(false);
210
227
  setQuery('');
@@ -221,7 +238,7 @@ function SelectCombobox({
221
238
  open,
222
239
  itemCount: filteredItems.length,
223
240
  activeIndex,
224
- setActiveIndex,
241
+ setActiveIndex: moveActive,
225
242
  onSelectIndex: index => commit(filteredItems[index]),
226
243
  onEscape: close,
227
244
  onReachEnd: loadMore
@@ -282,7 +299,7 @@ function SelectCombobox({
282
299
  ref: inputRef,
283
300
  autosize: autosize,
284
301
  value: inputValue,
285
- onChange: event => setQuery(event.target.value),
302
+ onChange: search,
286
303
  onKeyDown: onInputKeyDown,
287
304
  onFocus: open,
288
305
  placeholder: placeholder,
@@ -92,7 +92,8 @@ export function ComboboxMulti({
92
92
  const close = useCallback(() => {
93
93
  setIsOpen(false);
94
94
  setQuery('');
95
- }, []);
95
+ setActiveIndex(0);
96
+ }, [setActiveIndex]);
96
97
  const toggle = useCallback(item => {
97
98
  const key = getKey(item);
98
99
  if (selectedKeys.has(key)) {
@@ -202,7 +203,10 @@ export function ComboboxMulti({
202
203
  ref: inputRef,
203
204
  autosize: autosize,
204
205
  value: query,
205
- onChange: e => setQuery(e.target.value),
206
+ onChange: e => {
207
+ setQuery(e.target.value);
208
+ setActiveIndex(0);
209
+ },
206
210
  onKeyDown: onInputKeyDown,
207
211
  onFocus: open,
208
212
  placeholder: hasTags ? undefined : placeholder,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/dumb-components",
3
- "version": "2.9.15",
3
+ "version": "2.9.17",
4
4
  "description": "React components to build Pushwoosh products",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -20,6 +20,7 @@ export const FieldInput = forwardRef(({
20
20
  ref: ref,
21
21
  value: value,
22
22
  placeholder: placeholder,
23
+ spellCheck: false,
23
24
  ...rest
24
25
  });
25
26
  }
@@ -28,6 +29,7 @@ export const FieldInput = forwardRef(({
28
29
  ref: ref,
29
30
  value: value,
30
31
  placeholder: placeholder,
32
+ spellCheck: false,
31
33
  style: {
32
34
  flex: 'none',
33
35
  minWidth: 0,
@@ -5,6 +5,7 @@ import { CreateItemLabel, Dropdown, DropdownEmpty, DropdownItem, DropdownLoading
5
5
  import { Spinner } from '../../Spinner';
6
6
  const LOAD_MORE_THRESHOLD = 24;
7
7
  const CREATE_KEY = '__create__';
8
+ const TOUCH_SCROLL_THRESHOLD = 8;
8
9
  /**
9
10
  * Internal floating option list shared by the Combobox family: renders the rows,
10
11
  * empty/loading text, an optional "create" row and load-more support.
@@ -34,10 +35,44 @@ export function SuggestionsDropdown({
34
35
  const activeItem = items.find(item => item.active);
35
36
  const activeKey = createActive ? CREATE_KEY : activeItem?.key ?? null;
36
37
  useEffect(() => {
37
- if (activeKey === null) return;
38
- activeItemRef.current?.scrollIntoView({
38
+ if (activeKey === null) return undefined;
39
+ const scrollToActive = () => activeItemRef.current?.scrollIntoView({
39
40
  block: 'nearest'
40
41
  });
42
+ scrollToActive();
43
+ const list = activeItemRef.current?.parentElement;
44
+ if (!list || typeof ResizeObserver === 'undefined') return undefined;
45
+ const observer = new ResizeObserver(scrollToActive);
46
+ const stopFollowing = () => observer.disconnect();
47
+ const onScrollbarPointerDown = event => {
48
+ if (event.target === list) stopFollowing();
49
+ };
50
+ let touchStartY = null;
51
+ const onTouchStart = event => {
52
+ touchStartY = event.touches[0]?.clientY ?? null;
53
+ };
54
+ const onTouchMove = event => {
55
+ const y = event.touches[0]?.clientY;
56
+ if (touchStartY !== null && y !== undefined && Math.abs(y - touchStartY) > TOUCH_SCROLL_THRESHOLD) stopFollowing();
57
+ };
58
+ observer.observe(list);
59
+ list.addEventListener('wheel', stopFollowing, {
60
+ passive: true
61
+ });
62
+ list.addEventListener('touchstart', onTouchStart, {
63
+ passive: true
64
+ });
65
+ list.addEventListener('touchmove', onTouchMove, {
66
+ passive: true
67
+ });
68
+ list.addEventListener('pointerdown', onScrollbarPointerDown);
69
+ return () => {
70
+ observer.disconnect();
71
+ list.removeEventListener('wheel', stopFollowing);
72
+ list.removeEventListener('touchstart', onTouchStart);
73
+ list.removeEventListener('touchmove', onTouchMove);
74
+ list.removeEventListener('pointerdown', onScrollbarPointerDown);
75
+ };
41
76
  }, [activeKey]);
42
77
  if (typeof document === 'undefined') return null;
43
78
  const isEmpty = !loading && items.length === 0 && !createLabel;