Input.tsx 5.5 KB

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