utils.tsx 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. restGroupProps.key = child.key;
  51. let originKeys = [];
  52. if (Array.isArray(children)) {
  53. // if group has children > 1
  54. originKeys = children.map(item => item.key);
  55. } else {
  56. originKeys.push(children.key);
  57. }
  58. children = React.Children.toArray(children);
  59. const childrenOption = children.map((option: React.ReactElement, index: number) => {
  60. let newKey = option.key;
  61. if (originKeys[index] === null) {
  62. 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)
  63. }
  64. optionIndex++;
  65. return generateOption(option, restGroupProps, optionIndex, newKey);
  66. });
  67. const group = {
  68. ...child.props,
  69. children: childrenOption,
  70. key: child.key,
  71. };
  72. optionGroups.push(group);
  73. options = options.concat(childrenOption);
  74. } else {
  75. warning(true, '[Semi Select] The children of `Select` should be `Select.Option` or `Select.OptionGroup`');
  76. }
  77. });
  78. if (type === 'option') {
  79. optionGroups = [emptyGroup] as OptionGroupProps[];
  80. }
  81. return { optionGroups, options };
  82. };
  83. export { generateOption, getOptionsFromGroup };