react95
Version:
Refreshed Windows95 UI components for modern web apps - React95
115 lines (110 loc) • 4.26 kB
JavaScript
import React__default, { forwardRef, useCallback } from 'react';
import styled, { css } from 'styled-components';
import { Button } from '../Button/Button.mjs';
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled.mjs';
import { blockSizes } from '../common/system.mjs';
import { clamp, getSize } from '../common/utils/index.mjs';
import { TextInput } from '../TextInput/TextInput.mjs';
const StyledNumberInputWrapper = styled.div`
display: inline-flex;
align-items: center;
`;
const StyledButton = styled(Button)`
width: 30px;
padding: 0;
flex-shrink: 0;
${({ variant }) => variant === "flat" ? css`
height: calc(50% - 1px);
` : css`
height: 50%;
`}
`;
const StyledButtonWrapper = styled.div`
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: space-between;
${({ variant }) => variant === "flat" ? css`
height: calc(${blockSizes.md} - 4px);
` : css`
height: ${blockSizes.md};
margin-left: 2px;
`}
`;
const StyledButtonIcon = styled.span`
width: 0px;
height: 0px;
display: inline-block;
${({ invert }) => invert ? css`
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid ${({ theme }) => theme.materialText};
` : css`
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid ${({ theme }) => theme.materialText};
`}
${StyledButton}:disabled & {
filter: drop-shadow(
1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow}
);
${({ invert }) => invert ? css`
border-bottom-color: ${({ theme }) => theme.materialTextDisabled};
` : css`
border-top-color: ${({ theme }) => theme.materialTextDisabled};
`}
}
`;
const NumberInput = forwardRef(({ className, defaultValue, disabled = false, max, min, onChange, readOnly, step = 1, style, value, variant = "default", width, ...otherProps }, ref) => {
const [valueDerived, setValueState] = useControlledOrUncontrolled({
defaultValue,
onChange,
readOnly,
value
});
const handleInputChange = useCallback((e) => {
const newValue = parseFloat(e.target.value);
setValueState(newValue);
}, [setValueState]);
const handleClick = useCallback((delta) => {
const newValue = clamp(parseFloat(((valueDerived !== null && valueDerived !== void 0 ? valueDerived : 0) + delta).toFixed(2)), min !== null && min !== void 0 ? min : null, max !== null && max !== void 0 ? max : null);
setValueState(newValue);
onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
}, [max, min, onChange, setValueState, valueDerived]);
const onBlur = useCallback(() => {
if (valueDerived !== void 0) {
onChange === null || onChange === void 0 ? void 0 : onChange(valueDerived);
}
}, [onChange, valueDerived]);
const stepUp = useCallback(() => {
handleClick(step);
}, [handleClick, step]);
const stepDown = useCallback(() => {
handleClick(-step);
}, [handleClick, step]);
const buttonVariant = variant === "flat" ? "flat" : "raised";
return React__default.createElement(
StyledNumberInputWrapper,
{ className, style: {
...style,
width: width !== void 0 ? getSize(width) : "auto"
}, ...otherProps },
React__default.createElement(TextInput, { value: valueDerived, variant, onChange: handleInputChange, disabled, type: "number", readOnly, ref, fullWidth: true, onBlur }),
React__default.createElement(
StyledButtonWrapper,
{ variant },
React__default.createElement(
StyledButton,
{ "data-testid": "increment", variant: buttonVariant, disabled: disabled || readOnly, onClick: stepUp },
React__default.createElement(StyledButtonIcon, { invert: true })
),
React__default.createElement(
StyledButton,
{ "data-testid": "decrement", variant: buttonVariant, disabled: disabled || readOnly, onClick: stepDown },
React__default.createElement(StyledButtonIcon, null)
)
)
);
});
NumberInput.displayName = "NumberInput";
export { NumberInput };