variable-picker-plugin.tsx
Overview
variable-picker-plugin.tsx is a React component file that implements a custom variable picker menu plugin for a Lexical-based rich text editor. This plugin enables users to insert structured "variables" into the editor content via a typeahead menu triggered by the / character. The variables are organized into categories and can be filtered dynamically as users type. Selected variables are inserted as specialized nodes in the Lexical editor, supporting both textual and complex variable representations.
The plugin supports:
Dynamic fetching and filtering of variable options.
Nested menu options with categories and sub-options.
Parsing initial text values with embedded variables into the editor's node structure.
Programmatic insertion of variables into the Lexical editor state.
A polished UI with keyboard and mouse interaction support for selecting variables.
Detailed Explanation of Contents
Imports and Dependencies
Lexical Framework:
Core Lexical APIs:
$createParagraphNode,$createTextNode,$getRoot,$getSelection, etc.React Lexical plugins:
LexicalTypeaheadMenuPlugin,MenuOption,useBasicTypeaheadTriggerMatch.
React: Hooks (
useCallback,useEffect,useRef) and JSX.ReactDOM: For rendering the typeahead menu portal.
Local Modules:
$createVariableNode: Custom Lexical node for variables.useBuildQueryVariableOptions: Hook providing variable options.PromptIdentity,ProgrammaticTag: Constants used for logic.
CSS: Styles imported from
./index.css.
Classes
VariableInnerOption (extends MenuOption)
Represents an individual selectable variable option in the menu.
Properties:
label: string— Display text for the variable.value: string— Unique value/key for the variable.parentLabel: string | JSX.Element— Category or parent label associated.icon?: ReactNode— Optional icon for UI representation.
Constructor:
constructor( label: string, value: string, parentLabel: string | JSX.Element, icon?: ReactNode, )Initializes the option with its label, value, parent label, and optional icon.
VariableOption (extends MenuOption)
Represents a category grouping multiple VariableInnerOption items.
Properties:
label: ReactElement | string— Display label for the category.title: string— Title used internally as key and display.options: VariableInnerOption[]— List of sub-options under this category.
Constructor:
constructor( label: ReactElement | string, title: string, options: VariableInnerOption[], )Initializes the category with label, title, and nested options.
Functional Components
VariablePickerMenuItem
Renders a single category item in the menu, including its nested variable options.
Props:
index: number— Index in the list (for accessibility/id).option: VariableOption— The category option to render.selectOptionAndCleanUp: (option: VariableOption | VariableInnerOption) => void— Callback to handle selection.
Behavior:
Displays the category title.
Lists each child variable option as clickable list items.
Clicking a variable option triggers selection cleanup.
Usage Example:
<VariablePickerMenuItem index={0} option={someVariableOption} selectOptionAndCleanUp={(selected) => console.log(selected)} />
Main Exported Component: VariablePickerMenuPlugin
A React component implementing the variable picker typeahead menu plugin.
Props:
type VariablePickerMenuPluginProps = { value?: string; // Initial text value to parse into variable nodes on first render. extraOptions?: Array<{ label: string; title: string; options: Array<{ label: string; value: string; icon?: ReactNode }>; }>; // Additional categories/options to include. }Key Hooks and State:
useLexicalComposerContext()to get the Lexical editor instance.useBasicTypeaheadTriggerMatch('/')to activate menu on/trigger.queryStringstate to track current input after trigger for filtering.useBuildQueryVariableOptions()to fetch base variable options.isFirstRenderref to parse initial value only once.
Core Functions:
buildNextOptions()
Builds the filtered and combined list of variable categories and options based on the current query string and extra options.Filters options by matching label/value against the query (case-insensitive).
Returns an array of
VariableOptioninstances with nestedVariableInnerOptions.
findItemByValue(value: string)
Searches all variable options to find an item by its value.onSelectOption(selectedOption, nodeToRemove, closeMenu)
Handles the insertion of the selected variable into the Lexical editor:Removes the query text node if present.
Inserts either plain text (if
parentLabelmatchesPromptIdentity) or a custom variable node.Closes the typeahead menu.
parseTextToVariableNodes(text: string)
Parses an initial string with embedded variable placeholders (e.g.,{variableName}) and converts them into a Lexical paragraph node containing a mix of text and variable nodes.Uses regex to find
{...}tokens.Inserts variable nodes for recognized variables, otherwise inserts raw text nodes.
Clears editor root and replaces content with parsed nodes.
Selects end of editor content after insertion.
Effects:
On first render, if a
valueprop is provided, the component parses and inserts the variable nodes programmatically.
Rendering:
Uses
LexicalTypeaheadMenuPluginwith:Custom trigger on
/Options generated by
buildNextOptions()Custom render function that creates a portal rendering the menu UI near the cursor.
The menu lists categories and their options via
VariablePickerMenuItem.
Important Implementation Details / Algorithms
Typeahead Trigger Matching:
UsesuseBasicTypeaheadTriggerMatchfrom Lexical React to activate the menu whenever the user types/. The menu can be triggered even with zero characters after/.Dynamic Filtering:
The filtering logic matches both labels and values of variables to the user input, enabling intuitive searching.Parsing Embedded Variables:
The regex/{([^}]*)}/gis used to identify variables enclosed in curly braces in the initial value text and convert them into structured variable nodes in the editor.Lexical Editor Integration:
The component uses Lexical's update API to mutate the editor state safely, inserting/removing nodes as needed. Custom nodes are created via$createVariableNode.Portal Rendering:
The typeahead menu is rendered outside the normal React tree viaReactDOM.createPortalto position the menu correctly relative to the cursor in the editor.
Interaction with Other Parts of the System
Lexical Editor:
This plugin integrates deeply with the Lexical editor context and APIs, extending its capabilities for variable insertion and parsing.Variable Node (
variable-node.ts):
The$createVariableNodefunction creates specialized Lexical nodes representing variables. This plugin depends on that implementation for rendering and managing variable content inside the editor.Hooks and Constants:
useBuildQueryVariableOptionssupplies the base set of variable options, likely sourced from the application state or API.PromptIdentityandProgrammaticTagare constants used to control insertion behavior and tagging of programmatic updates.
CSS Styling:
Custom styles from./index.cssdefine the appearance of the typeahead menu and its options.
Usage Example
import VariablePickerMenuPlugin from './variable-picker-plugin';
function EditorWithVariablePicker() {
const initialValue = "Hello {userName}, welcome to {platform}!";
const extraOptions = [
{
label: "Custom Variables",
title: "customVars",
options: [
{ label: "User Age", value: "userAge" },
{ label: "Signup Date", value: "signupDate" },
],
},
];
return (
<LexicalComposer initialConfig={/*...*/}>
{/* Other plugins */}
<VariablePickerMenuPlugin value={initialValue} extraOptions={extraOptions} />
{/* Editor UI */}
</LexicalComposer>
);
}
Mermaid Diagram: Component Structure and Interactions
classDiagram
class VariablePickerMenuPlugin {
- editor: LexicalEditor
- queryString: string | null
- options: VariableOption[]
+ buildNextOptions(): VariableOption[]
+ findItemByValue(value: string): VariableInnerOption | undefined
+ onSelectOption(selectedOption: VariableInnerOption, nodeToRemove: TextNode | null, closeMenu: () => void): void
+ parseTextToVariableNodes(text: string): void
+ render(): JSX.Element
}
class VariableOption {
+label: ReactElement | string
+title: string
+options: VariableInnerOption[]
+constructor(label, title, options)
}
class VariableInnerOption {
+label: string
+value: string
+parentLabel: string | JSX.Element
+icon?: ReactNode
+constructor(label, value, parentLabel, icon?)
}
class VariablePickerMenuItem {
+index: number
+option: VariableOption
+selectOptionAndCleanUp(option: VariableOption | VariableInnerOption): void
+render(): JSX.Element
}
VariablePickerMenuPlugin "1" --> "*" VariableOption : builds options
VariableOption "1" --> "*" VariableInnerOption : contains
VariablePickerMenuPlugin "1" --> "1" VariablePickerMenuItem : renders
Summary
variable-picker-plugin.tsx is a specialized React Lexical plugin that provides an intuitive, searchable variable insertion UI triggered by the / character within a rich text editor. It supports nested categories of variables, dynamic filtering, and seamless integration with the Lexical editor state and custom variable nodes. The file is modular, leveraging classes for menu options, React functional components for UI, and Lexical editor APIs for content manipulation.
This plugin is essential for applications that require users to insert dynamic placeholders or variables into text content, such as chatbots, template editors, or advanced form builders.