utils.tsx 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import React from 'react';
  2. import warning from '@douyinfe/semi-foundation/utils/warning';
  3. import { OptionProps } from './option';
  4. import { OptionGroupProps } from './optionGroup';
  5. const generateOption = (child: React.ReactElement, parent: any, index: number, newKey?: string | number): OptionProps => {
  6. const childProps = child.props;
  7. if (!child || !childProps) {
  8. return null;
  9. }
  10. const option = {
  11. value: childProps.value,
  12. // Dropdown menu rendering priority label value, children, value in turn downgrade
  13. label: childProps.label || childProps.children || childProps.value,
  14. _show: true,
  15. _selected: false,
  16. _scrollIndex: index,
  17. ...childProps,
  18. _parentGroup: parent,
  19. };
  20. // Props are collected from ReactNode, after React.Children.toArray
  21. // no need to determine whether the key exists in child
  22. // Even if the user does not explicitly declare it, React will always generate a key.
  23. option._keyInJsx = newKey || child.key;
  24. return option;
  25. };
  26. const getOptionsFromGroup = (selectChildren: React.ReactNode) => {
  27. let optionGroups: OptionGroupProps[] = [];
  28. let options: OptionProps[] = [];
  29. const emptyGroup: {
  30. label: string;
  31. children: OptionProps[];
  32. _show: boolean
  33. } = { label: '', children: [], _show: false };
  34. // avoid null
  35. let childNodes = React.Children.toArray(selectChildren) as React.ReactElement[];
  36. childNodes = childNodes.filter((childNode) => childNode && childNode.props);
  37. let type = '';
  38. let optionIndex = -1;
  39. childNodes.forEach((child: React.ReactElement<any, any>) => {
  40. if (child.type.isSelectOption) {
  41. type = 'option';
  42. optionIndex++;
  43. const option = generateOption(child, undefined, optionIndex);
  44. emptyGroup.children.push(option);
  45. options.push(option);
  46. } else if (child.type.isSelectOptionGroup) {
  47. type = 'group';
  48. // Avoid saving children (reactNode) by... removing other props from the group except children, causing performance problems
  49. let { children, ...restGroupProps } = child.props;
  50. let originKeys = [];
  51. if (Array.isArray(children)) {
  52. // if group has children > 1
  53. originKeys = children.map(item => item.key);
  54. } else {
  55. originKeys.push(children.key);
  56. }
  57. children = React.Children.toArray(children);
  58. const childrenOption = children.map((option: React.ReactElement, index: number) => {
  59. let newKey = option.key;
  60. if (originKeys[index] === null) {
  61. newKey = child.key + '' + option.key; // if option in group and didn't set key, concat parent key to avoid conflict (default generate key just like .0, .1)
  62. }
  63. optionIndex++;
  64. return generateOption(option, restGroupProps, optionIndex, newKey);
  65. });
  66. const group = {
  67. ...child.props,
  68. children: childrenOption,
  69. key: child.key,
  70. };
  71. optionGroups.push(group);
  72. options = options.concat(childrenOption);
  73. } else {
  74. warning(true, '[Semi Select] The children of `Select` should be `Select.Option` or `Select.OptionGroup`');
  75. }
  76. });
  77. if (type === 'option') {
  78. optionGroups = [emptyGroup] as OptionGroupProps[];
  79. }
  80. return { optionGroups, options };
  81. };
  82. export { generateOption, getOptionsFromGroup };