funda-ui 4.7.755 → 4.7.770

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.
@@ -332,7 +332,7 @@ var external_root_React_commonjs2_react_commonjs_react_amd_react_default = /*#__
332
332
  // EXTERNAL MODULE: ../Utils/dist/cjs/cls.js
333
333
  var cls = __webpack_require__(188);
334
334
  ;// CONCATENATED MODULE: ./src/AccordionItem.tsx
335
- var _excluded = ["heightObserver", "index", "animSpeed", "easing", "arrowOnly", "itemClassName", "itemContentWrapperClassName", "itemContentClassName", "itemTriggerClassName", "itemHeaderClassName", "itemTriggerIcon", "itemStyle", "activeItem", "title", "onToggleEv", "onTransitionEnd", "onItemCollapse", "isExpanded", "children"];
335
+ var _excluded = ["heightObserver", "index", "animSpeed", "easing", "arrowOnly", "itemClassName", "itemContentWrapperClassName", "itemContentClassName", "itemTriggerClassName", "itemHeaderClassName", "itemTriggerIcon", "itemStyle", "activeItem", "title", "onToggleEv", "onTransitionEnd", "onItemCollapse", "isExpanded", "forceExpanded", "children"];
336
336
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
337
337
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
338
338
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
@@ -363,6 +363,7 @@ var AccordionItem = function AccordionItem(props) {
363
363
  onTransitionEnd = props.onTransitionEnd,
364
364
  onItemCollapse = props.onItemCollapse,
365
365
  controlledExpanded = props.isExpanded,
366
+ forceExpanded = props.forceExpanded,
366
367
  children = props.children,
367
368
  attributes = _objectWithoutProperties(props, _excluded);
368
369
  var _useState = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(false),
@@ -371,9 +372,12 @@ var AccordionItem = function AccordionItem(props) {
371
372
  setInternalExpanded = _useState2[1];
372
373
  var isFirstRender = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(true);
373
374
  var initialHeightSet = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(false);
375
+ var hasUserInteracted = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(false);
374
376
 
375
- // Use controlled or uncontrolled expanded state
376
- var isExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
377
+ // Use forceExpanded if provided and user hasn't interacted, otherwise use controlled or uncontrolled expanded state
378
+ // forceExpanded takes priority over isExpanded from parent, but only before user interaction
379
+ var actualExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
380
+ var isExpanded = forceExpanded !== undefined && !hasUserInteracted.current ? forceExpanded : actualExpanded;
377
381
  var observer = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
378
382
  var contentWrapperRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
379
383
  var contentRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
@@ -382,6 +386,9 @@ var AccordionItem = function AccordionItem(props) {
382
386
  var handleToggle = function handleToggle(e) {
383
387
  e.preventDefault();
384
388
  e.stopPropagation();
389
+
390
+ // Mark that user has interacted, so forceExpanded will be ignored
391
+ hasUserInteracted.current = true;
385
392
  if (controlledExpanded === undefined) {
386
393
  setInternalExpanded(function (prev) {
387
394
  return !prev;
@@ -399,6 +406,13 @@ var AccordionItem = function AccordionItem(props) {
399
406
  overflow: 'hidden'
400
407
  };
401
408
  };
409
+
410
+ // Reset user interaction flag when forceExpanded changes externally
411
+ (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function () {
412
+ if (forceExpanded !== undefined) {
413
+ hasUserInteracted.current = false;
414
+ }
415
+ }, [forceExpanded]);
402
416
  (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function () {
403
417
  if (triggerRef.current && typeof onItemCollapse === 'function') {
404
418
  if (isFirstRender.current) {
@@ -3853,30 +3853,41 @@ var LiveSearch = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(
3853
3853
  if (listRef.current === null || !rootRef.current.classList.contains('active')) return;
3854
3854
  var options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide)'));
3855
3855
  // Avoid selecting options that are disabled
3856
- options = options.filter(function (options) {
3857
- return !options.classList.contains('disabled');
3856
+ options = options.filter(function (option) {
3857
+ return !option.classList.contains('disabled');
3858
3858
  });
3859
+ if (!options.length) return;
3860
+ var activeNode = listRef.current.querySelector('.list-group-item.active');
3859
3861
  var currentIndex = options.findIndex(function (e) {
3860
- return e === listRef.current.querySelector('.list-group-item.active');
3862
+ return e === activeNode;
3861
3863
  });
3862
3864
 
3863
3865
  // get the next element in the list, "%" will loop around to 0
3864
- var nextIndex;
3866
+ var nextIndex = -1;
3865
3867
  if (type === 'increase') {
3866
- nextIndex = currentIndex + 1 % options.length;
3868
+ // ArrowDown
3869
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
3867
3870
  } else {
3868
- nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
3871
+ // ArrowUp
3872
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
3869
3873
  }
3870
3874
 
3871
3875
  //only one
3872
3876
  if (options.length === 1) nextIndex = 0;
3873
- if (!isNaN(nextIndex)) {
3874
- options.forEach(function (node, index) {
3877
+ if (nextIndex >= 0 && nextIndex < options.length) {
3878
+ options.forEach(function (node) {
3875
3879
  node === null || node === void 0 ? void 0 : node.classList.remove('active');
3876
3880
  });
3877
3881
  var targetOption = options[nextIndex];
3878
3882
  if (typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match')) {
3879
3883
  targetOption.classList.add('active');
3884
+
3885
+ // Ensure the focused option is visible in the scroll area
3886
+ if (typeof targetOption.scrollIntoView === 'function') {
3887
+ targetOption.scrollIntoView({
3888
+ block: 'nearest'
3889
+ });
3890
+ }
3880
3891
  resolve(targetOption);
3881
3892
  }
3882
3893
  }
package/Select/index.js CHANGED
@@ -5721,28 +5721,39 @@ var Select = /*#__PURE__*/(0,external_root_React_commonjs2_react_commonjs_react_
5721
5721
 
5722
5722
  // Avoid selecting options that are disabled
5723
5723
  var options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide):not(.custom-select-multi__control-option-item--select-all):not(.custom-select-multi__control-option-item--clear)'));
5724
+ if (!options.length) return;
5725
+ var activeNode = listRef.current.querySelector('.list-group-item.active');
5724
5726
  var currentIndex = options.findIndex(function (e) {
5725
- return e === listRef.current.querySelector('.list-group-item.active');
5727
+ return e === activeNode;
5726
5728
  });
5727
5729
 
5728
5730
  // get the next element in the list, "%" will loop around to 0
5729
- var nextIndex;
5731
+ var nextIndex = -1;
5730
5732
  if (type === 'increase') {
5731
- nextIndex = currentIndex + 1 % options.length;
5733
+ // ArrowDown
5734
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
5732
5735
  } else {
5733
- nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
5736
+ // ArrowUp
5737
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
5734
5738
  }
5735
5739
 
5736
5740
  //only one
5737
5741
  if (options.length === 1) nextIndex = 0;
5738
- if (!isNaN(nextIndex)) {
5739
- options.forEach(function (node, index) {
5742
+ if (nextIndex >= 0 && nextIndex < options.length) {
5743
+ options.forEach(function (node) {
5740
5744
  node === null || node === void 0 ? void 0 : node.classList.remove('active');
5741
5745
  });
5742
5746
  var targetOption = options[nextIndex];
5743
5747
  if (typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match')) {
5744
5748
  targetOption.classList.add('active');
5745
5749
  keyboardSelectedItem.current = targetOption;
5750
+
5751
+ // Ensure the focused option is visible in the scroll area
5752
+ if (typeof targetOption.scrollIntoView === 'function') {
5753
+ targetOption.scrollIntoView({
5754
+ block: 'nearest'
5755
+ });
5756
+ }
5746
5757
  resolve(targetOption);
5747
5758
  }
5748
5759
  }
@@ -332,7 +332,7 @@ var external_root_React_commonjs2_react_commonjs_react_amd_react_default = /*#__
332
332
  // EXTERNAL MODULE: ../Utils/dist/cjs/cls.js
333
333
  var cls = __webpack_require__(188);
334
334
  ;// CONCATENATED MODULE: ./src/AccordionItem.tsx
335
- var _excluded = ["heightObserver", "index", "animSpeed", "easing", "arrowOnly", "itemClassName", "itemContentWrapperClassName", "itemContentClassName", "itemTriggerClassName", "itemHeaderClassName", "itemTriggerIcon", "itemStyle", "activeItem", "title", "onToggleEv", "onTransitionEnd", "onItemCollapse", "isExpanded", "children"];
335
+ var _excluded = ["heightObserver", "index", "animSpeed", "easing", "arrowOnly", "itemClassName", "itemContentWrapperClassName", "itemContentClassName", "itemTriggerClassName", "itemHeaderClassName", "itemTriggerIcon", "itemStyle", "activeItem", "title", "onToggleEv", "onTransitionEnd", "onItemCollapse", "isExpanded", "forceExpanded", "children"];
336
336
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
337
337
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
338
338
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
@@ -363,6 +363,7 @@ var AccordionItem = function AccordionItem(props) {
363
363
  onTransitionEnd = props.onTransitionEnd,
364
364
  onItemCollapse = props.onItemCollapse,
365
365
  controlledExpanded = props.isExpanded,
366
+ forceExpanded = props.forceExpanded,
366
367
  children = props.children,
367
368
  attributes = _objectWithoutProperties(props, _excluded);
368
369
  var _useState = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(false),
@@ -371,9 +372,12 @@ var AccordionItem = function AccordionItem(props) {
371
372
  setInternalExpanded = _useState2[1];
372
373
  var isFirstRender = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(true);
373
374
  var initialHeightSet = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(false);
375
+ var hasUserInteracted = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(false);
374
376
 
375
- // Use controlled or uncontrolled expanded state
376
- var isExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
377
+ // Use forceExpanded if provided and user hasn't interacted, otherwise use controlled or uncontrolled expanded state
378
+ // forceExpanded takes priority over isExpanded from parent, but only before user interaction
379
+ var actualExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
380
+ var isExpanded = forceExpanded !== undefined && !hasUserInteracted.current ? forceExpanded : actualExpanded;
377
381
  var observer = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
378
382
  var contentWrapperRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
379
383
  var contentRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
@@ -382,6 +386,9 @@ var AccordionItem = function AccordionItem(props) {
382
386
  var handleToggle = function handleToggle(e) {
383
387
  e.preventDefault();
384
388
  e.stopPropagation();
389
+
390
+ // Mark that user has interacted, so forceExpanded will be ignored
391
+ hasUserInteracted.current = true;
385
392
  if (controlledExpanded === undefined) {
386
393
  setInternalExpanded(function (prev) {
387
394
  return !prev;
@@ -399,6 +406,13 @@ var AccordionItem = function AccordionItem(props) {
399
406
  overflow: 'hidden'
400
407
  };
401
408
  };
409
+
410
+ // Reset user interaction flag when forceExpanded changes externally
411
+ (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function () {
412
+ if (forceExpanded !== undefined) {
413
+ hasUserInteracted.current = false;
414
+ }
415
+ }, [forceExpanded]);
402
416
  (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function () {
403
417
  if (triggerRef.current && typeof onItemCollapse === 'function') {
404
418
  if (isFirstRender.current) {
@@ -3853,30 +3853,41 @@ var LiveSearch = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(
3853
3853
  if (listRef.current === null || !rootRef.current.classList.contains('active')) return;
3854
3854
  var options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide)'));
3855
3855
  // Avoid selecting options that are disabled
3856
- options = options.filter(function (options) {
3857
- return !options.classList.contains('disabled');
3856
+ options = options.filter(function (option) {
3857
+ return !option.classList.contains('disabled');
3858
3858
  });
3859
+ if (!options.length) return;
3860
+ var activeNode = listRef.current.querySelector('.list-group-item.active');
3859
3861
  var currentIndex = options.findIndex(function (e) {
3860
- return e === listRef.current.querySelector('.list-group-item.active');
3862
+ return e === activeNode;
3861
3863
  });
3862
3864
 
3863
3865
  // get the next element in the list, "%" will loop around to 0
3864
- var nextIndex;
3866
+ var nextIndex = -1;
3865
3867
  if (type === 'increase') {
3866
- nextIndex = currentIndex + 1 % options.length;
3868
+ // ArrowDown
3869
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
3867
3870
  } else {
3868
- nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
3871
+ // ArrowUp
3872
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
3869
3873
  }
3870
3874
 
3871
3875
  //only one
3872
3876
  if (options.length === 1) nextIndex = 0;
3873
- if (!isNaN(nextIndex)) {
3874
- options.forEach(function (node, index) {
3877
+ if (nextIndex >= 0 && nextIndex < options.length) {
3878
+ options.forEach(function (node) {
3875
3879
  node === null || node === void 0 ? void 0 : node.classList.remove('active');
3876
3880
  });
3877
3881
  var targetOption = options[nextIndex];
3878
3882
  if (typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match')) {
3879
3883
  targetOption.classList.add('active');
3884
+
3885
+ // Ensure the focused option is visible in the scroll area
3886
+ if (typeof targetOption.scrollIntoView === 'function') {
3887
+ targetOption.scrollIntoView({
3888
+ block: 'nearest'
3889
+ });
3890
+ }
3880
3891
  resolve(targetOption);
3881
3892
  }
3882
3893
  }
@@ -5721,28 +5721,39 @@ var Select = /*#__PURE__*/(0,external_root_React_commonjs2_react_commonjs_react_
5721
5721
 
5722
5722
  // Avoid selecting options that are disabled
5723
5723
  var options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide):not(.custom-select-multi__control-option-item--select-all):not(.custom-select-multi__control-option-item--clear)'));
5724
+ if (!options.length) return;
5725
+ var activeNode = listRef.current.querySelector('.list-group-item.active');
5724
5726
  var currentIndex = options.findIndex(function (e) {
5725
- return e === listRef.current.querySelector('.list-group-item.active');
5727
+ return e === activeNode;
5726
5728
  });
5727
5729
 
5728
5730
  // get the next element in the list, "%" will loop around to 0
5729
- var nextIndex;
5731
+ var nextIndex = -1;
5730
5732
  if (type === 'increase') {
5731
- nextIndex = currentIndex + 1 % options.length;
5733
+ // ArrowDown
5734
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
5732
5735
  } else {
5733
- nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
5736
+ // ArrowUp
5737
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
5734
5738
  }
5735
5739
 
5736
5740
  //only one
5737
5741
  if (options.length === 1) nextIndex = 0;
5738
- if (!isNaN(nextIndex)) {
5739
- options.forEach(function (node, index) {
5742
+ if (nextIndex >= 0 && nextIndex < options.length) {
5743
+ options.forEach(function (node) {
5740
5744
  node === null || node === void 0 ? void 0 : node.classList.remove('active');
5741
5745
  });
5742
5746
  var targetOption = options[nextIndex];
5743
5747
  if (typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match')) {
5744
5748
  targetOption.classList.add('active');
5745
5749
  keyboardSelectedItem.current = targetOption;
5750
+
5751
+ // Ensure the focused option is visible in the scroll area
5752
+ if (typeof targetOption.scrollIntoView === 'function') {
5753
+ targetOption.scrollIntoView({
5754
+ block: 'nearest'
5755
+ });
5756
+ }
5746
5757
  resolve(targetOption);
5747
5758
  }
5748
5759
  }
@@ -2,7 +2,6 @@ import React, { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
4
4
 
5
-
6
5
  export type AccordionItemProps = {
7
6
  heightObserver?: number[];
8
7
  index?: number;
@@ -29,6 +28,8 @@ export type AccordionItemProps = {
29
28
  onItemCollapse?: (trigger: HTMLElement, icon: HTMLElement, isExpanded: boolean) => void;
30
29
  /** Control expanded state from parent */
31
30
  isExpanded?: boolean;
31
+ /** Force expanded state, takes priority over isExpanded from parent */
32
+ forceExpanded?: boolean;
32
33
  /** -- */
33
34
  children: React.ReactNode;
34
35
  };
@@ -56,6 +57,7 @@ const AccordionItem = (props: AccordionItemProps) => {
56
57
  onTransitionEnd,
57
58
  onItemCollapse,
58
59
  isExpanded: controlledExpanded,
60
+ forceExpanded,
59
61
  children,
60
62
  ...attributes
61
63
  } = props;
@@ -63,9 +65,12 @@ const AccordionItem = (props: AccordionItemProps) => {
63
65
  const [internalExpanded, setInternalExpanded] = useState<boolean>(false);
64
66
  const isFirstRender = useRef<boolean>(true);
65
67
  const initialHeightSet = useRef<boolean>(false);
68
+ const hasUserInteracted = useRef<boolean>(false);
66
69
 
67
- // Use controlled or uncontrolled expanded state
68
- const isExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
70
+ // Use forceExpanded if provided and user hasn't interacted, otherwise use controlled or uncontrolled expanded state
71
+ // forceExpanded takes priority over isExpanded from parent, but only before user interaction
72
+ const actualExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
73
+ const isExpanded = (forceExpanded !== undefined && !hasUserInteracted.current) ? forceExpanded : actualExpanded;
69
74
 
70
75
  const observer = useRef<ResizeObserver | null>(null);
71
76
  const contentWrapperRef = useRef<HTMLDivElement | null>(null);
@@ -77,6 +82,9 @@ const AccordionItem = (props: AccordionItemProps) => {
77
82
  e.preventDefault();
78
83
  e.stopPropagation();
79
84
 
85
+ // Mark that user has interacted, so forceExpanded will be ignored
86
+ hasUserInteracted.current = true;
87
+
80
88
  if (controlledExpanded === undefined) {
81
89
  setInternalExpanded(prev => !prev);
82
90
  }
@@ -96,6 +104,13 @@ const AccordionItem = (props: AccordionItemProps) => {
96
104
  };
97
105
 
98
106
 
107
+ // Reset user interaction flag when forceExpanded changes externally
108
+ useEffect(() => {
109
+ if (forceExpanded !== undefined) {
110
+ hasUserInteracted.current = false;
111
+ }
112
+ }, [forceExpanded]);
113
+
99
114
  useEffect(() => {
100
115
  if (triggerRef.current && typeof onItemCollapse === 'function') {
101
116
  if (isFirstRender.current) {
@@ -17,7 +17,6 @@ import {
17
17
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
18
18
 
19
19
 
20
-
21
20
  export interface OptionConfig {
22
21
  disabled?: boolean;
23
22
  label: any;
@@ -740,35 +739,45 @@ const LiveSearch = forwardRef((props: LiveSearchProps, externalRef: any) => {
740
739
  if (listRef.current === null || !rootRef.current.classList.contains('active')) return;
741
740
 
742
741
 
743
- let options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide)'));
742
+ let options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide)')) as HTMLElement[];
744
743
  // Avoid selecting options that are disabled
745
- options = options.filter((options: HTMLElement) => !options.classList.contains('disabled'));
744
+ options = options.filter((option: HTMLElement) => !option.classList.contains('disabled'));
745
+
746
+ if (!options.length) return;
746
747
 
747
- const currentIndex = options.findIndex((e) => e === listRef.current.querySelector('.list-group-item.active'));
748
+ const activeNode = listRef.current.querySelector('.list-group-item.active') as HTMLElement | null;
749
+ const currentIndex = options.findIndex((e: HTMLElement) => e === activeNode);
748
750
 
749
751
 
750
752
  // get the next element in the list, "%" will loop around to 0
751
- let nextIndex;
753
+ let nextIndex = -1;
752
754
  if (type === 'increase') {
753
- nextIndex = currentIndex + 1 % options.length;
755
+ // ArrowDown
756
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
754
757
  } else {
755
- nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
758
+ // ArrowUp
759
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
756
760
  }
757
761
 
758
762
 
759
-
760
763
  //only one
761
764
  if (options.length === 1) nextIndex = 0;
762
765
 
763
766
 
764
- if (!isNaN(nextIndex)) {
765
- options.forEach((node: any, index: number) => {
767
+ if (nextIndex >= 0 && nextIndex < options.length) {
768
+ options.forEach((node: any) => {
766
769
  node?.classList.remove('active');
767
770
  });
768
771
 
769
772
  const targetOption = options[nextIndex] as HTMLElement;
770
773
  if (typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match')) {
771
774
  targetOption.classList.add('active');
775
+
776
+ // Ensure the focused option is visible in the scroll area
777
+ if (typeof (targetOption as any).scrollIntoView === 'function') {
778
+ (targetOption as any).scrollIntoView({ block: 'nearest' });
779
+ }
780
+
772
781
  resolve(targetOption);
773
782
  }
774
783
 
@@ -47,6 +47,7 @@ import {
47
47
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
48
48
 
49
49
 
50
+
50
51
  export interface MultiSelectValue {
51
52
  items: { label: string; value: string }[];
52
53
  labels: string[];
@@ -2012,26 +2013,35 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2012
2013
 
2013
2014
 
2014
2015
  // Avoid selecting options that are disabled
2015
- const options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.hide):not(.custom-select-multi__control-option-item--select-all):not(.custom-select-multi__control-option-item--clear)'));
2016
- const currentIndex = options.findIndex((e) => e === listRef.current.querySelector('.list-group-item.active'));
2016
+ const options = [].slice.call(
2017
+ listRef.current.querySelectorAll(
2018
+ '.list-group-item:not(.hide):not(.custom-select-multi__control-option-item--select-all):not(.custom-select-multi__control-option-item--clear)'
2019
+ )
2020
+ );
2021
+
2022
+ if (!options.length) return;
2023
+
2024
+ const activeNode = listRef.current.querySelector('.list-group-item.active');
2025
+ const currentIndex = options.findIndex((e: HTMLElement) => e === activeNode);
2017
2026
 
2018
2027
 
2019
2028
  // get the next element in the list, "%" will loop around to 0
2020
- let nextIndex;
2029
+ let nextIndex = -1;
2021
2030
  if (type === 'increase') {
2022
- nextIndex = currentIndex + 1 % options.length;
2031
+ // ArrowDown
2032
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
2023
2033
  } else {
2024
- nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
2034
+ // ArrowUp
2035
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
2025
2036
  }
2026
2037
 
2027
2038
 
2028
-
2029
2039
  //only one
2030
2040
  if (options.length === 1) nextIndex = 0;
2031
2041
 
2032
2042
 
2033
- if (!isNaN(nextIndex)) {
2034
- options.forEach((node: any, index: number) => {
2043
+ if (nextIndex >= 0 && nextIndex < options.length) {
2044
+ options.forEach((node: any) => {
2035
2045
  node?.classList.remove('active');
2036
2046
  });
2037
2047
 
@@ -2040,6 +2050,12 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2040
2050
  targetOption.classList.add('active');
2041
2051
 
2042
2052
  keyboardSelectedItem.current = targetOption;
2053
+
2054
+ // Ensure the focused option is visible in the scroll area
2055
+ if (typeof targetOption.scrollIntoView === 'function') {
2056
+ targetOption.scrollIntoView({ block: 'nearest' });
2057
+ }
2058
+
2043
2059
  resolve(targetOption);
2044
2060
  }
2045
2061
 
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"UIUX Lab","email":"uiuxlab@gmail.com","name":"funda-ui","version":"4.7.755","description":"React components using pure Bootstrap 5+ which does not contain any external style and script libraries.","repository":{"type":"git","url":"git+https://github.com/xizon/funda-ui.git"},"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"keywords":["bootstrap","react-bootstrap","react-components","components","components-react","react-bootstrap-components","react","funda-ui","fundaui","uikit","ui-kit","ui-components"],"bugs":{"url":"https://github.com/xizon/funda-ui/issues"},"homepage":"https://github.com/xizon/funda-ui#readme","main":"all.js","license":"MIT","dependencies":{"react":"^18.2.0","react-dom":"^18.2.0"},"types":"all.d.ts","publishConfig":{"directory":"lib"}}
1
+ {"author":"UIUX Lab","email":"uiuxlab@gmail.com","name":"funda-ui","version":"4.7.770","description":"React components using pure Bootstrap 5+ which does not contain any external style and script libraries.","repository":{"type":"git","url":"git+https://github.com/xizon/funda-ui.git"},"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"keywords":["bootstrap","react-bootstrap","react-components","components","components-react","react-bootstrap-components","react","funda-ui","fundaui","uikit","ui-kit","ui-components"],"bugs":{"url":"https://github.com/xizon/funda-ui/issues"},"homepage":"https://github.com/xizon/funda-ui#readme","main":"all.js","license":"MIT","dependencies":{"react":"^18.2.0","react-dom":"^18.2.0"},"types":"all.d.ts","publishConfig":{"directory":"lib"},"directories":{"lib":"lib"}}