mirror of
https://github.com/ershisan99/DevToysWeb.git
synced 2025-12-16 12:32:48 +00:00
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
172 lines
5.1 KiB
TypeScript
172 lines
5.1 KiB
TypeScript
"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>
|
||
);
|
||
}
|