Design system.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

197 linhas
5.7 KiB

  1. import * as React from 'react';
  2. import { TextControl } from '@tesseract-design/web-base';
  3. import clsx from 'clsx';
  4. export type PhoneNumberInputDerivedElement = HTMLInputElement;
  5. export interface PhoneNumberInputProps extends Omit<React.HTMLProps<PhoneNumberInputDerivedElement>, 'size' | 'type' | 'label'> {
  6. /**
  7. * Short textual description indicating the nature of the component's value.
  8. */
  9. label?: React.ReactNode,
  10. /**
  11. * Short textual description as guidelines for valid input values.
  12. */
  13. hint?: React.ReactNode,
  14. /**
  15. * Size of the component.
  16. */
  17. size?: TextControl.Size,
  18. /**
  19. * Additional description, usually graphical, indicating the nature of the component's value.
  20. */
  21. indicator?: React.ReactNode,
  22. /**
  23. * Should the component display a border?
  24. */
  25. border?: boolean,
  26. /**
  27. * Should the component occupy the whole width of its parent?
  28. */
  29. block?: boolean,
  30. /**
  31. * Style of the component.
  32. */
  33. variant?: TextControl.Variant,
  34. /**
  35. * Is the label hidden?
  36. */
  37. hiddenLabel?: boolean,
  38. }
  39. /**
  40. * Component for inputting textual values.
  41. */
  42. export const PhoneNumberInput = React.forwardRef<PhoneNumberInputDerivedElement, PhoneNumberInputProps>(
  43. (
  44. {
  45. label,
  46. hint,
  47. indicator,
  48. size = 'medium' as const,
  49. border = false,
  50. block = false,
  51. variant = 'default' as const,
  52. hiddenLabel = false,
  53. className,
  54. ...etcProps
  55. }: PhoneNumberInputProps,
  56. ref,
  57. ) => {
  58. const labelId = React.useId();
  59. return (
  60. <div
  61. className={clsx(
  62. 'relative rounded ring-secondary/50',
  63. 'focus-within:ring-4',
  64. {
  65. 'block': block,
  66. 'inline-block align-middle': !block,
  67. },
  68. className,
  69. )}
  70. >
  71. <input
  72. {...etcProps}
  73. ref={ref}
  74. aria-labelledby={labelId}
  75. type="tel"
  76. data-testid="input"
  77. className={clsx(
  78. 'bg-negative rounded-inherit w-full peer block',
  79. 'focus:outline-0',
  80. 'disabled:opacity-50 disabled:cursor-not-allowed',
  81. {
  82. 'text-xxs': size === 'small',
  83. 'text-xs': size === 'medium',
  84. },
  85. {
  86. 'pl-4': variant === 'default',
  87. 'pl-1.5': variant === 'alternate',
  88. },
  89. {
  90. 'pt-4': variant === 'alternate',
  91. },
  92. {
  93. 'pr-4': variant === 'default' && !indicator,
  94. 'pr-1.5': variant === 'alternate' && !indicator,
  95. },
  96. {
  97. 'pr-10': indicator && size === 'small',
  98. 'pr-12': indicator && size === 'medium',
  99. 'pr-16': indicator && size === 'large',
  100. },
  101. {
  102. 'h-10': size === 'small',
  103. 'h-12': size === 'medium',
  104. 'h-16': size === 'large',
  105. },
  106. )}
  107. />
  108. {
  109. label && (
  110. <label
  111. data-testid="label"
  112. id={labelId}
  113. className={clsx(
  114. 'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative',
  115. {
  116. 'sr-only': hiddenLabel,
  117. },
  118. {
  119. 'pr-1': !indicator,
  120. },
  121. {
  122. 'pr-10': indicator && size === 'small',
  123. 'pr-12': indicator && size === 'medium',
  124. 'pr-16': indicator && size === 'large',
  125. },
  126. )}
  127. >
  128. <span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
  129. {label}
  130. </span>
  131. </label>
  132. )
  133. }
  134. {hint && (
  135. <div
  136. data-testid="hint"
  137. className={clsx(
  138. 'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative',
  139. {
  140. 'bottom-0 pl-4 pb-1': variant === 'default',
  141. 'top-0.5': variant === 'alternate',
  142. },
  143. {
  144. 'pt-2': variant === 'alternate' && size === 'small',
  145. 'pt-3': variant === 'alternate' && size !== 'small',
  146. },
  147. {
  148. 'pr-4': !indicator && variant === 'default',
  149. 'pr-1': !indicator && variant === 'alternate',
  150. },
  151. {
  152. 'pr-10': indicator && size === 'small',
  153. 'pr-12': indicator && size === 'medium',
  154. 'pr-16': indicator && size === 'large',
  155. },
  156. )}
  157. >
  158. <div
  159. className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis"
  160. >
  161. {hint}
  162. </div>
  163. </div>
  164. )}
  165. {indicator && (
  166. <div
  167. className={clsx(
  168. 'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none',
  169. {
  170. 'w-10': size === 'small',
  171. 'w-12': size === 'medium',
  172. 'w-16': size === 'large',
  173. },
  174. )}
  175. >
  176. {indicator}
  177. </div>
  178. )}
  179. {
  180. border && (
  181. <span
  182. data-testid="border"
  183. className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary"
  184. />
  185. )
  186. }
  187. </div>
  188. );
  189. },
  190. );
  191. PhoneNumberInput.displayName = 'PhoneNumberInput';