SoFunction
Updated on 2025-03-02

Example of how to add tab selection box to antd

text

The antd selection box does not allow tab to be selected by default. If there is such a requirement, the source code needs to be modified.

After analysis, the selection box of antd uses rc-select, and add Tab to rc-select/lib/ and rc-select/es/ in node_modules.

(Note: Every time npm install, rc-select will change and need to be re-overrided. If the antd version is updated, you need to re-test whether the tab function is normal to avoid invalidation due to its upgrade)

/lib/

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
(exports, "__esModule", {
  value: true
});
 = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var React = _interopRequireWildcard(require("react"));
var _KeyCode = _interopRequireDefault(require("rc-util/lib/KeyCode"));
var _omit = _interopRequireDefault(require("rc-util/lib/omit"));
var _pickAttrs = _interopRequireDefault(require("rc-util/lib/pickAttrs"));
var _useMemo = _interopRequireDefault(require("rc-util/lib/hooks/useMemo"));
var _classnames = _interopRequireDefault(require("classnames"));
var _rcVirtualList = _interopRequireDefault(require("rc-virtual-list"));
var _TransBtn = _interopRequireDefault(require("./TransBtn"));
var _platformUtil = require("./utils/platformUtil");
var _useBaseProps2 = _interopRequireDefault(require("./hooks/useBaseProps"));
var _SelectContext = _interopRequireDefault(require("./SelectContext"));
var _excluded = ["disabled", "title", "children", "style", "className"];
/**
 * Using virtual list of option display.
 * Will fallback to dom if use customize render.
 */
var OptionList = function OptionList(_, ref) {
  var _useBaseProps = (0, _useBaseProps2.default)(),
      prefixCls = _useBaseProps.prefixCls,
      id = _useBaseProps.id,
      open = _useBaseProps.open,
      multiple = _useBaseProps.multiple,
      searchValue = _useBaseProps.searchValue,
      toggleOpen = _useBaseProps.toggleOpen,
      notFoundContent = _useBaseProps.notFoundContent,
      onPopupScroll = _useBaseProps.onPopupScroll;
  var _React$useContext = (_SelectContext.default),
      flattenOptions = _React$,
      onActiveValue = _React$,
      defaultActiveFirstOption = _React$,
      onSelect = _React$,
      menuItemSelectedIcon = _React$,
      rawValues = _React$,
      fieldNames = _React$,
      virtual = _React$,
      listHeight = _React$,
      listItemHeight = _React$;
  var itemPrefixCls = "".concat(prefixCls, "-item");
  var memoFlattenOptions = (0, _useMemo.default)(function () {
    return flattenOptions;
  }, [open, flattenOptions], function (prev, next) {
    return next[0] && prev[1] !== next[1];
  }); // =========================== List ===========================
  var listRef = (null);
  var onListMouseDown = function onListMouseDown(event) {
    ();
  };
  var scrollIntoView = function scrollIntoView(index) {
    if () {
      ({
        index: index
      });
    }
  }; // ========================== Active ==========================
  var getEnabledActiveIndex = function getEnabledActiveIndex(index) {
    var offset =  > 1 && arguments[1] !== undefined ? arguments[1] : 1;
    var len = ;
    for (var i = 0; i < len; i += 1) {
      var current = (index + i * offset + len) % len;
      var _memoFlattenOptions$c = memoFlattenOptions[current],
          group = _memoFlattenOptions$,
          data = _memoFlattenOptions$;
      if (!group && !) {
        return current;
      }
    }
    return -1;
  };
  var _React$useState = (function () {
    return getEnabledActiveIndex(0);
  }),
      _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
      activeIndex = _React$useState2[0],
      setActiveIndex = _React$useState2[1];
  var setActive = function setActive(index) {
    var fromKeyboard =  > 1 && arguments[1] !== undefined ? arguments[1] : false;
    setActiveIndex(index);
    var info = {
      source: fromKeyboard ? 'keyboard' : 'mouse'
    }; // Trigger active event
    var flattenItem = memoFlattenOptions[index];
    if (!flattenItem) {
      onActiveValue(null, -1, info);
      return;
    }
    onActiveValue(, index, info);
  }; // Auto active first item when list length or searchValue changed
  (0, )(function () {
    setActive(defaultActiveFirstOption !== false ? getEnabledActiveIndex(0) : -1);
  }, [, searchValue]); // Auto scroll to item position in single mode
  (0, )(function () {
    /**
     * React will skip `onChange` when component update.
     * `setActive` function will call root accessibility state update which makes re-render.
     * So we need to delay to let Input component trigger onChange first.
     */
    var timeoutId = setTimeout(function () {
      if (!multiple && open &&  === 1) {
        var value = (rawValues)[0];
        var index = (function (_ref) {
          var data = _ref.data;
          return  === value;
        });
        if (index !== -1) {
          setActive(index);
          scrollIntoView(index);
        }
      }
    }); // Force trigger scrollbar visible when open
    if (open) {
      var _listRef$current;
      (_listRef$current = ) === null || _listRef$current === void 0 ? void 0 : _listRef$(undefined);
    }
    return function () {
      return clearTimeout(timeoutId);
    };
  }, [open, searchValue]); // ========================== Values ==========================
  var onSelectValue = function onSelectValue(value) {
    if (value !== undefined) {
      onSelect(value, {
        selected: !(value)
      });
    } // Single mode should always close by select
    if (!multiple) {
      toggleOpen(false);
    }
  }; // ========================= Keyboard =========================
  (ref, function () {
    return {
      onKeyDown: function onKeyDown(event) {
        var which = ,
            ctrlKey = ;
        switch (which) {
          // >>> Arrow keys & ctrl + n/p on Mac
          case _KeyCode.:
          case _KeyCode.:
          case _KeyCode.:
          case _KeyCode.:
            {
              var offset = 0;
              if (which === _KeyCode.) {
                offset = -1;
              } else if (which === _KeyCode.) {
                offset = 1;
              } else if ((0, _platformUtil.isPlatformMac)() && ctrlKey) {
                if (which === _KeyCode.) {
                  offset = 1;
                } else if (which === _KeyCode.) {
                  offset = -1;
                }
              }
              if (offset !== 0) {
                var nextActiveIndex = getEnabledActiveIndex(activeIndex + offset, offset);
                scrollIntoView(nextActiveIndex);
                setActive(nextActiveIndex, true);
              }
              break;
            }
          // >>> Select
           case _KeyCode.:Add totabSelect toggle
           {
             // value
             var item = memoFlattenOptions[activeIndex];
             if (item && !) {
               onSelectValue();
             } else {
               onSelectValue(undefined);
             }
             break;
           }
          case _KeyCode.:
            {
              // value
              var item = memoFlattenOptions[activeIndex];
              if (item && !) {
                onSelectValue();
              } else {
                onSelectValue(undefined);
              }
              if (open) {
                ();
              }
              break;
            }
          // >>> Close
          case _KeyCode.:
            {
              toggleOpen(false);
              if (open) {
                ();
              }
            }
        }
      },
      onKeyUp: function onKeyUp() {},
      scrollTo: function scrollTo(index) {
        scrollIntoView(index);
      }
    };
  }); // ========================== Render ==========================
  if ( === 0) {
    return /*#__PURE__*/("div", {
      role: "listbox",
      id: "".concat(id, "_list"),
      className: "".concat(itemPrefixCls, "-empty"),
      onMouseDown: onListMouseDown
    }, notFoundContent);
  }
  var omitFieldNameList = (fieldNames).map(function (key) {
    return fieldNames[key];
  });
  var getLabel = function getLabel(item) {
    return ;
  };
  var renderItem = function renderItem(index) {
    var item = memoFlattenOptions[index];
    if (!item) return null;
    var itemData =  || {};
    var value = ;
    var group = ;
    var attrs = (0, _pickAttrs.default)(itemData, true);
    var mergedLabel = getLabel(item);
    return item ? /*#__PURE__*/("div", (0, _extends2.default)({
      "aria-label": typeof mergedLabel === 'string' && !group ? mergedLabel : null
    }, attrs, {
      key: index,
      role: group ? 'presentation' : 'option',
      id: "".concat(id, "_list_").concat(index),
      "aria-selected": (value)
    }), value) : null;
  };
  return /*#__PURE__*/(, null, /*#__PURE__*/("div", {
    role: "listbox",
    id: "".concat(id, "_list"),
    style: {
      height: 0,
      width: 0,
      overflow: 'hidden'
    }
  }, renderItem(activeIndex - 1), renderItem(activeIndex), renderItem(activeIndex + 1)), /*#__PURE__*/(_rcVirtualList.default, {
    itemKey: "key",
    ref: listRef,
    data: memoFlattenOptions,
    height: listHeight,
    itemHeight: listItemHeight,
    fullHeight: false,
    onMouseDown: onListMouseDown,
    onScroll: onPopupScroll,
    virtual: virtual
  }, function (item, itemIndex) {
    var _classNames;
    var group = ,
        groupOption = ,
        data = ,
        label = ,
        value = ;
    var key = ; // Group
    if (group) {
      return /*#__PURE__*/("div", {
        className: (0, _classnames.default)(itemPrefixCls, "".concat(itemPrefixCls, "-group"))
      }, label !== undefined ? label : key);
    }
    var disabled = ,
        title = ,
        children = ,
        style = ,
        className = ,
        otherProps = (0, _objectWithoutProperties2.default)(data, _excluded);
    var passedProps = (0, _omit.default)(otherProps, omitFieldNameList); // Option
    var selected = (value);
    var optionPrefixCls = "".concat(itemPrefixCls, "-option");
    var optionClassName = (0, _classnames.default)(itemPrefixCls, optionPrefixCls, className, (_classNames = {}, (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-grouped"), groupOption), (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-active"), activeIndex === itemIndex && !disabled), (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-disabled"), disabled), (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-selected"), selected), _classNames));
    var mergedLabel = getLabel(item);
    var iconVisible = !menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
    var content = mergedLabel || value; // /ant-design/ant-design/issues/26717
    var optionTitle = typeof content === 'string' || typeof content === 'number' ? () : undefined;
    if (title !== undefined) {
      optionTitle = title;
    }
    return /*#__PURE__*/("div", (0, _extends2.default)({}, passedProps, {
      "aria-selected": selected,
      className: optionClassName,
      title: optionTitle,
      onMouseMove: function onMouseMove() {
        if (activeIndex === itemIndex || disabled) {
          return;
        }
        setActive(itemIndex);
      },
      onClick: function onClick() {
        if (!disabled) {
          onSelectValue(value);
        }
      },
      style: style
    }), /*#__PURE__*/("div", {
      className: "".concat(optionPrefixCls, "-content")
    }, content), /*#__PURE__*/(menuItemSelectedIcon) || selected, iconVisible && /*#__PURE__*/(_TransBtn.default, {
      className: "".concat(itemPrefixCls, "-option-state"),
      customizeIcon: menuItemSelectedIcon,
      customizeIconProps: {
        isSelected: selected
      }
    }, selected ? '✓' : null));
  }));
};
var RefOptionList = /*#__PURE__*/(OptionList);
 = 'OptionList';
var _default = RefOptionList;
 = _default;

/es/

import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/esm/extends";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
var _excluded = ["disabled", "title", "children", "style", "className"];
import * as React from 'react';
import { useEffect } from 'react';
import KeyCode from "rc-util/es/KeyCode";
import omit from "rc-util/es/omit";
import pickAttrs from "rc-util/es/pickAttrs";
import useMemo from "rc-util/es/hooks/useMemo";
import classNames from 'classnames';
import List from 'rc-virtual-list';
import TransBtn from './TransBtn';
import { isPlatformMac } from './utils/platformUtil';
import useBaseProps from './hooks/useBaseProps';
import SelectContext from './SelectContext';
/**
 * Using virtual list of option display.
 * Will fallback to dom if use customize render.
 */
var OptionList = function OptionList(_, ref) {
  var _useBaseProps = useBaseProps(),
      prefixCls = _useBaseProps.prefixCls,
      id = _useBaseProps.id,
      open = _useBaseProps.open,
      multiple = _useBaseProps.multiple,
      searchValue = _useBaseProps.searchValue,
      toggleOpen = _useBaseProps.toggleOpen,
      notFoundContent = _useBaseProps.notFoundContent,
      onPopupScroll = _useBaseProps.onPopupScroll;
  var _React$useContext = (SelectContext),
      flattenOptions = _React$,
      onActiveValue = _React$,
      defaultActiveFirstOption = _React$,
      onSelect = _React$,
      menuItemSelectedIcon = _React$,
      rawValues = _React$,
      fieldNames = _React$,
      virtual = _React$,
      listHeight = _React$,
      listItemHeight = _React$;
  var itemPrefixCls = "".concat(prefixCls, "-item");
  var memoFlattenOptions = useMemo(function () {
    return flattenOptions;
  }, [open, flattenOptions], function (prev, next) {
    return next[0] && prev[1] !== next[1];
  }); // =========================== List ===========================
  var listRef = (null);
  var onListMouseDown = function onListMouseDown(event) {
    ();
  };
  var scrollIntoView = function scrollIntoView(index) {
    if () {
      ({
        index: index
      });
    }
  }; // ========================== Active ==========================
  var getEnabledActiveIndex = function getEnabledActiveIndex(index) {
    var offset =  > 1 && arguments[1] !== undefined ? arguments[1] : 1;
    var len = ;
    for (var i = 0; i < len; i += 1) {
      var current = (index + i * offset + len) % len;
      var _memoFlattenOptions$c = memoFlattenOptions[current],
          group = _memoFlattenOptions$,
          data = _memoFlattenOptions$;
      if (!group && !) {
        return current;
      }
    }
    return -1;
  };
  var _React$useState = (function () {
    return getEnabledActiveIndex(0);
  }),
      _React$useState2 = _slicedToArray(_React$useState, 2),
      activeIndex = _React$useState2[0],
      setActiveIndex = _React$useState2[1];
  var setActive = function setActive(index) {
    var fromKeyboard =  > 1 && arguments[1] !== undefined ? arguments[1] : false;
    setActiveIndex(index);
    var info = {
      source: fromKeyboard ? 'keyboard' : 'mouse'
    }; // Trigger active event
    var flattenItem = memoFlattenOptions[index];
    if (!flattenItem) {
      onActiveValue(null, -1, info);
      return;
    }
    onActiveValue(, index, info);
  }; // Auto active first item when list length or searchValue changed
  useEffect(function () {
    setActive(defaultActiveFirstOption !== false ? getEnabledActiveIndex(0) : -1);
  }, [, searchValue]); // Auto scroll to item position in single mode
  useEffect(function () {
    /**
     * React will skip `onChange` when component update.
     * `setActive` function will call root accessibility state update which makes re-render.
     * So we need to delay to let Input component trigger onChange first.
     */
    var timeoutId = setTimeout(function () {
      if (!multiple && open &&  === 1) {
        var value = (rawValues)[0];
        var index = (function (_ref) {
          var data = _ref.data;
          return  === value;
        });
        if (index !== -1) {
          setActive(index);
          scrollIntoView(index);
        }
      }
    }); // Force trigger scrollbar visible when open
    if (open) {
      var _listRef$current;
      (_listRef$current = ) === null || _listRef$current === void 0 ? void 0 : _listRef$(undefined);
    }
    return function () {
      return clearTimeout(timeoutId);
    };
  }, [open, searchValue]); // ========================== Values ==========================
  var onSelectValue = function onSelectValue(value) {
    if (value !== undefined) {
      onSelect(value, {
        selected: !(value)
      });
    } // Single mode should always close by select
    if (!multiple) {
      toggleOpen(false);
    }
  }; // ========================= Keyboard =========================
  (ref, function () {
    return {
      onKeyDown: function onKeyDown(event) {
        var which = ,
            ctrlKey = ;
        switch (which) {
          // >>> Arrow keys & ctrl + n/p on Mac
          case :
          case :
          case :
          case :
            {
              var offset = 0;
              if (which === ) {
                offset = -1;
              } else if (which === ) {
                offset = 1;
              } else if (isPlatformMac() && ctrlKey) {
                if (which === ) {
                  offset = 1;
                } else if (which === ) {
                  offset = -1;
                }
              }
              if (offset !== 0) {
                var nextActiveIndex = getEnabledActiveIndex(activeIndex + offset, offset);
                scrollIntoView(nextActiveIndex);
                setActive(nextActiveIndex, true);
              }
              break;
            }
          // >>> Select
          case ://Add tab key to select to switch            {
              // value
              var item = memoFlattenOptions[activeIndex];
              if (item && !) {
                onSelectValue();
              } else {
                onSelectValue(undefined);
              }
              break;
            }
          case :
            {
              // value
              var item = memoFlattenOptions[activeIndex];
              if (item && !) {
                onSelectValue();
              } else {
                onSelectValue(undefined);
              }
              if (open) {
                ();
              }
              break;
            }
          // >>> Close
          case :
            {
              toggleOpen(false);
              if (open) {
                ();
              }
            }
        }
      },
      onKeyUp: function onKeyUp() {},
      scrollTo: function scrollTo(index) {
        scrollIntoView(index);
      }
    };
  }); // ========================== Render ==========================
  if ( === 0) {
    return /*#__PURE__*/("div", {
      role: "listbox",
      id: "".concat(id, "_list"),
      className: "".concat(itemPrefixCls, "-empty"),
      onMouseDown: onListMouseDown
    }, notFoundContent);
  }
  var omitFieldNameList = (fieldNames).map(function (key) {
    return fieldNames[key];
  });
  var getLabel = function getLabel(item) {
    return ;
  };
  var renderItem = function renderItem(index) {
    var item = memoFlattenOptions[index];
    if (!item) return null;
    var itemData =  || {};
    var value = ;
    var group = ;
    var attrs = pickAttrs(itemData, true);
    var mergedLabel = getLabel(item);
    return item ? /*#__PURE__*/("div", _extends({
      "aria-label": typeof mergedLabel === 'string' && !group ? mergedLabel : null
    }, attrs, {
      key: index,
      role: group ? 'presentation' : 'option',
      id: "".concat(id, "_list_").concat(index),
      "aria-selected": (value)
    }), value) : null;
  };
  return /*#__PURE__*/(, null, /*#__PURE__*/("div", {
    role: "listbox",
    id: "".concat(id, "_list"),
    style: {
      height: 0,
      width: 0,
      overflow: 'hidden'
    }
  }, renderItem(activeIndex - 1), renderItem(activeIndex), renderItem(activeIndex + 1)), /*#__PURE__*/(List, {
    itemKey: "key",
    ref: listRef,
    data: memoFlattenOptions,
    height: listHeight,
    itemHeight: listItemHeight,
    fullHeight: false,
    onMouseDown: onListMouseDown,
    onScroll: onPopupScroll,
    virtual: virtual
  }, function (item, itemIndex) {
    var _classNames;
    var group = ,
        groupOption = ,
        data = ,
        label = ,
        value = ;
    var key = ; // Group
    if (group) {
      return /*#__PURE__*/("div", {
        className: classNames(itemPrefixCls, "".concat(itemPrefixCls, "-group"))
      }, label !== undefined ? label : key);
    }
    var disabled = ,
        title = ,
        children = ,
        style = ,
        className = ,
        otherProps = _objectWithoutProperties(data, _excluded);
    var passedProps = omit(otherProps, omitFieldNameList); // Option
    var selected = (value);
    var optionPrefixCls = "".concat(itemPrefixCls, "-option");
    var optionClassName = classNames(itemPrefixCls, optionPrefixCls, className, (_classNames = {}, _defineProperty(_classNames, "".concat(optionPrefixCls, "-grouped"), groupOption), _defineProperty(_classNames, "".concat(optionPrefixCls, "-active"), activeIndex === itemIndex && !disabled), _defineProperty(_classNames, "".concat(optionPrefixCls, "-disabled"), disabled), _defineProperty(_classNames, "".concat(optionPrefixCls, "-selected"), selected), _classNames));
    var mergedLabel = getLabel(item);
    var iconVisible = !menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
    var content = mergedLabel || value; // /ant-design/ant-design/issues/26717
    var optionTitle = typeof content === 'string' || typeof content === 'number' ? () : undefined;
    if (title !== undefined) {
      optionTitle = title;
    }
    return /*#__PURE__*/("div", _extends({}, passedProps, {
      "aria-selected": selected,
      className: optionClassName,
      title: optionTitle,
      onMouseMove: function onMouseMove() {
        if (activeIndex === itemIndex || disabled) {
          return;
        }
        setActive(itemIndex);
      },
      onClick: function onClick() {
        if (!disabled) {
          onSelectValue(value);
        }
      },
      style: style
    }), /*#__PURE__*/("div", {
      className: "".concat(optionPrefixCls, "-content")
    }, content), /*#__PURE__*/(menuItemSelectedIcon) || selected, iconVisible && /*#__PURE__*/(TransBtn, {
      className: "".concat(itemPrefixCls, "-option-state"),
      customizeIcon: menuItemSelectedIcon,
      customizeIconProps: {
        isSelected: selected
      }
    }, selected ? '✓' : null));
  }));
};
var RefOptionList = /*#__PURE__*/(OptionList);
 = 'OptionList';
export default RefOptionList;

The above is the detailed content of the method example of how to add tab selection box to antd selection box. For more information about adding tab selection box to antd selection box, please pay attention to my other related articles!