0d817537be
Introduce @workspace/lexical with composable actions, toolbars, embeds, rich-text nodes, media uploads, selection utilities, HTML serialization, and theme styling.\n\nLocalize built-in controls through MessageDescriptor catalogs for English and Simplified Chinese, while providing an English I18nProvider fallback for standalone editor use. Include focused unit coverage for actions, controls, serialization, plugins, public API, and catalogs.
95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
import { useEffect, useMemo, useState } from "react"
|
|
import type { ComponentProps, ReactNode } from "react"
|
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"
|
|
import { mergeRegister } from "@lexical/utils"
|
|
import {
|
|
CAN_REDO_COMMAND,
|
|
CAN_UNDO_COMMAND,
|
|
COMMAND_PRIORITY_LOW,
|
|
SELECTION_CHANGE_COMMAND,
|
|
} from "lexical"
|
|
import { TooltipProvider } from "@workspace/ui/components/tooltip"
|
|
import { cn } from "@workspace/ui/lib/utils"
|
|
|
|
import { defaultActionState, LexicalActionContext } from "./context"
|
|
import { LexicalActionTree } from "./actions-view"
|
|
import { useLexicalActions } from "./actions-context"
|
|
|
|
export type LexicalFixedToolbarProps = ComponentProps<"div">
|
|
|
|
function LexicalActionRuntimeProvider({ children }: { children: ReactNode }) {
|
|
const [editor] = useLexicalComposerContext()
|
|
const [state, setState] = useState(defaultActionState)
|
|
const context = useMemo(() => ({ editor, state }), [editor, state])
|
|
|
|
useEffect(() => {
|
|
const update = () => {
|
|
setState((current) => ({
|
|
...current,
|
|
revision: current.revision + 1,
|
|
}))
|
|
}
|
|
return mergeRegister(
|
|
editor.registerUpdateListener(update),
|
|
editor.registerCommand(
|
|
SELECTION_CHANGE_COMMAND,
|
|
() => {
|
|
update()
|
|
return false
|
|
},
|
|
COMMAND_PRIORITY_LOW
|
|
),
|
|
editor.registerCommand(
|
|
CAN_UNDO_COMMAND,
|
|
(canUndo) => {
|
|
setState((current) => ({ ...current, canUndo }))
|
|
return false
|
|
},
|
|
COMMAND_PRIORITY_LOW
|
|
),
|
|
editor.registerCommand(
|
|
CAN_REDO_COMMAND,
|
|
(canRedo) => {
|
|
setState((current) => ({ ...current, canRedo }))
|
|
return false
|
|
},
|
|
COMMAND_PRIORITY_LOW
|
|
)
|
|
)
|
|
}, [editor])
|
|
|
|
return (
|
|
<LexicalActionContext.Provider value={context}>
|
|
<TooltipProvider>{children}</TooltipProvider>
|
|
</LexicalActionContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function LexicalFixedToolbar({
|
|
className,
|
|
children,
|
|
...props
|
|
}: LexicalFixedToolbarProps) {
|
|
const actions = useLexicalActions("toolbar")
|
|
|
|
if (actions.length === 0 && children == null) return null
|
|
|
|
return (
|
|
<LexicalActionRuntimeProvider>
|
|
<div
|
|
role="toolbar"
|
|
className={cn(
|
|
"flex min-h-11 flex-wrap items-center gap-1 border-b p-1",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
<LexicalActionTree items={actions} />
|
|
{children}
|
|
</div>
|
|
</LexicalActionRuntimeProvider>
|
|
)
|
|
}
|
|
|
|
export { LexicalActionRuntimeProvider }
|