|
1 | | -import React, { InputHTMLAttributes } from "react"; |
| 1 | +import React, { InputHTMLAttributes, useRef } from "react"; |
2 | 2 | import styled, { css } from "styled-components"; |
3 | 3 | import SuccessIcon from "../../assets/svgs/status-icons/success.svg"; |
4 | 4 | import WarningIcon from "../../assets/svgs/status-icons/warning.svg"; |
5 | 5 | import ErrorIcon from "../../assets/svgs/status-icons/error.svg"; |
6 | 6 | 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 | + |
7 | 10 | import { borderBox, small, svg } from "../../styles/common-style"; |
| 11 | +import { useHover } from "usehooks-ts"; |
8 | 12 |
|
9 | 13 | export type VariantProp = { |
10 | 14 | variant?: "success" | "warning" | "error" | string; |
@@ -80,14 +84,67 @@ const StyledInput = styled.input<{ |
80 | 84 | padding-top: 14px; |
81 | 85 | padding-bottom: 14px; |
82 | 86 | 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"; |
87 | 91 | }}; |
| 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; |
88 | 103 | ${baseInputStyle} |
89 | 104 | `; |
90 | 105 |
|
| 106 | +const ArrowsContainer = styled.div<{ |
| 107 | + variant?: string; |
| 108 | + Icon?: React.FC<React.SVGAttributes<SVGElement>>; |
| 109 | +}>` |
| 110 | + display: flex; |
| 111 | + flex-direction: column; |
| 112 | + align-items: center; |
| 113 | + position: absolute; |
| 114 | + height: 17px; |
| 115 | + width: 14px; |
| 116 | + top: 14px; |
| 117 | + right: ${({ Icon, variant }) => { |
| 118 | + if (Icon) return "48px"; |
| 119 | + if (variant) return "36px"; |
| 120 | + return "12px"; |
| 121 | + }}; |
| 122 | +`; |
| 123 | + |
| 124 | +const ArrowButton = styled.button` |
| 125 | + height: 10px; |
| 126 | + width: 100%; |
| 127 | + display: flex; |
| 128 | + justify-content: center; |
| 129 | + align-items: center; |
| 130 | + background: transparent; |
| 131 | + border: none; |
| 132 | + padding: 0; |
| 133 | +
|
| 134 | + cursor: pointer; |
| 135 | + :hover { |
| 136 | + background: ${(props) => props.theme.klerosUIComponentsStroke}; |
| 137 | + } |
| 138 | +`; |
| 139 | + |
| 140 | +const StyledArrowIcon = styled.svg` |
| 141 | + width: 8px; |
| 142 | + height: 8px; |
| 143 | + path { |
| 144 | + fill: ${(props) => props.theme.klerosUIComponentsSecondaryText}; |
| 145 | + } |
| 146 | +`; |
| 147 | + |
91 | 148 | const StyledSVG = styled.svg``; |
92 | 149 |
|
93 | 150 | export const StyledMessage = styled.small<VariantProp>` |
@@ -141,27 +198,49 @@ const Field: React.FC<FieldProps> = ({ |
141 | 198 | Icon, |
142 | 199 | className, |
143 | 200 | ...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 | | -); |
| 201 | +}) => { |
| 202 | + const wrapperRef = useRef<HTMLDivElement>(null); |
| 203 | + const inputRef = useRef<HTMLInputElement>(null); |
| 204 | + const hovering = useHover(wrapperRef); |
| 205 | + |
| 206 | + return ( |
| 207 | + <Wrapper ref={wrapperRef} {...{ className }}> |
| 208 | + <StyledInput ref={inputRef} {...{ variant, Icon, ...props }} /> |
| 209 | + {props.type === "number" && hovering && ( |
| 210 | + <ArrowsContainer className="field-arrows" {...{ variant, Icon }}> |
| 211 | + <ArrowButton |
| 212 | + aria-label="increment" |
| 213 | + onClick={() => inputRef?.current?.stepUp()} |
| 214 | + > |
| 215 | + <StyledArrowIcon as={UpArrowIcon} /> |
| 216 | + </ArrowButton> |
| 217 | + <ArrowButton aria-label="decrement"> |
| 218 | + <StyledArrowIcon |
| 219 | + as={DownArrowIcon} |
| 220 | + onClick={() => inputRef?.current?.stepDown()} |
| 221 | + /> |
| 222 | + </ArrowButton> |
| 223 | + </ArrowsContainer> |
| 224 | + )} |
| 225 | + {variant === "success" && <StyledSuccessIcon className="field-svg" />} |
| 226 | + {variant === "warning" && <StyledWarningIcon className="field-svg" />} |
| 227 | + {variant === "error" && <StyledErrorIcon className="field-svg" />} |
| 228 | + {Icon && ( |
| 229 | + <IconContainer> |
| 230 | + <StyledIconSVG as={Icon} /> |
| 231 | + </IconContainer> |
| 232 | + )} |
| 233 | + {message && ( |
| 234 | + <StyledMessage {...{ variant }}> |
| 235 | + {variant === "info" && ( |
| 236 | + <InfoIcon className={StyledSVG.styledComponentId} /> |
| 237 | + )} |
| 238 | + <StyledSmall {...{ variant }}>{message}</StyledSmall> |
| 239 | + </StyledMessage> |
| 240 | + )} |
| 241 | + </Wrapper> |
| 242 | + ); |
| 243 | +}; |
165 | 244 |
|
166 | 245 | Field.displayName = "Field"; |
167 | 246 |
|
|
0 commit comments