index.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. if (onChange) {
  39. onChange(options[index]);
  40. }
  41. };
  42. const handleBlur = (): void => {
  43. setCollapse(true);
  44. };
  45. const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
  46. setValue(e.target.value || 0);
  47. };
  48. const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
  49. if (e.keyCode === 13 && onChange && value) {
  50. const param = {
  51. key: '',
  52. content: '',
  53. child: parseInt(value as string, 10),
  54. };
  55. onChange(param);
  56. setCollapse(true);
  57. }
  58. };
  59. useEffect(() => {
  60. if (defaultValue) {
  61. setValue(defaultValue);
  62. } else {
  63. setValue(options[0].content);
  64. }
  65. }, [defaultValue]);
  66. useEffect(() => {
  67. if (!isCollapse && selectRef.current) {
  68. const position = selectRef.current.getBoundingClientRect();
  69. if (optionRef.current) {
  70. optionRef.current.style.top = `${
  71. selectRef.current.clientHeight + position.top
  72. }px`;
  73. optionRef.current.style.left = `${position.left}px`;
  74. }
  75. }
  76. });
  77. return (
  78. <ClickAwayListener onClick={handleBlur}>
  79. <Container style={style}>
  80. <Selected
  81. ref={selectRef}
  82. onMouseDown={handleClick}
  83. onBlur={handleBlur}
  84. tabIndex={0}
  85. data-testid="selected"
  86. >
  87. {useInput &&
  88. (typeof value === 'string' || typeof value === 'number') ? (
  89. <InputContent
  90. value={value}
  91. onChange={handleChange}
  92. onKeyDown={handleKeyDown}
  93. />
  94. ) : (
  95. <Content>{value}</Content>
  96. )}
  97. {isDivide ? <Divider /> : null}
  98. <Icon glyph="down-arrow" />
  99. </Selected>
  100. {!isCollapse ? (
  101. <OptionWrapper ref={optionRef} data-testid="option-list">
  102. {options.map((option: SelectOptionType, index: number) => (
  103. <Option
  104. key={option.key}
  105. onMouseDown={(): void => {
  106. handleSelect(index);
  107. }}
  108. >
  109. {option.content}
  110. </Option>
  111. ))}
  112. </OptionWrapper>
  113. ) : null}
  114. </Container>
  115. </ClickAwayListener>
  116. );
  117. };
  118. SelectBox.defaultProps = {
  119. isDivide: false,
  120. useInput: false,
  121. style: {},
  122. defaultValue: '',
  123. };
  124. export default SelectBox;