Design system.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

263 rindas
6.7 KiB

  1. import * as React from 'react';
  2. import { TextControl, tailwind } from '@tesseract-design/web-base';
  3. import { useFallbackId } from '@modal-sh/react-utils';
  4. const { tw } = tailwind;
  5. const DateDropdownDerivedElementComponent = 'input';
  6. /**
  7. * Derived HTML element of the {@link DateDropdown} component.
  8. */
  9. export type DateDropdownDerivedElement = HTMLElementTagNameMap[
  10. typeof DateDropdownDerivedElementComponent
  11. ];
  12. /**
  13. * Props of the {@link DateDropdown} component.
  14. */
  15. export interface DateDropdownProps extends Omit<React.HTMLProps<DateDropdownDerivedElement>, 'size' | 'type' | 'label' | 'pattern'> {
  16. /**
  17. * Short textual description indicating the nature of the component's value.
  18. */
  19. label?: React.ReactNode,
  20. /**
  21. * Short textual description as guidelines for valid input values.
  22. */
  23. hint?: React.ReactNode,
  24. /**
  25. * Size of the component.
  26. */
  27. size?: TextControl.Size,
  28. /**
  29. * Should the component display a border?
  30. */
  31. border?: boolean,
  32. /**
  33. * Should the component occupy the whole width of its parent?
  34. */
  35. block?: boolean,
  36. /**
  37. * Style of the component.
  38. */
  39. variant?: TextControl.Variant,
  40. /**
  41. * Is the label hidden?
  42. */
  43. hiddenLabel?: boolean,
  44. /**
  45. * Visual length of the input.
  46. */
  47. length?: number,
  48. }
  49. export const dateDropdownPlugin: tailwind.PluginCreator = ({ addComponents }) => {
  50. addComponents({
  51. '.date-dropdown': {
  52. '& > input::-webkit-calendar-picker-indicator': {
  53. 'background-image': 'none',
  54. 'position': 'absolute',
  55. 'bottom': '0',
  56. 'right': '0',
  57. 'height': '100%',
  58. 'padding': '0',
  59. 'aspect-ratio': '1 / 1',
  60. 'cursor': 'inherit',
  61. },
  62. '&[data-size="small"] > input::-webkit-calendar-picker-indicator': {
  63. 'width': '2.5rem',
  64. },
  65. '&[data-size="medium"] > input::-webkit-calendar-picker-indicator': {
  66. 'width': '3rem',
  67. },
  68. '&[data-size="large"] > input::-webkit-calendar-picker-indicator': {
  69. 'width': '4rem',
  70. },
  71. },
  72. });
  73. };
  74. /**
  75. * Component for inputting date values.
  76. */
  77. export const DateDropdown = React.forwardRef<
  78. DateDropdownDerivedElement,
  79. DateDropdownProps
  80. >((
  81. {
  82. label,
  83. hint,
  84. size = 'medium' as const,
  85. border = false,
  86. block = false,
  87. variant = 'default' as const,
  88. hiddenLabel = false,
  89. className,
  90. id: idProp,
  91. style,
  92. length,
  93. ...etcProps
  94. }: DateDropdownProps,
  95. forwardedRef,
  96. ) => {
  97. const labelId = React.useId();
  98. const id = useFallbackId(idProp);
  99. return (
  100. <div
  101. className={tw(
  102. 'relative rounded ring-secondary/50 overflow-hidden group has-[:disabled]:opacity-50 date-dropdown',
  103. 'focus-within:ring-4',
  104. {
  105. 'block': block,
  106. 'inline-block align-middle': !block,
  107. },
  108. className,
  109. )}
  110. style={style}
  111. data-size={size}
  112. data-testid="base"
  113. >
  114. {label && (
  115. <>
  116. <label
  117. data-testid="label"
  118. id={labelId}
  119. htmlFor={id}
  120. className={tw(
  121. 'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold group-focus-within:text-secondary text-primary leading-none bg-negative select-none',
  122. {
  123. 'sr-only': hiddenLabel,
  124. },
  125. {
  126. 'pr-10': size === 'small',
  127. 'pr-12': size === 'medium',
  128. 'pr-16': size === 'large',
  129. },
  130. )}
  131. >
  132. <span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
  133. {label}
  134. </span>
  135. </label>
  136. {' '}
  137. </>
  138. )}
  139. <DateDropdownDerivedElementComponent
  140. {...etcProps}
  141. size={length}
  142. ref={forwardedRef}
  143. id={id}
  144. aria-labelledby={labelId}
  145. type="date"
  146. data-testid="input"
  147. pattern="\d{4}-\d{2}-\d{2}"
  148. className={tw(
  149. 'bg-negative rounded-inherit w-full peer block font-inherit tabular-nums cursor-pointer',
  150. 'focus:outline-0',
  151. 'disabled:cursor-not-allowed',
  152. {
  153. 'pl-4': variant === 'default',
  154. 'pl-1.5 pt-4': variant === 'alternate',
  155. },
  156. {
  157. 'pr-10 h-10 text-xxs': size === 'small',
  158. 'pr-12 h-12 text-xs': size === 'medium',
  159. 'pr-16 h-16': size === 'large',
  160. },
  161. )}
  162. />
  163. {hint && (
  164. <div
  165. data-testid="hint"
  166. className={tw(
  167. 'absolute left-0 px-1 pointer-events-none text-xxs leading-none w-full bg-negative select-none',
  168. {
  169. 'bottom-0 pl-4 pb-1': variant === 'default',
  170. 'top-0.5': variant === 'alternate',
  171. },
  172. {
  173. 'pt-2': variant === 'alternate' && size === 'small',
  174. 'pt-3': variant === 'alternate' && size !== 'small',
  175. },
  176. {
  177. 'pr-10': size === 'small',
  178. 'pr-12': size === 'medium',
  179. 'pr-16': size === 'large',
  180. },
  181. )}
  182. >
  183. <div
  184. className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis"
  185. >
  186. {hint}
  187. </div>
  188. </div>
  189. )}
  190. <div
  191. data-testid="indicator"
  192. className={tw(
  193. 'text-center flex items-center justify-center aspect-square absolute bottom-0 right-0 pointer-events-none select-none text-primary group-focus-within:text-secondary',
  194. {
  195. 'w-10': size === 'small',
  196. 'w-12': size === 'medium',
  197. 'w-16': size === 'large',
  198. },
  199. )}
  200. >
  201. <svg
  202. className="w-6 h-6 fill-none stroke-current stroke-2 linejoin-round linecap-round"
  203. viewBox="0 0 24 24"
  204. role="presentation"
  205. >
  206. <rect
  207. x="3"
  208. y="4"
  209. width="18"
  210. height="18"
  211. rx="2"
  212. ry="2"
  213. />
  214. <line
  215. x1="16"
  216. y1="2"
  217. x2="16"
  218. y2="6"
  219. />
  220. <line
  221. x1="8"
  222. y1="2"
  223. x2="8"
  224. y2="6"
  225. />
  226. <line
  227. x1="3"
  228. y1="10"
  229. x2="21"
  230. y2="10"
  231. />
  232. </svg>
  233. </div>
  234. {border && (
  235. <span
  236. data-testid="border"
  237. className="absolute z-[1] inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary"
  238. />
  239. )}
  240. </div>
  241. );
  242. });
  243. DateDropdown.displayName = 'DateDropdown';
  244. DateDropdown.defaultProps = {
  245. label: undefined,
  246. hint: undefined,
  247. size: 'medium',
  248. border: false,
  249. block: false,
  250. variant: 'default',
  251. hiddenLabel: false,
  252. length: undefined,
  253. };