All files / src/components/ui/form CoordinateInput.tsx

0% Statements 0/23
0% Branches 0/10
0% Functions 0/6
0% Lines 0/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99                                                                                                                                                                                                     
import React, { FC, useCallback, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { StyledInput as StyledInputDefault } from "./style";
 
export interface CoordinateInputProps {
  coordinate: "x" | "y" | "w" | "h";
  name: string;
  value?: number;
  min?: number;
  max?: number;
  step?: number;
  placeholder?: string;
  disabled?: boolean;
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
 
const Wrapper = styled.div`
  position: relative;
  width: 100%;
`;
 
const StyledInput = styled(StyledInputDefault)`
  padding-left: 32px;
`;
 
const Label = styled.label`
  position: absolute;
  top: 1px;
  left: 0px;
  bottom: 1px;
  width: 27px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-weight: bold;
  font-size: 11px;
  color: #999;
  text-transform: uppercase;
  border-right: 1px solid ${(props) => props.theme.colors.input.border};
`;
 
const valueToString = (value: unknown) =>
  value !== undefined && value !== null ? String(value) : "";
 
export const CoordinateInput: FC<CoordinateInputProps> = ({
  name,
  coordinate = "x",
  value,
  min,
  max,
  step,
  placeholder,
  disabled,
  onChange,
}) => {
  const inputRef = useRef<HTMLInputElement>(null);
  const [stringValue, setStringValue] = useState(valueToString(value));
 
  const onChangeInternal = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const newStringValue = e.currentTarget.value;
      setStringValue(newStringValue);
      onChange?.(e);
    },
    [onChange]
  );
 
  const onBlurInternal = useCallback(() => {
    setStringValue(valueToString(value));
  }, [value]);
 
  useEffect(() => {
    Iif (document.activeElement !== inputRef.current) {
      setStringValue(valueToString(value));
    }
  }, [value]);
 
  return (
    <Wrapper>
      <StyledInput
        ref={inputRef}
        id={name}
        name={name}
        type="number"
        value={stringValue}
        min={min}
        max={max}
        step={step}
        placeholder={placeholder}
        disabled={disabled}
        onChange={onChangeInternal}
        onBlur={onBlurInternal}
      />
      <Label htmlFor={name}>{coordinate}</Label>
    </Wrapper>
  );
};