refactor: make some states grouped

This commit is contained in:
rusconn
2023-06-01 15:28:32 +09:00
parent d935d0579f
commit c8f69a7eef
4 changed files with 101 additions and 80 deletions

View File

@@ -31,50 +31,52 @@ const two = " ";
const four = " "; const four = " ";
export default function Page() { export default function Page() {
const [indentation, setIndentation] = useState(two); const [form, setForm] = useState({
const [json, setJson] = useState('{\n "foo": "bar"\n}'); indentation: two,
const [yaml, setYaml] = useState("foo: bar"); json: '{\n "foo": "bar"\n}',
yaml: "foo: bar",
});
const setJsonReactively = useCallback( const setJsonReactively = useCallback((text: string) => {
(text: string) => { const parsed = safeJsonParse(text);
const parsed = safeJsonParse(text);
setJson(text); setForm(prev => ({
setYaml( ...prev,
O.isNone(parsed) json: text,
? "" yaml: O.isNone(parsed)
: YAML.stringify(parsed.value, { indent: indentation.length, simpleKeys: true }) ? ""
); : YAML.stringify(parsed.value, { indent: prev.indentation.length, simpleKeys: true }),
}, }));
[indentation.length] }, []);
);
const setYamlReactively = useCallback( const setYamlReactively = useCallback((text: string) => {
(text: string) => { const parsed = safeYamlParse(text, (_, v) => v, { merge: true });
const parsed = safeYamlParse(text, (_, v) => v, { merge: true });
setYaml(text); setForm(prev => ({
setJson(O.isNone(parsed) ? "" : JSON.stringify(parsed.value, null, indentation)); ...prev,
}, yaml: text,
[indentation] json: O.isNone(parsed) ? "" : JSON.stringify(parsed.value, null, prev.indentation),
); }));
}, []);
const clearBoth = useCallback(() => { const clearBoth = useCallback(() => {
setJson(""); setForm(prev => ({ ...prev, json: "", yaml: "" }));
setYaml("");
}, []); }, []);
const onIndentationChange: SelectProps["onValueChange"] = value => { const onIndentationChange: SelectProps["onValueChange"] = value => {
setIndentation(value); const parsed = safeJsonParse(form.json);
const parsed = safeJsonParse(json); const jsonYaml = O.isNone(parsed)
? { json: "", yaml: "" }
: {
json: JSON.stringify(parsed.value, null, value),
yaml: YAML.stringify(parsed.value, { indent: value.length, simpleKeys: true }),
};
if (O.isNone(parsed)) { setForm({
clearBoth(); indentation: value,
} else { ...jsonYaml,
setJson(JSON.stringify(parsed.value, null, value)); });
setYaml(YAML.stringify(parsed.value, { indent: value.length, simpleKeys: true }));
}
}; };
const onJsonChange: EditorProps["onChange"] = value => setJsonReactively(value ?? ""); const onJsonChange: EditorProps["onChange"] = value => setJsonReactively(value ?? "");
@@ -87,12 +89,12 @@ export default function Page() {
icon={indentationIcon} icon={indentationIcon}
title="Indentation" title="Indentation"
control={ control={
<Select value={indentation} onValueChange={onIndentationChange}> <Select value={form.indentation} onValueChange={onIndentationChange}>
<SelectTrigger <SelectTrigger
className="w-28" className="w-28"
aria-label="toggle open/close state of indentation selection" aria-label="toggle open/close state of indentation selection"
> >
<SelectValue placeholder={indentation} /> <SelectValue placeholder={form.indentation} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value={two}>2 spaces</SelectItem> <SelectItem value={two}>2 spaces</SelectItem>
@@ -137,8 +139,8 @@ export default function Page() {
[setYamlReactively] [setYamlReactively]
); );
const jsonCopyButton = <CopyButton text={json} />; const jsonCopyButton = <CopyButton text={form.json} />;
const yamlCopyButton = <CopyButton text={yaml} />; const yamlCopyButton = <CopyButton text={form.yaml} />;
const clearButton = useMemo( const clearButton = useMemo(
() => <ClearButton onClick={clearBoth} iconOnly aria-label="clear json and yaml" />, () => <ClearButton onClick={clearBoth} iconOnly aria-label="clear json and yaml" />,
@@ -163,10 +165,10 @@ export default function Page() {
</PageSection> </PageSection>
<div className="flex flex-1 flex-col gap-x-4 gap-y-5 lg:flex-row"> <div className="flex flex-1 flex-col gap-x-4 gap-y-5 lg:flex-row">
<PageSection className="mt-0 min-h-[200px] flex-1" title="Json" control={jsonControl}> <PageSection className="mt-0 min-h-[200px] flex-1" title="Json" control={jsonControl}>
<Editor language="json" value={json} onChange={onJsonChange} /> <Editor language="json" value={form.json} onChange={onJsonChange} />
</PageSection> </PageSection>
<PageSection className="mt-0 min-h-[200px] flex-1" title="Yaml" control={yamlControl}> <PageSection className="mt-0 min-h-[200px] flex-1" title="Yaml" control={yamlControl}>
<Editor language="yaml" value={yaml} onChange={onYamlChange} /> <Editor language="yaml" value={form.yaml} onChange={onYamlChange} />
</PageSection> </PageSection>
</div> </div>
</PageRootSection> </PageRootSection>

View File

@@ -14,29 +14,32 @@ import { PageRootSection } from "@/components/page-root-section";
import { PageSection } from "@/components/page-section"; import { PageSection } from "@/components/page-section";
export default function Page() { export default function Page() {
const [decoded, setDecoded] = useState("😀😂🤣"); const [form, setForm] = useState({
const [encoded, setEncoded] = useState("8J+YgPCfmILwn6Sj"); decoded: "😀😂🤣",
encoded: "8J+YgPCfmILwn6Sj",
});
const setDecodedReactively = useCallback((text: string) => { const setDecodedReactively = useCallback((text: string) => {
setDecoded(text); setForm({
setEncoded(encode(text)); decoded: text,
encoded: encode(text),
});
}, []); }, []);
const setEncodedReactively = useCallback((text: string) => { const setEncodedReactively = useCallback((text: string) => {
setEncoded(text);
const newDecoded = decode(text); const newDecoded = decode(text);
if (isValid(text) && !newDecoded.includes("<22>")) { setForm({
setDecoded(newDecoded); encoded: text,
} else { decoded: isValid(text) && !newDecoded.includes("<22>") ? newDecoded : "",
setDecoded(""); });
}
}, []); }, []);
const clearBoth = useCallback(() => { const clearBoth = useCallback(() => {
setDecoded(""); setForm({
setEncoded(""); decoded: "",
encoded: "",
});
}, []); }, []);
const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) => const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) =>
@@ -69,8 +72,8 @@ export default function Page() {
[setEncodedReactively] [setEncodedReactively]
); );
const decodedCopyButton = useMemo(() => <CopyButton text={decoded} />, [decoded]); const decodedCopyButton = useMemo(() => <CopyButton text={form.decoded} />, [form.decoded]);
const encodedCopyButton = useMemo(() => <CopyButton text={encoded} />, [encoded]); const encodedCopyButton = useMemo(() => <CopyButton text={form.encoded} />, [form.encoded]);
const clearButton = useMemo( const clearButton = useMemo(
() => <ClearButton onClick={clearBoth} iconOnly aria-label="clear decoded and encoded" />, () => <ClearButton onClick={clearBoth} iconOnly aria-label="clear decoded and encoded" />,
@@ -88,10 +91,10 @@ export default function Page() {
return ( return (
<PageRootSection title={toolGroups.encodersDecoders.tools.base64.longTitle}> <PageRootSection title={toolGroups.encodersDecoders.tools.base64.longTitle}>
<PageSection title="Decoded" control={decodedControl}> <PageSection title="Decoded" control={decodedControl}>
<Textarea value={decoded} onChange={onDecodedChange} rows={10} /> <Textarea value={form.decoded} onChange={onDecodedChange} rows={10} />
</PageSection> </PageSection>
<PageSection title="Encoded" control={encodedControl}> <PageSection title="Encoded" control={encodedControl}>
<Textarea value={encoded} onChange={onEncodedChange} rows={10} /> <Textarea value={form.encoded} onChange={onEncodedChange} rows={10} />
</PageSection> </PageSection>
</PageRootSection> </PageRootSection>
); );

View File

@@ -14,22 +14,30 @@ import { PageRootSection } from "@/components/page-root-section";
import { PageSection } from "@/components/page-section"; import { PageSection } from "@/components/page-section";
export default function Page() { export default function Page() {
const [decoded, setDecoded] = useState('> It\'s "HTML escaping".'); const [form, setForm] = useState({
const [encoded, setEncoded] = useState("&gt; It&apos;s &quot;HTML escaping&quot;."); decoded: '> It\'s "HTML escaping".',
encoded: "&gt; It&apos;s &quot;HTML escaping&quot;.",
});
const setDecodedReactively = useCallback((text: string) => { const setDecodedReactively = useCallback((text: string) => {
setDecoded(text); setForm({
setEncoded(encode(text)); decoded: text,
encoded: encode(text),
});
}, []); }, []);
const setEncodedReactively = useCallback((text: string) => { const setEncodedReactively = useCallback((text: string) => {
setEncoded(text); setForm({
setDecoded(decode(text)); encoded: text,
decoded: decode(text),
});
}, []); }, []);
const clearBoth = useCallback(() => { const clearBoth = useCallback(() => {
setDecoded(""); setForm({
setEncoded(""); decoded: "",
encoded: "",
});
}, []); }, []);
const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) => const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) =>
@@ -62,8 +70,8 @@ export default function Page() {
[setEncodedReactively] [setEncodedReactively]
); );
const decodedCopyButton = useMemo(() => <CopyButton text={decoded} />, [decoded]); const decodedCopyButton = useMemo(() => <CopyButton text={form.decoded} />, [form.decoded]);
const encodedCopyButton = useMemo(() => <CopyButton text={encoded} />, [encoded]); const encodedCopyButton = useMemo(() => <CopyButton text={form.encoded} />, [form.encoded]);
const clearButton = useMemo( const clearButton = useMemo(
() => <ClearButton onClick={clearBoth} iconOnly aria-label="clear decoded and encoded" />, () => <ClearButton onClick={clearBoth} iconOnly aria-label="clear decoded and encoded" />,
@@ -81,10 +89,10 @@ export default function Page() {
return ( return (
<PageRootSection title={toolGroups.encodersDecoders.tools.html.longTitle}> <PageRootSection title={toolGroups.encodersDecoders.tools.html.longTitle}>
<PageSection title="Decoded" control={decodedControl}> <PageSection title="Decoded" control={decodedControl}>
<Textarea value={decoded} onChange={onDecodedChange} rows={10} /> <Textarea value={form.decoded} onChange={onDecodedChange} rows={10} />
</PageSection> </PageSection>
<PageSection title="Encoded" control={encodedControl}> <PageSection title="Encoded" control={encodedControl}>
<Textarea value={encoded} onChange={onEncodedChange} rows={10} /> <Textarea value={form.encoded} onChange={onEncodedChange} rows={10} />
</PageSection> </PageSection>
</PageRootSection> </PageRootSection>
); );

View File

@@ -16,22 +16,30 @@ import { PageRootSection } from "@/components/page-root-section";
import { PageSection } from "@/components/page-section"; import { PageSection } from "@/components/page-section";
export default function Page() { export default function Page() {
const [decoded, setDecoded] = useState('> It\'s "URL encoding"?'); const [form, setForm] = useState({
const [encoded, setEncoded] = useState("%3E%20It's%20%22URL%20encoding%22%3F"); decoded: '> It\'s "URL encoding"?',
encoded: "%3E%20It's%20%22URL%20encoding%22%3F",
});
const setDecodedReactively = useCallback((text: string) => { const setDecodedReactively = useCallback((text: string) => {
setDecoded(text); setForm({
setEncoded(O.getOrElse(constant(""))(safeEncodeURIComponent(text))); decoded: text,
encoded: O.getOrElse(constant(""))(safeEncodeURIComponent(text)),
});
}, []); }, []);
const setEncodedReactively = useCallback((text: string) => { const setEncodedReactively = useCallback((text: string) => {
setEncoded(text); setForm({
setDecoded(O.getOrElse(constant(""))(safeDecodeURIComponent(text))); encoded: text,
decoded: O.getOrElse(constant(""))(safeDecodeURIComponent(text)),
});
}, []); }, []);
const clearBoth = useCallback(() => { const clearBoth = useCallback(() => {
setDecoded(""); setForm({
setEncoded(""); decoded: "",
encoded: "",
});
}, []); }, []);
const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) => const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) =>
@@ -64,8 +72,8 @@ export default function Page() {
[setEncodedReactively] [setEncodedReactively]
); );
const decodedCopyButton = useMemo(() => <CopyButton text={decoded} />, [decoded]); const decodedCopyButton = useMemo(() => <CopyButton text={form.decoded} />, [form.decoded]);
const encodedCopyButton = useMemo(() => <CopyButton text={encoded} />, [encoded]); const encodedCopyButton = useMemo(() => <CopyButton text={form.encoded} />, [form.encoded]);
const clearButton = useMemo( const clearButton = useMemo(
() => <ClearButton onClick={clearBoth} iconOnly aria-label="clear decoded and encoded" />, () => <ClearButton onClick={clearBoth} iconOnly aria-label="clear decoded and encoded" />,
@@ -83,10 +91,10 @@ export default function Page() {
return ( return (
<PageRootSection title={toolGroups.encodersDecoders.tools.url.longTitle}> <PageRootSection title={toolGroups.encodersDecoders.tools.url.longTitle}>
<PageSection title="Decoded" control={decodedControl}> <PageSection title="Decoded" control={decodedControl}>
<Textarea value={decoded} onChange={onDecodedChange} rows={10} /> <Textarea value={form.decoded} onChange={onDecodedChange} rows={10} />
</PageSection> </PageSection>
<PageSection title="Encoded" control={encodedControl}> <PageSection title="Encoded" control={encodedControl}>
<Textarea value={encoded} onChange={onEncodedChange} rows={10} /> <Textarea value={form.encoded} onChange={onEncodedChange} rows={10} />
</PageSection> </PageSection>
</PageRootSection> </PageRootSection>
); );