您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

Select.tsx 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import { Theme } from '@mui/material';
  2. import React, { ChangeEvent } from 'react';
  3. import { makeStyles } from 'tss-react/mui';
  4. import { isMobileBrowser } from '../../../environment/utils';
  5. import Icon from '../../../icons/components/Icon';
  6. import { IconArrowDown } from '../../../icons/svg';
  7. import { withPixelLineHeight } from '../../../styles/functions.web';
  8. interface SelectProps {
  9. /**
  10. * Helper text to be displayed below the select.
  11. */
  12. bottomLabel?: string;
  13. /**
  14. * Class name for additional styles.
  15. */
  16. className?: string;
  17. /**
  18. * Wether or not the select is disabled.
  19. */
  20. disabled?: boolean;
  21. /**
  22. * Wether or not the select is in the error state.
  23. */
  24. error?: boolean;
  25. /**
  26. * Label to be displayed above the select.
  27. */
  28. label?: string;
  29. /**
  30. * Change handler.
  31. */
  32. onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
  33. /**
  34. * The options of the select.
  35. */
  36. options: Array<{
  37. label: string;
  38. value: number | string;
  39. }>;
  40. /**
  41. * The value of the select.
  42. */
  43. value: number | string;
  44. }
  45. const useStyles = makeStyles()((theme: Theme) => {
  46. return {
  47. container: {
  48. display: 'flex',
  49. flexDirection: 'column'
  50. },
  51. label: {
  52. color: theme.palette.text01,
  53. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  54. marginBottom: theme.spacing(2),
  55. '&.is-mobile': {
  56. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
  57. }
  58. },
  59. selectContainer: {
  60. position: 'relative'
  61. },
  62. select: {
  63. backgroundColor: theme.palette.ui03,
  64. borderRadius: `${theme.shape.borderRadius}px`,
  65. width: '100%',
  66. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  67. color: theme.palette.text01,
  68. padding: '8px 16px',
  69. paddingRight: '42px',
  70. border: 0,
  71. appearance: 'none',
  72. overflow: 'hidden',
  73. whiteSpace: 'nowrap',
  74. textOverflow: 'ellipsis',
  75. '&:focus': {
  76. outline: 0,
  77. boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
  78. },
  79. '&:disabled': {
  80. color: theme.palette.text03
  81. },
  82. '&.is-mobile': {
  83. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
  84. padding: '12px 16px',
  85. paddingRight: '46px'
  86. },
  87. '&.error': {
  88. boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
  89. }
  90. },
  91. icon: {
  92. position: 'absolute',
  93. top: '8px',
  94. right: '8px',
  95. pointerEvents: 'none',
  96. '&.is-mobile': {
  97. top: '12px',
  98. right: '12px'
  99. }
  100. },
  101. bottomLabel: {
  102. marginTop: theme.spacing(2),
  103. ...withPixelLineHeight(theme.typography.labelRegular),
  104. color: theme.palette.text02,
  105. '&.is-mobile': {
  106. ...withPixelLineHeight(theme.typography.bodyShortRegular)
  107. },
  108. '&.error': {
  109. color: theme.palette.textError
  110. }
  111. }
  112. };
  113. });
  114. const Select = ({
  115. bottomLabel,
  116. className,
  117. disabled,
  118. error,
  119. label,
  120. onChange,
  121. options,
  122. value }: SelectProps) => {
  123. const { classes, cx, theme } = useStyles();
  124. const isMobile = isMobileBrowser();
  125. return (
  126. <div className = { classes.container }>
  127. {label && <span className = { cx(classes.label, isMobile && 'is-mobile') }>{label}</span>}
  128. <div className = { classes.selectContainer }>
  129. <select
  130. className = { cx(classes.select, isMobile && 'is-mobile', className, error && 'error') }
  131. disabled = { disabled }
  132. onChange = { onChange }
  133. value = { value }>
  134. {options.map(option => (<option
  135. key = { option.value }
  136. value = { option.value }>{option.label}</option>))}
  137. </select>
  138. <Icon
  139. className = { cx(classes.icon, isMobile && 'is-mobile') }
  140. color = { disabled ? theme.palette.icon03 : theme.palette.icon01 }
  141. size = { 22 }
  142. src = { IconArrowDown } />
  143. </div>
  144. {bottomLabel && (
  145. <span className = { cx(classes.bottomLabel, isMobile && 'is-mobile', error && 'error') }>
  146. {bottomLabel}
  147. </span>
  148. )}
  149. </div>
  150. );
  151. };
  152. export default Select;