Files
DevToysWeb/app/generators/uuid/page.tsx
rusconn f1c3bd2971 renewal
recreate project by using https://github.com/shadcn/next-template

App:
- support dark mode
- add toggle theme button
- add clear search button
- add search button
- add current page indicator
- add tool group pages
- add settings tool
- add 1 tab format option to Json format tool
- add paste button to some tools
- add file button to some tools
- add copy button to some tools
- add clear button to some tools
- change favicon
- change search hit rate
- change each page title
- change icons from Material Icons to Lucide
- change sidebar scroll area
- change editor from Ace to Monaco
- change parsable separators of number base converter
- change default value of format option of number base converter
- change default values of some tool forms
- change some styles
- remove disabled tools
- remove real-time search
- fix uri encoding tool

Dev:
- MUI + Emotion -> Radix UI + Tailwind CSS
- Next.js 12 Pages -> Next.js 13 App Router
- React 17 -> React 18
- many other packages upgraded
- use useState instead of recoil
- use Next.js typedRoutes instead of pathpida
- clean npm scripts
- format import statements by Prettier
- no component separations between container and presenter
- effective component memoizations
- add vscode settings
- many refactors
2023-05-28 23:56:43 +09:00

172 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useCallback, useMemo, useState } from "react";
import { range } from "fp-ts/NonEmptyArray";
import * as t from "io-ts";
import { toolGroups } from "@/config/tools";
import { uuid } from "@/lib/uuid";
import { useScrollFollow } from "@/hooks/useScrollFollow";
import { Button } from "@/components/ui/button";
import { Input, InputProps } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectProps,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { ClearButton } from "@/components/buttons/clear";
import { CopyButton } from "@/components/buttons/copy";
import { Configuration } from "@/components/configuration";
import { Configurations } from "@/components/configurations";
import { ControlMenu } from "@/components/control-menu";
import { icons } from "@/components/icons";
import { LabeledSwitch } from "@/components/labeled-switch";
import { PageRootSection } from "@/components/page-root-section";
import { PageSection } from "@/components/page-section";
const v1 = "1";
const v4 = "4";
const uuidVersions = t.keyof({ [v1]: null, [v4]: null });
type UuidVersion = t.TypeOf<typeof uuidVersions>;
export default function Page() {
const [hyphens, setHyphens] = useState(true);
const [uppercase, setUppercase] = useState(false);
const [uuidVersion, setUuidVersion] = useState<UuidVersion>("4");
const [generates, setGenerates] = useState(1);
const [uuids, setUuids] = useState<string[]>([]);
const ref = useScrollFollow<HTMLTextAreaElement>([uuids]);
const uuidsString = uuids.join("\n");
const clearUuids = useCallback(() => setUuids([]), []);
const onUuidVersionChange: SelectProps["onValueChange"] = value => {
if (uuidVersions.is(value)) {
setUuidVersion(value);
}
};
const onGeneratesChange: InputProps["onChange"] = ({ currentTarget: { value } }) => {
const newGenerates = Number(value);
if (newGenerates >= 1 && newGenerates <= 1000) {
setGenerates(newGenerates);
}
};
const onGenerateClick = () => {
const newUuids = range(1, generates).map(_ => uuid(uuidVersion, hyphens, uppercase));
setUuids([...uuids, ...newUuids]);
};
const hyphensConfig = useMemo(
() => (
<Configuration
icon={<icons.Minus size={24} />}
title="Hyphens"
control={
<LabeledSwitch
id="hyphens-switch"
label={hyphens ? "On" : "Off"}
checked={hyphens}
onCheckedChange={setHyphens}
aria-label="toggle whether to add hyphens"
/>
}
/>
),
[hyphens]
);
const uppercaseConfig = useMemo(
() => (
<Configuration
icon={<icons.CaseSensitive size={24} />}
title="Uppercase"
control={
<LabeledSwitch
id="uppercase-switch"
label={uppercase ? "On" : "Off"}
checked={uppercase}
onCheckedChange={setUppercase}
aria-label="toggle whether to generate in uppercase"
/>
}
/>
),
[uppercase]
);
const uuidVersionConfig = useMemo(
() => (
<Configuration
icon={<icons.Settings2 size={24} />}
title="UUID version"
description="Choose the version of UUID to generate"
control={
<Select value={uuidVersion} onValueChange={onUuidVersionChange}>
<SelectTrigger
className="w-28"
aria-label="toggle open/close state of uuid version selection"
>
<SelectValue placeholder={uuidVersion} />
</SelectTrigger>
<SelectContent>
<SelectItem value={v1}>1</SelectItem>
<SelectItem value={v4}>4 (GUID)</SelectItem>
</SelectContent>
</Select>
}
/>
),
[uuidVersion]
);
const generatesInput = useMemo(
() => (
<Input
className="w-24 font-sans"
type="number"
value={generates}
onChange={onGeneratesChange}
/>
),
[generates]
);
const uuidsCopyButton = useMemo(() => <CopyButton text={uuidsString} />, [uuidsString]);
const uuidsClearButton = useMemo(
() => <ClearButton onClick={clearUuids} iconOnly aria-label="clear uuids" />,
[clearUuids]
);
const uuidsControl = <ControlMenu list={[uuidsCopyButton, uuidsClearButton]} />;
return (
<PageRootSection title={toolGroups.generators.tools.uuid.longTitle}>
<PageSection title="Configuration">
<Configurations list={[hyphensConfig, uppercaseConfig, uuidVersionConfig]} />
</PageSection>
<PageSection className="mt-6" title="Generate">
<div className="flex items-center gap-2">
<Button variant="secondary" onClick={onGenerateClick}>
Generate UUID(s)
</Button>
<span>×</span>
{generatesInput}
</div>
</PageSection>
<PageSection title="UUID(s)" control={uuidsControl}>
<Textarea {...{ ref }} value={uuidsString} rows={10} readOnly />
</PageSection>
</PageRootSection>
);
}