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
This commit is contained in:
rusconn
2023-05-14 12:44:34 +00:00
parent a7001213f8
commit f1c3bd2971
167 changed files with 5931 additions and 3853 deletions

View File

@@ -0,0 +1,11 @@
import { Metadata } from "next";
import { toolGroups } from "@/config/tools";
export const metadata: Metadata = {
title: toolGroups.generators.tools.uuid.longTitle,
};
export default function Layout({ children }: { children: React.ReactNode }) {
return children;
}

View File

@@ -0,0 +1,171 @@
"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>
);
}