Files
DevToysWeb/src/components/pages/converters/number-base/Content.tsx
2023-05-28 23:55:49 +09:00

146 lines
3.6 KiB
TypeScript

import { Stack } from "@mui/material";
import { ComponentPropsWithoutRef, memo, useCallback, useState } from "react";
import { Main, MainItem, TextField } from "@/components/common";
import {
formatBinary,
formatDecimal,
formatHexadecimal,
formatOctal,
isBinary,
isDecimal,
isHexadecimal,
isOctal,
unformatBinary,
unformatDecimal,
unformatHexadecimal,
unformatOctal,
} from "@/libs/string";
import Configuration from "./Configuration";
type TextFieldValue = ComponentPropsWithoutRef<typeof TextField>["value"];
type OnTextFieldChange = NonNullable<ComponentPropsWithoutRef<typeof TextField>["onChange"]>;
type Props = {
dec: TextFieldValue;
hex: TextFieldValue;
oct: TextFieldValue;
bin: TextFieldValue;
onDecChange: OnTextFieldChange;
onHexChange: OnTextFieldChange;
onOctChange: OnTextFieldChange;
onBinChange: OnTextFieldChange;
} & ComponentPropsWithoutRef<typeof Configuration>;
const StyledComponent = ({
format,
dec,
hex,
oct,
bin,
onFormatChange,
onDecChange,
onHexChange,
onOctChange,
onBinChange,
}: Props) => (
<Main title="Number Base Converter">
<MainItem title="Configuration">
<Configuration {...{ format, onFormatChange }} />
</MainItem>
<Stack spacing={1}>
<MainItem title="Decimal">
<TextField value={dec} onChange={onDecChange} />
</MainItem>
<MainItem title="Hexadecimal">
<TextField value={hex} onChange={onHexChange} />
</MainItem>
<MainItem title="Octal">
<TextField value={oct} onChange={onOctChange} />
</MainItem>
<MainItem title="Binary">
<TextField value={bin} onChange={onBinChange} />
</MainItem>
</Stack>
</Main>
);
export const Component = memo(StyledComponent);
const Container = () => {
const [format, setFormat] = useState(false);
const [int, setInt] = useState<bigint | undefined>(BigInt(42));
const onFormatChange: Props["onFormatChange"] = useCallback((_e, checked) => {
setFormat(checked);
}, []);
const trySetInt = (
value: string,
prefix: string,
validate: (x: string) => boolean,
unformat: (x: string) => string
) => {
if (value === "") {
setInt(undefined);
return;
}
const unformatted = unformat(value);
if (!validate(unformatted)) {
return;
}
const newInt = BigInt(`${prefix}${unformatted}`);
setInt(newInt);
};
const onDecChange: Props["onDecChange"] = useCallback(({ currentTarget: { value } }) => {
trySetInt(value, "", isDecimal, unformatDecimal);
}, []);
const onHexChange: Props["onHexChange"] = useCallback(({ currentTarget: { value } }) => {
trySetInt(value, "0x", isHexadecimal, unformatHexadecimal);
}, []);
const onOctChange: Props["onOctChange"] = useCallback(({ currentTarget: { value } }) => {
trySetInt(value, "0o", isOctal, unformatOctal);
}, []);
const onBinChange: Props["onBinChange"] = useCallback(({ currentTarget: { value } }) => {
trySetInt(value, "0b", isBinary, unformatBinary);
}, []);
const newDec = int?.toString(10) ?? "";
const newHex = int?.toString(16).toUpperCase() ?? "";
const newOct = int?.toString(8) ?? "";
const newBin = int?.toString(2) ?? "";
const dec = format ? formatDecimal(newDec) : newDec;
const hex = format ? formatHexadecimal(newHex) : newHex;
const oct = format ? formatOctal(newOct) : newOct;
const bin = format ? formatBinary(newBin) : newBin;
return (
<Component
{...{
format,
dec,
hex,
oct,
bin,
onFormatChange,
onDecChange,
onHexChange,
onOctChange,
onBinChange,
}}
/>
);
};
export default Container;