258 lines
5.6 KiB
TypeScript
258 lines
5.6 KiB
TypeScript
import React from "react";
|
|
import { Flex, BoxProps } from "rebass/styled-components";
|
|
import styled, { css, keyframes } from "styled-components";
|
|
import { withProp, prop, ifProp } from "styled-tools";
|
|
import { FC } from "react";
|
|
|
|
import { Span } from "./Text";
|
|
|
|
interface StyledTextProps extends BoxProps {
|
|
autoFocus?: boolean;
|
|
name?: string;
|
|
id?: string;
|
|
type?: string;
|
|
value?: string;
|
|
required?: boolean;
|
|
onChange?: any;
|
|
placeholderSize?: number[];
|
|
br?: string;
|
|
bbw?: string;
|
|
autocomplete?: "on" | "off";
|
|
}
|
|
|
|
export const TextInput = styled(Flex).attrs({
|
|
as: "input"
|
|
})<StyledTextProps>`
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
letter-spacing: 0.05em;
|
|
color: #444;
|
|
background-color: white;
|
|
box-shadow: 0 10px 35px hsla(200, 15%, 70%, 0.2);
|
|
border: none;
|
|
border-radius: ${prop("br", "100px")};
|
|
border-bottom: 5px solid #f5f5f5;
|
|
border-bottom-width: ${prop("bbw", "5px")};
|
|
transition: all 0.5s ease-out;
|
|
|
|
:focus {
|
|
outline: none;
|
|
box-shadow: 0 20px 35px hsla(200, 15%, 70%, 0.4);
|
|
}
|
|
|
|
::placeholder {
|
|
font-size: ${withProp("placeholderSize", (s) => s[0] || 14)}px;
|
|
letter-spacing: 0.05em;
|
|
color: #888;
|
|
}
|
|
|
|
@media screen and (min-width: 64em) {
|
|
::placeholder {
|
|
font-size: ${withProp(
|
|
"placeholderSize",
|
|
(s) => s[3] || s[2] || s[1] || s[0] || 16
|
|
)}px;
|
|
}
|
|
}
|
|
|
|
@media screen and (min-width: 52em) {
|
|
letter-spacing: 0.1em;
|
|
border-bottom-width: ${prop("bbw", "6px")};
|
|
::placeholder {
|
|
font-size: ${withProp(
|
|
"placeholderSize",
|
|
(s) => s[2] || s[1] || s[0] || 15
|
|
)}px;
|
|
}
|
|
}
|
|
|
|
@media screen and (min-width: 40em) {
|
|
::placeholder {
|
|
font-size: ${withProp("placeholderSize", (s) => s[1] || s[0] || 15)}px;
|
|
}
|
|
}
|
|
`;
|
|
|
|
TextInput.defaultProps = {
|
|
value: "",
|
|
height: [40, 44],
|
|
py: 0,
|
|
px: [3, 24],
|
|
fontSize: [14, 15],
|
|
placeholderSize: [13, 14]
|
|
};
|
|
|
|
interface StyledSelectProps extends BoxProps {
|
|
name?: string;
|
|
id?: string;
|
|
type?: string;
|
|
value?: string;
|
|
required?: boolean;
|
|
onChange?: any;
|
|
br?: string;
|
|
bbw?: string;
|
|
}
|
|
|
|
interface SelectOptions extends StyledSelectProps {
|
|
options: Array<{ key: string; value: string | number }>;
|
|
}
|
|
|
|
const StyledSelect: FC<StyledSelectProps> = styled(Flex).attrs({
|
|
as: "select"
|
|
})<StyledSelectProps>`
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
letter-spacing: 0.05em;
|
|
color: #444;
|
|
background-color: white;
|
|
box-shadow: 0 10px 35px hsla(200, 15%, 70%, 0.2);
|
|
border: none;
|
|
border-radius: ${prop("br", "100px")};
|
|
border-bottom: 5px solid #f5f5f5;
|
|
border-bottom-width: ${prop("bbw", "5px")};
|
|
transition: all 0.5s ease-out;
|
|
appearance: none;
|
|
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");
|
|
background-repeat: no-repeat, repeat;
|
|
background-position: right 1.2em top 50%, 0 0;
|
|
background-size: 1em auto, 100%;
|
|
|
|
:focus {
|
|
outline: none;
|
|
box-shadow: 0 20px 35px hsla(200, 15%, 70%, 0.4);
|
|
}
|
|
|
|
@media screen and (min-width: 52em) {
|
|
letter-spacing: 0.1em;
|
|
border-bottom-width: ${prop("bbw", "6px")};
|
|
}
|
|
`;
|
|
|
|
export const Select: FC<SelectOptions> = ({ options, ...props }) => (
|
|
<StyledSelect {...props}>
|
|
{options.map(({ key, value }) => (
|
|
<option key={value} value={value}>
|
|
{key}
|
|
</option>
|
|
))}
|
|
</StyledSelect>
|
|
);
|
|
|
|
Select.defaultProps = {
|
|
value: "",
|
|
height: [40, 44],
|
|
py: 0,
|
|
px: [3, 24],
|
|
fontSize: [14, 15]
|
|
};
|
|
|
|
interface ChecknoxInputProps {
|
|
checked: boolean;
|
|
id?: string;
|
|
name: string;
|
|
onChange: any;
|
|
}
|
|
|
|
const CheckboxInput = styled(Flex).attrs({
|
|
as: "input",
|
|
type: "checkbox",
|
|
m: 0,
|
|
p: 0,
|
|
width: 0,
|
|
height: 0,
|
|
opacity: 0
|
|
})<ChecknoxInputProps>`
|
|
position: relative;
|
|
opacity: 0;
|
|
`;
|
|
|
|
const CheckboxBox = styled(Flex).attrs({
|
|
alignItems: "center",
|
|
justifyContent: "center"
|
|
})<{ checked: boolean }>`
|
|
position: relative;
|
|
transition: color 0.3s ease-out;
|
|
border-radius: 4px;
|
|
background-color: white;
|
|
box-shadow: 0 2px 4px rgba(50, 50, 50, 0.2);
|
|
cursor: pointer;
|
|
|
|
input:focus + & {
|
|
outline: 3px solid rgba(65, 164, 245, 0.5);
|
|
}
|
|
|
|
${ifProp(
|
|
"checked",
|
|
css`
|
|
box-shadow: 0 3px 5px rgba(50, 50, 50, 0.4);
|
|
|
|
:after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 80%;
|
|
height: 80%;
|
|
display: block;
|
|
border-radius: 2px;
|
|
background-color: #9575cd;
|
|
box-shadow: 0 2px 4px rgba(50, 50, 50, 0.2);
|
|
cursor: pointer;
|
|
animation: ${keyframes`
|
|
from {
|
|
opacity: 0;
|
|
transform: scale(0, 0);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scale(1, 1);
|
|
}
|
|
`} 0.1s ease-in;
|
|
}
|
|
`
|
|
)}
|
|
`;
|
|
|
|
interface CheckboxProps
|
|
extends ChecknoxInputProps,
|
|
Omit<BoxProps, "name" | "checked" | "onChange" | "value"> {
|
|
label: string;
|
|
value?: boolean | string;
|
|
}
|
|
|
|
export const Checkbox: FC<CheckboxProps> = ({
|
|
checked,
|
|
height,
|
|
id,
|
|
label,
|
|
name,
|
|
width,
|
|
onChange,
|
|
...rest
|
|
}) => {
|
|
return (
|
|
<Flex
|
|
flex="0 0 auto"
|
|
as="label"
|
|
alignItems="center"
|
|
style={{ cursor: "pointer" }}
|
|
{...(rest as any)}
|
|
>
|
|
<CheckboxInput
|
|
onChange={onChange}
|
|
name={name}
|
|
id={id}
|
|
checked={checked}
|
|
/>
|
|
<CheckboxBox checked={checked} width={width} height={height} />
|
|
<Span ml={[10, 12]} mt="1px" color="#555">
|
|
{label}
|
|
</Span>
|
|
</Flex>
|
|
);
|
|
};
|
|
|
|
Checkbox.defaultProps = {
|
|
width: [16, 18],
|
|
height: [16, 18],
|
|
fontSize: [15, 16]
|
|
};
|