mirror of
https://github.com/ershisan99/DevToysWeb.git
synced 2025-12-16 20:49:23 +00:00
feat: implement text inspector and case converter tool
This commit is contained in:
17
app/text/inspector/layout.tsx
Normal file
17
app/text/inspector/layout.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: toolGroups.text.tools.inspector_and_case_converter.longTitle,
|
||||
description: toolGroups.text.tools.inspector_and_case_converter.description,
|
||||
robots: {
|
||||
googleBot: {
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
89
app/text/inspector/page.tsx
Normal file
89
app/text/inspector/page.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { noOp } from "@/lib/base";
|
||||
import {
|
||||
countBytes,
|
||||
countCharacters,
|
||||
countLines,
|
||||
countWords,
|
||||
modeTitle,
|
||||
TextTransformMode,
|
||||
textTransformModes,
|
||||
transformText,
|
||||
} from "@/lib/text";
|
||||
import { Textarea, TextareaProps } from "@/components/ui/textarea";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import * as Button from "@/components/buttons";
|
||||
import { ControlMenu } from "@/components/control-menu";
|
||||
import { PageRootSection } from "@/components/page-root-section";
|
||||
import { PageSection } from "@/components/page-section";
|
||||
|
||||
export default function Page() {
|
||||
const [input, setInput] = useState("ConvertMe");
|
||||
const [mode, setMode] = useState(TextTransformMode.original);
|
||||
|
||||
const output = transformText(input, mode);
|
||||
|
||||
const stats = useMemo(
|
||||
() => ({
|
||||
characters: countCharacters(input),
|
||||
words: countWords(input),
|
||||
lines: countLines(input),
|
||||
bytes: countBytes(input),
|
||||
}),
|
||||
[input]
|
||||
);
|
||||
|
||||
const clearInput = useCallback(() => setInput(""), []);
|
||||
|
||||
const onInputChange: TextareaProps["onChange"] = e => setInput(e.currentTarget.value);
|
||||
const onModeChange = (value: string) => {
|
||||
if (value && value in TextTransformMode) {
|
||||
setMode(value as TextTransformMode);
|
||||
}
|
||||
};
|
||||
|
||||
const inputPasteButton = <Button.Paste onClipboardRead={setInput} />;
|
||||
const inputFileButton = <Button.File onFileRead={setInput} iconOnly aria-label="load a file" />;
|
||||
const inputClearButton = <Button.Clear onClick={clearInput} iconOnly aria-label="clear input" />;
|
||||
|
||||
const outputCopyButton = <Button.Copy text={output} />;
|
||||
|
||||
const inputControl = <ControlMenu list={[inputPasteButton, inputFileButton, inputClearButton]} />;
|
||||
const outputControl = <ControlMenu list={[outputCopyButton]} />;
|
||||
|
||||
return (
|
||||
<PageRootSection title={toolGroups.text.tools.inspector_and_case_converter.longTitle}>
|
||||
<PageSection title="Convert">
|
||||
<ToggleGroup className="flex-wrap" type="single" value={mode} onValueChange={onModeChange}>
|
||||
{textTransformModes.map(m => (
|
||||
<ToggleGroupItem key={m} value={m}>
|
||||
{modeTitle[m]}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
</PageSection>
|
||||
|
||||
<div className="flex flex-1 flex-col gap-x-4 gap-y-5 lg:flex-row">
|
||||
<PageSection className="min-h-[200px] flex-1" title="Input" control={inputControl}>
|
||||
<Textarea value={input} onChange={onInputChange} rows={10} />
|
||||
</PageSection>
|
||||
<PageSection className="min-h-[200px] flex-1" title="Output" control={outputControl}>
|
||||
<Textarea value={output} onChange={noOp} rows={10} readOnly />
|
||||
</PageSection>
|
||||
</div>
|
||||
|
||||
<PageSection className="min-h-[200px] flex-1" title="Information">
|
||||
<div className="grid max-w-sm grid-cols-2 gap-x-4">
|
||||
Characters: <span className="font-mono">{stats.characters}</span>
|
||||
Words: <span className="font-mono">{stats.words}</span>
|
||||
Lines: <span className="font-mono">{stats.lines}</span>
|
||||
Bytes: <span className="font-mono">{stats.bytes}</span>
|
||||
</div>
|
||||
</PageSection>
|
||||
</PageRootSection>
|
||||
);
|
||||
}
|
||||
16
app/text/layout.tsx
Normal file
16
app/text/layout.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: toolGroups.generators.title,
|
||||
robots: {
|
||||
googleBot: {
|
||||
index: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
11
app/text/page.tsx
Normal file
11
app/text/page.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { PageRootSection } from "@/components/page-root-section";
|
||||
import { ToolCards } from "@/components/tool-cards";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<PageRootSection title={toolGroups.text.title}>
|
||||
<ToolCards tools={Object.values(toolGroups.text.tools)} />
|
||||
</PageRootSection>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user