Design system.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

197 líneas
4.6 KiB

  1. import * as React from 'react';
  2. import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
  3. import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol';
  4. type RenderOptionsProps = {
  5. options: SelectControlBase.SelectOption[],
  6. optionComponent?: React.ElementType,
  7. optgroupComponent?: React.ElementType,
  8. level?: number,
  9. }
  10. const RenderOptions: React.VFC<RenderOptionsProps> = ({
  11. options,
  12. optionComponent: Option = 'option',
  13. optgroupComponent: Optgroup = 'optgroup',
  14. level = 0,
  15. }: RenderOptionsProps) => (
  16. <>
  17. {
  18. options.map((o) => {
  19. if (typeof o.value !== 'undefined') {
  20. return (
  21. <Option
  22. key={`${o.label}:${o.value.toString()}`}
  23. value={o.value}
  24. >
  25. {o.label}
  26. </Option>
  27. );
  28. }
  29. if (typeof o.children !== 'undefined') {
  30. if (level === 0) {
  31. return (
  32. <Optgroup
  33. key={o.label}
  34. label={o.label}
  35. >
  36. <RenderOptions
  37. options={o.children}
  38. optionComponent={Option}
  39. optgroupComponent={Optgroup}
  40. level={level + 1}
  41. />
  42. </Optgroup>
  43. );
  44. }
  45. return (
  46. <React.Fragment
  47. key={o.label}
  48. >
  49. <Option
  50. disabled
  51. >
  52. {o.label}
  53. </Option>
  54. <RenderOptions
  55. options={o.children}
  56. optionComponent={Option}
  57. optgroupComponent={Optgroup}
  58. level={level + 1}
  59. />
  60. </React.Fragment>
  61. );
  62. }
  63. return null;
  64. })
  65. }
  66. </>
  67. );
  68. export type MenuSelectProps = Omit<React.HTMLProps<HTMLSelectElement>, 'size' | 'style'> & {
  69. /**
  70. * Short textual description indicating the nature of the component's value.
  71. */
  72. label?: React.ReactNode,
  73. /**
  74. * Short textual description as guidelines for valid input values.
  75. */
  76. hint?: React.ReactNode,
  77. /**
  78. * Size of the component.
  79. */
  80. size?: TextControlBase.TextControlSize,
  81. /**
  82. * Additional description, usually graphical, indicating the nature of the component's value.
  83. */
  84. indicator?: React.ReactNode,
  85. /**
  86. * Should the component display a border?
  87. */
  88. border?: boolean,
  89. /**
  90. * Should the component occupy the whole width of its parent?
  91. */
  92. block?: boolean,
  93. /**
  94. * Style of the component.
  95. */
  96. style?: TextControlBase.TextControlStyle,
  97. /**
  98. * Is the label hidden?
  99. */
  100. hiddenLabel?: boolean,
  101. /**
  102. * Options available for the component's values.
  103. */
  104. options?: SelectControlBase.SelectOption[],
  105. }
  106. export const MenuSelect = React.forwardRef<HTMLSelectElement, MenuSelectProps>(({
  107. label = '',
  108. hint = '',
  109. indicator = null,
  110. size = TextControlBase.TextControlSize.MEDIUM,
  111. border = false,
  112. block = false,
  113. style = TextControlBase.TextControlStyle.DEFAULT,
  114. hiddenLabel = false,
  115. options = [],
  116. className: _className,
  117. placeholder: _placeholder,
  118. as: _as,
  119. ...etcProps
  120. }: MenuSelectProps, ref) => {
  121. const styleArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({
  122. block,
  123. border,
  124. size,
  125. indicator: true,
  126. style,
  127. resizable: true,
  128. predefinedValues: true,
  129. }), [block, border, size, style]);
  130. return (
  131. <div
  132. className={TextControlBase.Root(styleArgs)}
  133. >
  134. <select
  135. {...etcProps}
  136. className={TextControlBase.Input(styleArgs)}
  137. ref={ref}
  138. aria-label={label}
  139. style={{
  140. height: TextControlBase.MIN_HEIGHTS[size],
  141. }}
  142. size={2}
  143. data-testid="input"
  144. >
  145. <RenderOptions
  146. options={options}
  147. />
  148. </select>
  149. {
  150. border && (
  151. <span
  152. data-testid="border"
  153. />
  154. )
  155. }
  156. {
  157. label && !hiddenLabel && (
  158. <div
  159. data-testid="label"
  160. className={TextControlBase.LabelWrapper(styleArgs)}
  161. >
  162. {label}
  163. </div>
  164. )
  165. }
  166. {hint && (
  167. <div
  168. className={TextControlBase.HintWrapper(styleArgs)}
  169. data-testid="hint"
  170. >
  171. <div
  172. className={TextControlBase.Hint()}
  173. >
  174. {hint}
  175. </div>
  176. </div>
  177. )}
  178. {indicator && (
  179. <div
  180. className={TextControlBase.IndicatorWrapper(styleArgs)}
  181. >
  182. {indicator}
  183. </div>
  184. )}
  185. </div>
  186. );
  187. });
  188. MenuSelect.displayName = 'MenuSelect';