Skip to content

Commit c900809

Browse files
authored
Merge pull request #55 from kleros/feat/number-field-custom-counter-arrows
feat: custom-counter-arrows-in-number-field
2 parents 2fb36bb + 8e9612b commit c900809

File tree

4 files changed

+160
-26
lines changed

4 files changed

+160
-26
lines changed
Lines changed: 25 additions & 0 deletions
Loading
Lines changed: 24 additions & 0 deletions
Loading

src/examples/form.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Searchbar from "../lib/form/searchbar";
55
import Textarea from "../lib/form/textarea";
66
import Slider from "../lib/form/slider";
77
import Datepicker from "../lib/form/datepicker";
8+
import Telegram from "../assets/svgs/telegram.svg";
89

910
const Form = () => {
1011
const [value, setValue] = useState(1);
@@ -26,6 +27,8 @@ const Form = () => {
2627
variant="success"
2728
message="success msg"
2829
/>
30+
<Field placeholder={"Number"} type="number" Icon={Telegram} />
31+
<Field placeholder={"Number"} type="number" />
2932
<Searchbar />
3033
<Textarea
3134
placeholder={"eg. longer text"}

src/lib/form/field.tsx

Lines changed: 108 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import React, { InputHTMLAttributes } from "react";
1+
import React, { InputHTMLAttributes, useRef } from "react";
22
import styled, { css } from "styled-components";
33
import SuccessIcon from "../../assets/svgs/status-icons/success.svg";
44
import WarningIcon from "../../assets/svgs/status-icons/warning.svg";
55
import ErrorIcon from "../../assets/svgs/status-icons/error.svg";
66
import InfoIcon from "../../assets/svgs/status-icons/info.svg";
7+
import UpArrowIcon from "../../assets/svgs/arrows/field-arrow-up.svg";
8+
import DownArrowIcon from "../../assets/svgs/arrows/field-arrow-down.svg";
9+
710
import { borderBox, small, svg } from "../../styles/common-style";
11+
import { useHover } from "usehooks-ts";
812

913
export type VariantProp = {
1014
variant?: "success" | "warning" | "error" | string;
@@ -80,14 +84,70 @@ const StyledInput = styled.input<{
8084
padding-top: 14px;
8185
padding-bottom: 14px;
8286
padding-left: 16px;
83-
padding-right: ${({ Icon, variant }) => {
84-
if (Icon) return "56px";
85-
if (variant) return "44px";
86-
return "16px";
87+
padding-right: ${({ Icon, variant, type }) => {
88+
if (Icon) return type === "number" ? "64px" : "56px";
89+
if (variant) return type === "number" ? "52px" : "44px";
90+
return type === "number" ? "30px" : "16px";
8791
}};
92+
93+
/* Chrome, Safari, Edge, Opera */
94+
::-webkit-outer-spin-button,
95+
::-webkit-inner-spin-button {
96+
-webkit-appearance: none;
97+
margin: 0;
98+
}
99+
100+
/* Firefox */
101+
-moz-appearance: textfield;
102+
appearance: textfield;
88103
${baseInputStyle}
89104
`;
90105

106+
const ArrowsContainer = styled.div<{
107+
variant?: string;
108+
Icon?: React.FC<React.SVGAttributes<SVGElement>>;
109+
}>`
110+
display: flex;
111+
flex-direction: column;
112+
justify-content: center;
113+
align-items: center;
114+
position: absolute;
115+
height: 100%;
116+
width: 14px;
117+
gap: 4px;
118+
top: 0;
119+
right: ${({ Icon, variant }) => {
120+
if (Icon) return "48px";
121+
if (variant) return "36px";
122+
return "12px";
123+
}};
124+
`;
125+
126+
const ArrowButton = styled.button`
127+
height: 14px;
128+
width: 14px;
129+
display: flex;
130+
justify-content: center;
131+
align-items: center;
132+
background: transparent;
133+
border: none;
134+
border-radius: 3px;
135+
padding: 0;
136+
137+
cursor: pointer;
138+
:hover {
139+
background: ${(props) => props.theme.klerosUIComponentsStroke};
140+
}
141+
`;
142+
143+
const StyledArrowIcon = styled.svg`
144+
width: 8px;
145+
height: 8px;
146+
path {
147+
fill: ${(props) => props.theme.klerosUIComponentsSecondaryText};
148+
}
149+
`;
150+
91151
const StyledSVG = styled.svg``;
92152

93153
export const StyledMessage = styled.small<VariantProp>`
@@ -141,27 +201,49 @@ const Field: React.FC<FieldProps> = ({
141201
Icon,
142202
className,
143203
...props
144-
}) => (
145-
<Wrapper {...{ className }}>
146-
<StyledInput {...{ variant, Icon, ...props }} />
147-
{variant === "success" && <StyledSuccessIcon className="field-svg" />}
148-
{variant === "warning" && <StyledWarningIcon className="field-svg" />}
149-
{variant === "error" && <StyledErrorIcon className="field-svg" />}
150-
{Icon && (
151-
<IconContainer>
152-
<StyledIconSVG as={Icon} />
153-
</IconContainer>
154-
)}
155-
{message && (
156-
<StyledMessage {...{ variant }}>
157-
{variant === "info" && (
158-
<InfoIcon className={StyledSVG.styledComponentId} />
159-
)}
160-
<StyledSmall {...{ variant }}>{message}</StyledSmall>
161-
</StyledMessage>
162-
)}
163-
</Wrapper>
164-
);
204+
}) => {
205+
const wrapperRef = useRef<HTMLDivElement>(null);
206+
const inputRef = useRef<HTMLInputElement>(null);
207+
const hovering = useHover(wrapperRef);
208+
209+
return (
210+
<Wrapper ref={wrapperRef} {...{ className }}>
211+
<StyledInput ref={inputRef} {...{ variant, Icon, ...props }} />
212+
{props.type === "number" && hovering && (
213+
<ArrowsContainer className="field-arrows" {...{ variant, Icon }}>
214+
<ArrowButton
215+
aria-label="increment"
216+
onClick={() => inputRef?.current?.stepUp()}
217+
>
218+
<StyledArrowIcon as={UpArrowIcon} />
219+
</ArrowButton>
220+
<ArrowButton
221+
aria-label="decrement"
222+
onClick={() => inputRef?.current?.stepDown()}
223+
>
224+
<StyledArrowIcon as={DownArrowIcon} />
225+
</ArrowButton>
226+
</ArrowsContainer>
227+
)}
228+
{variant === "success" && <StyledSuccessIcon className="field-svg" />}
229+
{variant === "warning" && <StyledWarningIcon className="field-svg" />}
230+
{variant === "error" && <StyledErrorIcon className="field-svg" />}
231+
{Icon && (
232+
<IconContainer>
233+
<StyledIconSVG as={Icon} />
234+
</IconContainer>
235+
)}
236+
{message && (
237+
<StyledMessage {...{ variant }}>
238+
{variant === "info" && (
239+
<InfoIcon className={StyledSVG.styledComponentId} />
240+
)}
241+
<StyledSmall {...{ variant }}>{message}</StyledSmall>
242+
</StyledMessage>
243+
)}
244+
</Wrapper>
245+
);
246+
};
165247

166248
Field.displayName = "Field";
167249

0 commit comments

Comments
 (0)