Input.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import React from "react";
  2. import { Flex, BoxProps } from "rebass/styled-components";
  3. import styled, { css, keyframes } from "styled-components";
  4. import { withProp, prop, ifProp } from "styled-tools";
  5. import { FC } from "react";
  6. import { Span } from "./Text";
  7. interface StyledTextProps extends BoxProps {
  8. autoFocus?: boolean;
  9. name?: string;
  10. id?: string;
  11. type?: string;
  12. value?: string;
  13. required?: boolean;
  14. onChange?: any;
  15. placeholderSize?: number[];
  16. br?: string;
  17. bbw?: string;
  18. autocomplete?: "on" | "off";
  19. }
  20. export const TextInput = styled(Flex).attrs({
  21. as: "input"
  22. })<StyledTextProps>`
  23. position: relative;
  24. box-sizing: border-box;
  25. letter-spacing: 0.05em;
  26. color: #444;
  27. background-color: white;
  28. box-shadow: 0 10px 35px hsla(200, 15%, 70%, 0.2);
  29. border: none;
  30. border-radius: ${prop("br", "100px")};
  31. border-bottom: 5px solid #f5f5f5;
  32. border-bottom-width: ${prop("bbw", "5px")};
  33. transition: all 0.5s ease-out;
  34. :focus {
  35. outline: none;
  36. box-shadow: 0 20px 35px hsla(200, 15%, 70%, 0.4);
  37. }
  38. ::placeholder {
  39. font-size: ${withProp("placeholderSize", (s) => s[0] || 14)}px;
  40. letter-spacing: 0.05em;
  41. color: #888;
  42. }
  43. @media screen and (min-width: 64em) {
  44. ::placeholder {
  45. font-size: ${withProp(
  46. "placeholderSize",
  47. (s) => s[3] || s[2] || s[1] || s[0] || 16
  48. )}px;
  49. }
  50. }
  51. @media screen and (min-width: 52em) {
  52. letter-spacing: 0.1em;
  53. border-bottom-width: ${prop("bbw", "6px")};
  54. ::placeholder {
  55. font-size: ${withProp(
  56. "placeholderSize",
  57. (s) => s[2] || s[1] || s[0] || 15
  58. )}px;
  59. }
  60. }
  61. @media screen and (min-width: 40em) {
  62. ::placeholder {
  63. font-size: ${withProp("placeholderSize", (s) => s[1] || s[0] || 15)}px;
  64. }
  65. }
  66. `;
  67. TextInput.defaultProps = {
  68. value: "",
  69. height: [40, 44],
  70. py: 0,
  71. px: [3, 24],
  72. fontSize: [14, 15],
  73. placeholderSize: [13, 14]
  74. };
  75. interface StyledSelectProps extends BoxProps {
  76. name?: string;
  77. id?: string;
  78. type?: string;
  79. value?: string;
  80. required?: boolean;
  81. onChange?: any;
  82. br?: string;
  83. bbw?: string;
  84. }
  85. interface SelectOptions extends StyledSelectProps {
  86. options: Array<{ key: string; value: string | number }>;
  87. }
  88. const StyledSelect: FC<StyledSelectProps> = styled(Flex).attrs({
  89. as: "select"
  90. })<StyledSelectProps>`
  91. position: relative;
  92. box-sizing: border-box;
  93. letter-spacing: 0.05em;
  94. color: #444;
  95. background-color: white;
  96. box-shadow: 0 10px 35px hsla(200, 15%, 70%, 0.2);
  97. border: none;
  98. border-radius: ${prop("br", "100px")};
  99. border-bottom: 5px solid #f5f5f5;
  100. border-bottom-width: ${prop("bbw", "5px")};
  101. transition: all 0.5s ease-out;
  102. appearance: none;
  103. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24' fill='none' stroke='%235c666b' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  104. background-repeat: no-repeat, repeat;
  105. background-position: right 1.2em top 50%, 0 0;
  106. background-size: 1em auto, 100%;
  107. :focus {
  108. outline: none;
  109. box-shadow: 0 20px 35px hsla(200, 15%, 70%, 0.4);
  110. }
  111. @media screen and (min-width: 52em) {
  112. letter-spacing: 0.1em;
  113. border-bottom-width: ${prop("bbw", "6px")};
  114. }
  115. `;
  116. export const Select: FC<SelectOptions> = ({ options, ...props }) => (
  117. <StyledSelect {...props}>
  118. {options.map(({ key, value }) => (
  119. <option key={value} value={value}>
  120. {key}
  121. </option>
  122. ))}
  123. </StyledSelect>
  124. );
  125. Select.defaultProps = {
  126. value: "",
  127. height: [40, 44],
  128. py: 0,
  129. px: [3, 24],
  130. fontSize: [14, 15]
  131. };
  132. interface ChecknoxInputProps {
  133. checked: boolean;
  134. id?: string;
  135. name: string;
  136. onChange: any;
  137. }
  138. const CheckboxInput = styled(Flex).attrs({
  139. as: "input",
  140. type: "checkbox",
  141. m: 0,
  142. p: 0,
  143. width: 0,
  144. height: 0,
  145. opacity: 0
  146. })<ChecknoxInputProps>`
  147. position: relative;
  148. opacity: 0;
  149. `;
  150. const CheckboxBox = styled(Flex).attrs({
  151. alignItems: "center",
  152. justifyContent: "center"
  153. })<{ checked: boolean }>`
  154. position: relative;
  155. transition: color 0.3s ease-out;
  156. border-radius: 4px;
  157. background-color: white;
  158. box-shadow: 0 2px 4px rgba(50, 50, 50, 0.2);
  159. cursor: pointer;
  160. input:focus + & {
  161. outline: 3px solid rgba(65, 164, 245, 0.5);
  162. }
  163. ${ifProp(
  164. "checked",
  165. css`
  166. box-shadow: 0 3px 5px rgba(50, 50, 50, 0.4);
  167. :after {
  168. content: "";
  169. position: absolute;
  170. width: 80%;
  171. height: 80%;
  172. display: block;
  173. border-radius: 2px;
  174. background-color: #9575cd;
  175. box-shadow: 0 2px 4px rgba(50, 50, 50, 0.2);
  176. cursor: pointer;
  177. animation: ${keyframes`
  178. from {
  179. opacity: 0;
  180. transform: scale(0, 0);
  181. }
  182. to {
  183. opacity: 1;
  184. transform: scale(1, 1);
  185. }
  186. `} 0.1s ease-in;
  187. }
  188. `
  189. )}
  190. `;
  191. interface CheckboxProps
  192. extends ChecknoxInputProps,
  193. Omit<BoxProps, "name" | "checked" | "onChange" | "value"> {
  194. label: string;
  195. value?: boolean | string;
  196. }
  197. export const Checkbox: FC<CheckboxProps> = ({
  198. checked,
  199. height,
  200. id,
  201. label,
  202. name,
  203. width,
  204. onChange,
  205. ...rest
  206. }) => {
  207. return (
  208. <Flex
  209. flex="0 0 auto"
  210. as="label"
  211. alignItems="center"
  212. style={{ cursor: "pointer" }}
  213. {...(rest as any)}
  214. >
  215. <CheckboxInput
  216. onChange={onChange}
  217. name={name}
  218. id={id}
  219. checked={checked}
  220. />
  221. <CheckboxBox checked={checked} width={width} height={height} />
  222. <Span ml={[10, 12]} mt="1px" color="#555">
  223. {label}
  224. </Span>
  225. </Flex>
  226. );
  227. };
  228. Checkbox.defaultProps = {
  229. width: [16, 18],
  230. height: [16, 18],
  231. fontSize: [15, 16]
  232. };