diff --git a/app/components/NumberInput.tsx b/app/components/NumberInput.tsx index 0dfb0fd..e0c34de 100644 --- a/app/components/NumberInput.tsx +++ b/app/components/NumberInput.tsx @@ -1,16 +1,232 @@ "use client"; +import { ChangeEvent, KeyboardEvent, useState } from "react"; import styled from "styled-components"; -// TODO: Add props for NumberInput type NumberInputProps = { - propName: string; // replace string with actual prop type + defaultValue?: number; + disabled?: boolean; + label?: string; }; -// TODO: implement NumberInput component -export default function NumberInput({}: NumberInputProps) { - return ; +export default function NumberInput({ + defaultValue = 0, + disabled = false, + label, +}: NumberInputProps) { + const [val, setVal] = useState(defaultValue); + + // can change these for future purposes + const step = 1; + const min = 0; + const max = 9; + + const handleIncrement = () => { + setVal(Math.min(max, val + step)); + }; + + const handleDecrement = () => { + setVal(Math.max(min, val - step)); + }; + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Enter") { + event.currentTarget.blur(); + return; + } + }; + + const handleChange = (event: ChangeEvent) => { + const input = event.target as HTMLInputElement; + // if the field is empty + if (Number.isNaN(input.valueAsNumber)) { + setVal(min); + return; + } + const update = Math.max(min, Math.min(max, input.valueAsNumber)); + setVal(update); + input.value = String(update); + }; + + return ( + + + + + + + arrow_drop_up + + + arrow_drop_down + + + + + ); } +const NumberInputWrapper = styled.div` + --s: 1; + + @media (max-width: 768px) { + --s: 0.75; + } + + @media (max-width: 600px) { + --s: 0.5; + } + + @media (max-width: 360px) { + --s: 0.3; + } + + display: inline-block; + transform: scale(var(--s)); + transform-origin: top left; + + width: calc(52px / var(--s)); + height: calc(36px / var(--s)); +`; + const StyledNumberInput = styled.div` - /* TODO: Add styles for NumberInput */ + --border: #525252; + --bg: #ffffff; + --bg-hover: #f2f2f2; + --bg-focus: #e9e9e9; + --chevron: #525252; + + position: relative; + width: 52px; + height: 36px; + + display: grid; + grid-template-columns: 1fr 20px; + column-gap: 8px; + + border: 2px solid var(--border); + border-radius: 4px; + background: var(--bg); + box-sizing: border-box; + + padding-left: 8px; + padding-right: 2px; + overflow: hidden; + transition: + background 120ms ease, + border-color 120ms ease; + + &:focus-within { + background: var(--bg-focus); + } + + &[data-disabled="true"] { + --border: #cfcfcf; + --bg: #efefef; + --chevron: #cfcfcf; + } +`; + +const StyledInput = styled.input` + grid-column: 1 / 2; + grid-row: 1 / 2; + + width: 100%; + border: none; + outline: none; + background: transparent; + text-align: left; + + font-family: "Inter", sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 20px; + color: var(--Black, #000); + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + -moz-appearance: textfield; + + &:disabled { + color: #9e9e9e; + } +`; + +const MaterialIcon = styled.span` + font-family: "Material Symbols Rounded"; + font-weight: normal; + font-style: normal; + font-size: 16px; + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--chevron); + user-select: none; +`; + +const StyledNumberArrows = styled.div` + grid-column: 2 / 3; + grid-row: 1 / 2; + width: 20px; + height: 20px; + align-self: center; + position: relative; +`; + +const ArrowButton = styled.button<{ $dir: "up" | "down" }>` + position: absolute; + left: 0; + width: 100%; + border: none; + background: transparent; + cursor: pointer; + padding: 0; + transition: opacity 100ms ease; + + display: flex; + align-items: center; + justify-content: center; + + ${({ $dir }) => + $dir === "up" + ? ` + top: 0; + height: calc(50% - 2px); + ` + : ` + bottom: 0; + height: calc(50% - 2px); + `} + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } `;