feat(web): integrate localized Lexical editor demo
Register Lexical's English and Simplified Chinese catalogs in the application loader, import its global styles, and add the workspace dependency with its resolved lockfile graph.\n\nReplace the dashboard placeholder with a controlled rich-text editor demo that composes default actions, fixed and bubble toolbars, draggable blocks, generated HTML output, and reset behavior.
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
"@workspace/blocks/chats/locales/{locale}",
|
||||
"@workspace/blocks/layout/locales/{locale}",
|
||||
"@workspace/blocks/navigation/locales/{locale}",
|
||||
"@workspace/blocks/notifications/locales/{locale}"
|
||||
"@workspace/blocks/notifications/locales/{locale}",
|
||||
"@workspace/lexical/locales/{locale}"
|
||||
],
|
||||
"include": ["src", "../../packages/blocks/src", "../../packages/ui/src"],
|
||||
"exclude": ["**/*.test.{ts,tsx}", "**/node_modules/**"]
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"@tanstack/react-start": "^1.168.32",
|
||||
"@workspace/blocks": "workspace:*",
|
||||
"@workspace/i18n": "workspace:*",
|
||||
"@workspace/lexical": "workspace:*",
|
||||
"@workspace/ui": "workspace:*",
|
||||
"country-flag-icons": "^1.6.20",
|
||||
"lucide-react": "^1.27.0",
|
||||
|
||||
@@ -1,48 +1,76 @@
|
||||
import * as React from "react"
|
||||
import { formatForDisplay } from "@tanstack/react-hotkeys"
|
||||
import {
|
||||
LexicalActions,
|
||||
LexicalBubbleToolbar,
|
||||
LexicalContent,
|
||||
LexicalDraggableBlockPlugin,
|
||||
LexicalFixedToolbar,
|
||||
LexicalFooter,
|
||||
LexicalRoot,
|
||||
} from "@workspace/lexical"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { Slider } from "@workspace/ui/components/slider"
|
||||
import { Switch } from "@workspace/ui/components/switch"
|
||||
|
||||
const initialEditorHtml = [
|
||||
"<h2>Welcome to the Lexical editor</h2>",
|
||||
"<p>This example uses declarative actions to compose reusable editor features.</p>",
|
||||
"<ul><li>Format text with the toolbar</li><li>Insert links, images, videos, lists, dates, and more</li><li>Read the generated HTML beside the editor</li></ul>",
|
||||
].join("")
|
||||
|
||||
export function DashboardPage() {
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
className="h-auto w-full"
|
||||
src="https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/background/background-3-blur.webp"
|
||||
/>
|
||||
<div className="flex max-w-md min-w-0 flex-col gap-4 text-sm leading-loose">
|
||||
<div>
|
||||
<h1 className="font-medium">Project ready!</h1>
|
||||
<p>You may now add components and start building.</p>
|
||||
<p>We've already added the button component for you.</p>
|
||||
<Button variant="ghost" className="mt-2">
|
||||
Button
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Switch />
|
||||
<Slider
|
||||
defaultValue={[75]}
|
||||
max={100}
|
||||
step={1}
|
||||
className="mx-auto w-full max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="font-mono text-xs text-muted-foreground">
|
||||
(Press <kbd>Mod+D</kbd> to toggle dark mode)
|
||||
</div>
|
||||
const [html, setHtml] = React.useState(initialEditorHtml)
|
||||
|
||||
<div>
|
||||
<span className="mr-2">breakpoint:</span>
|
||||
<span className="hidden mobile:inline">mobile</span>
|
||||
<span className="hidden tablet:inline">tablet</span>
|
||||
<span className="hidden desktop:inline">desktop</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="mr-2">collapsed:</span>
|
||||
<span className="hidden collapsed:inline">true</span>
|
||||
<span className="inline collapsed:hidden">false</span>
|
||||
</div>
|
||||
return (
|
||||
<div className="flex min-w-0 flex-col gap-6 bg-background">
|
||||
<div>
|
||||
<h1 className="text-xl font-medium">Lexical editor</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
Actions declare their controls and editor dependencies in one place.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<div className="grid min-w-0 gap-6 xl:grid-cols-[minmax(0,1fr)_minmax(20rem,0.7fr)]">
|
||||
<section className="min-w-0">
|
||||
<div className="mb-2 flex items-center justify-between gap-3">
|
||||
<h2 className="font-medium">Editor</h2>
|
||||
<code className="text-xs text-muted-foreground">
|
||||
useDefaults="full"
|
||||
</code>
|
||||
</div>
|
||||
<LexicalRoot value={html} onChange={setHtml} className="shadow-xs">
|
||||
<LexicalActions useDefaults="full" />
|
||||
<LexicalFixedToolbar />
|
||||
<LexicalContent
|
||||
className="min-h-72"
|
||||
placeholder="Write something…"
|
||||
/>
|
||||
<LexicalDraggableBlockPlugin />
|
||||
<LexicalBubbleToolbar aria-label="Selected text formatting" />
|
||||
<LexicalFooter className="flex items-center justify-between gap-3 text-xs text-muted-foreground">
|
||||
<span>HTML output updates as you type.</span>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setHtml("")}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</LexicalFooter>
|
||||
</LexicalRoot>
|
||||
</section>
|
||||
|
||||
<section className="flex min-w-0 flex-col">
|
||||
<h2 className="mb-2 font-medium">Generated HTML</h2>
|
||||
<pre className="min-h-72 flex-1 overflow-auto rounded-lg border bg-muted/30 p-4 text-xs leading-relaxed break-words whitespace-pre-wrap">
|
||||
{html || "The editor is empty."}
|
||||
</pre>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<p className="font-mono text-xs text-muted-foreground">
|
||||
Press <kbd>{formatForDisplay("Mod+D")}</kbd> to toggle dark mode.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@import "@workspace/ui/globals.css";
|
||||
@import "@workspace/blocks/globals.css";
|
||||
@import "@workspace/lexical/globals.css";
|
||||
|
||||
:root {
|
||||
--ui-content-top-gap: --spacing(2);
|
||||
|
||||
Reference in New Issue
Block a user