mirror of
https://github.com/ershisan99/DevToysWeb.git
synced 2025-12-16 20:49:23 +00:00
feat: implement text diff tool
This commit is contained in:
17
app/text/diff/layout.tsx
Normal file
17
app/text/diff/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;
|
||||
}
|
||||
140
app/text/diff/page.tsx
Normal file
140
app/text/diff/page.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Panel, PanelGroup } from "react-resizable-panels";
|
||||
|
||||
import { PERSISTENCE_KEY } from "@/config/persistence-keys";
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { cn } from "@/lib/style";
|
||||
import { DiffEditor } from "@/components/ui/diff-editor";
|
||||
import { Editor } from "@/components/ui/editor";
|
||||
import * as Button from "@/components/buttons";
|
||||
import { Configuration } from "@/components/configuration";
|
||||
import { Configurations } from "@/components/configurations";
|
||||
import { ControlMenu } from "@/components/control-menu";
|
||||
import * as Icon from "@/components/icons";
|
||||
import * as icons from "@/components/icons";
|
||||
import { Rows } from "@/components/icons";
|
||||
import { LabeledSwitch } from "@/components/labeled-switch";
|
||||
import { PageRootSection } from "@/components/page-root-section";
|
||||
import { PageSection } from "@/components/page-section";
|
||||
import { PanelResizeHandle } from "@/components/panel-resize-handler";
|
||||
|
||||
/** No particular reason for these sizes, just feels like a good balance */
|
||||
const VERTICAL_PANEL_MAX_SIZE = 80;
|
||||
const HORIZONTAL_PANEL_MAX_SIZE = 90;
|
||||
const PANEL_FULL_SIZE = 100;
|
||||
|
||||
export default function Page() {
|
||||
const [input1, setInput1] = useState<string | undefined>("Hello world");
|
||||
const [input2, setInput2] = useState<string | undefined>("Hello, World!");
|
||||
const [diffFullHeight, setDiffFullHeight] = useState(false);
|
||||
const [inlineMode, setInlineMode] = useState(false);
|
||||
const diffPanelMaxSize = useMemo(
|
||||
() => (diffFullHeight ? PANEL_FULL_SIZE : VERTICAL_PANEL_MAX_SIZE),
|
||||
[diffFullHeight]
|
||||
);
|
||||
|
||||
const clearInput1 = useCallback(() => setInput1(""), [setInput1]);
|
||||
const clearInput2 = useCallback(() => setInput2(""), [setInput2]);
|
||||
const toggleFullHeight = useCallback(() => setDiffFullHeight(prev => !prev), [setDiffFullHeight]);
|
||||
|
||||
const inlineModeConfig = useMemo(
|
||||
() => (
|
||||
<Configuration
|
||||
icon={<icons.Rows size={24} />}
|
||||
title="Inline mode"
|
||||
control={
|
||||
<LabeledSwitch
|
||||
id="uppercase-switch"
|
||||
label={inlineMode ? "On" : "Off"}
|
||||
checked={inlineMode}
|
||||
onCheckedChange={setInlineMode}
|
||||
aria-label="toggle whether to show diff in inline mode"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
[inlineMode, setInlineMode]
|
||||
);
|
||||
const input1Control = useMemo(
|
||||
() => (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.Paste onClipboardRead={setInput1} />,
|
||||
<Button.File onFileRead={setInput1} iconOnly aria-label="load a file with old text" />,
|
||||
<Button.Clear onClick={clearInput1} iconOnly aria-label="clear old text input" />,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
[setInput1, clearInput1]
|
||||
);
|
||||
const input2Control = useMemo(
|
||||
() => (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.Paste onClipboardRead={setInput2} />,
|
||||
<Button.File onFileRead={setInput2} iconOnly aria-label="load a file with new text" />,
|
||||
<Button.Clear onClick={clearInput2} iconOnly aria-label="clear new text input" />,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
[setInput2, clearInput2]
|
||||
);
|
||||
|
||||
const diffControl = useMemo(
|
||||
() => (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.ToggleFullSize iconOnly onClick={toggleFullHeight} expanded={diffFullHeight} />,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
[diffFullHeight, toggleFullHeight]
|
||||
);
|
||||
|
||||
return (
|
||||
<PageRootSection
|
||||
className="h-full"
|
||||
title={toolGroups.text.tools.inspector_and_case_converter.longTitle}
|
||||
>
|
||||
<PageSection title="Configuration" className={cn(diffFullHeight && "hidden")}>
|
||||
<Configurations list={[inlineModeConfig]} />
|
||||
</PageSection>
|
||||
<PanelGroup direction="vertical" autoSaveId={PERSISTENCE_KEY.panels.textDiff.vertical}>
|
||||
<Panel maxSize={VERTICAL_PANEL_MAX_SIZE} className={cn(diffFullHeight && "hidden")}>
|
||||
<PanelGroup
|
||||
direction="horizontal"
|
||||
autoSaveId={PERSISTENCE_KEY.panels.textDiff.horizontal}
|
||||
>
|
||||
<Panel maxSize={HORIZONTAL_PANEL_MAX_SIZE}>
|
||||
<PageSection className="h-full" title="Old text" control={input1Control}>
|
||||
<Editor value={input1} onChange={setInput1} />
|
||||
</PageSection>
|
||||
</Panel>
|
||||
<PanelResizeHandle direction="vertical" className="mt-[42px]" />
|
||||
|
||||
<Panel maxSize={HORIZONTAL_PANEL_MAX_SIZE}>
|
||||
<PageSection className="h-full" title="New text" control={input2Control}>
|
||||
<Editor value={input2} onChange={setInput2} />
|
||||
</PageSection>
|
||||
</Panel>
|
||||
</PanelGroup>
|
||||
</Panel>
|
||||
<PanelResizeHandle direction="horizontal" className={cn(diffFullHeight && "hidden")} />
|
||||
<Panel maxSize={diffPanelMaxSize}>
|
||||
<PageSection className="h-full" title="Difference" control={diffControl}>
|
||||
<DiffEditor
|
||||
original={input1}
|
||||
modified={input2}
|
||||
options={{
|
||||
readOnly: true,
|
||||
renderSideBySide: !inlineMode,
|
||||
}}
|
||||
/>
|
||||
</PageSection>
|
||||
</Panel>
|
||||
</PanelGroup>
|
||||
</PageRootSection>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user