index.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import React, { useState, useEffect, useRef } from 'react';
  2. import Icon from '../Icon';
  3. import Divider from '../Divider';
  4. import ClickAwayListener from '../ClickAwayListener';
  5. import {
  6. Container,
  7. Selected,
  8. Content,
  9. InputContent,
  10. OptionWrapper,
  11. Option,
  12. } from './styled';
  13. type Props = {
  14. onChange?: (item: SelectOptionType) => void;
  15. options: SelectOptionType[];
  16. defaultValue?: React.ReactNode;
  17. isDivide?: boolean;
  18. useInput?: boolean;
  19. style?: Record<string, string>;
  20. };
  21. const SelectBox: React.FC<Props> = ({
  22. onChange,
  23. options,
  24. defaultValue,
  25. isDivide,
  26. useInput,
  27. style,
  28. }: Props) => {
  29. const selectRef = useRef<HTMLDivElement>(null);
  30. const optionRef = useRef<HTMLDivElement>(null);
  31. const [isCollapse, setCollapse] = useState(true);
  32. const [value, setValue] = useState(defaultValue);
  33. const handleClick = (): void => {
  34. setCollapse(!isCollapse);
  35. };
  36. const handleSelect = (index: number): void => {
  37. setValue(options[index].content);
  38. handleBlur();
  39. if (onChange) {
  40. onChange(options[index]);
  41. }
  42. };
  43. const handleBlur = (): void => {
  44. setCollapse(true);
  45. };
  46. const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
  47. setValue(e.target.value || 0);
  48. };
  49. const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
  50. if (e.keyCode === 13 && onChange && value) {
  51. const param = {
  52. key: '',
  53. content: '',
  54. child: parseInt(value as string, 10),
  55. };
  56. onChange(param);
  57. setCollapse(true);
  58. }
  59. };
  60. useEffect(() => {
  61. if (defaultValue) {
  62. setValue(defaultValue);
  63. } else {
  64. setValue(options[0].content);
  65. }
  66. }, [defaultValue]);
  67. useEffect(() => {
  68. if (!isCollapse && selectRef.current) {
  69. const position = selectRef.current.getBoundingClientRect();
  70. if (optionRef.current) {
  71. optionRef.current.style.top = `${
  72. selectRef.current.clientHeight + position.top
  73. }px`;
  74. optionRef.current.style.left = `${position.left}px`;
  75. }
  76. }
  77. });
  78. return (
  79. <ClickAwayListener onClick={handleBlur}>
  80. <Container style={style}>
  81. <Selected
  82. ref={selectRef}
  83. onMouseDown={handleClick}
  84. onBlur={handleBlur}
  85. tabIndex={0}
  86. data-testid="selected"
  87. >
  88. {useInput &&
  89. (typeof value === 'string' || typeof value === 'number') ? (
  90. <InputContent
  91. value={value}
  92. onChange={handleChange}
  93. onKeyDown={handleKeyDown}
  94. />
  95. ) : (
  96. <Content>{value}</Content>
  97. )}
  98. {isDivide ? <Divider /> : null}
  99. <Icon glyph="down-arrow" />
  100. </Selected>
  101. {!isCollapse ? (
  102. <OptionWrapper ref={optionRef} data-testid="option-list">
  103. {options.map((option: SelectOptionType, index: number) => (
  104. <Option
  105. key={option.key}
  106. onMouseDown={(): void => {
  107. handleSelect(index);
  108. }}
  109. >
  110. {option.content}
  111. </Option>
  112. ))}
  113. </OptionWrapper>
  114. ) : null}
  115. </Container>
  116. </ClickAwayListener>
  117. );
  118. };
  119. SelectBox.defaultProps = {
  120. isDivide: false,
  121. useInput: false,
  122. style: {},
  123. defaultValue: '',
  124. };
  125. export default SelectBox;