fix(web): jinja2 editor
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 15:17:31
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-02 16:06:03
|
||||
* @Last Modified time: 2026-04-07 15:14:02
|
||||
*/
|
||||
/**
|
||||
* RbMarkdown Component
|
||||
@@ -97,10 +97,6 @@ const buildComponents = (onFormSubmit?: (values: Record<string, any>) => void) =
|
||||
const ctx = useContext(FormContext)
|
||||
return <RbButton {...props} onClick={() => ctx?.onSubmit?.(ctx.values)}>{[children]}</RbButton>
|
||||
},
|
||||
table: ({ children, ...props }: any) => <div className="rb:overflow-x-auto rb:max-w-full"><table className="rb:border rb:border-[#D9D9D9] rb:mb-2" {...props}>{children}</table></div>,
|
||||
tr: ({ children, ...props }: any) => <tr className="rb:border rb:border-[#D9D9D9]" {...props}>{children}</tr>,
|
||||
th: ({ children, ...props }: any) => <th className="rb:border rb:border-[#D9D9D9] rb:px-2 rb:py-1 rb:text-left rb:font-bold" {...props}>{children}</th>,
|
||||
td: ({ children, ...props }: any) => <td className="rb:border rb:border-[#D9D9D9] rb:px-2 rb:py-1 rb:text-left" {...props}>{children}</td>,
|
||||
input: ({ children, ...props }: any) => {
|
||||
const ctx = useContext(FormContext)
|
||||
const handleChange = useCallback((val: any) => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-04-02 15:15:36
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-02 15:15:36
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-07 14:48:00
|
||||
*/
|
||||
import { type FC, useEffect, useMemo } from 'react';
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
||||
@@ -12,7 +12,7 @@ import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
|
||||
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
|
||||
|
||||
import { type Suggestion } from './plugin/AutocompletePlugin';
|
||||
import CharacterCountPlugin from './plugin/CharacterCountPlugin';
|
||||
import Jinjia2CharacterCountPlugin from './plugin/Jinjia2CharacterCountPlugin';
|
||||
import Jinja2InitialValuePlugin from './plugin/Jinja2InitialValuePlugin';
|
||||
import Jinja2AutocompletePlugin from './plugin/Jinja2AutocompletePlugin';
|
||||
import Jinja2HighlightPlugin from './plugin/Jinja2HighlightPlugin';
|
||||
@@ -89,7 +89,7 @@ export interface Jinja2EditorProps {
|
||||
|
||||
const Jinja2Editor: FC<Jinja2EditorProps> = ({
|
||||
placeholder = '请输入内容...',
|
||||
value = '',
|
||||
value,
|
||||
onChange,
|
||||
options = [],
|
||||
variant = 'borderless',
|
||||
@@ -174,8 +174,8 @@ const Jinja2Editor: FC<Jinja2EditorProps> = ({
|
||||
<Jinja2HighlightPlugin />
|
||||
<LineNumberPlugin />
|
||||
<Jinja2AutocompletePlugin options={options} />
|
||||
<CharacterCountPlugin setCount={() => {}} onChange={onChange} waitForInit />
|
||||
<Jinja2InitialValuePlugin value={value} />
|
||||
<Jinjia2CharacterCountPlugin setCount={() => {}} />
|
||||
<Jinja2InitialValuePlugin value={value} onChange={onChange} />
|
||||
<Jinja2BlurPlugin />
|
||||
</div>
|
||||
</LexicalComposer>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-04-02 17:10:59
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-02 17:10:59
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-07 14:50:14
|
||||
*/
|
||||
import { useEffect, useState, useRef, type FC } from 'react';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
@@ -161,7 +161,7 @@ const Jinja2AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) =>
|
||||
{Object.entries(groupedSuggestions).map(([nodeId, nodeOptions]) => (
|
||||
<div key={nodeId}>
|
||||
<Flex align="center" gap={4} className="rb:px-3! rb:text-[12px] rb:py-1.25! rb:font-medium rb:text-[#5B6167]">
|
||||
{nodeOptions[0]?.nodeData?.icon && <img src={nodeOptions[0].nodeData.icon} className="rb:size-3" alt="" />}
|
||||
{nodeOptions[0]?.nodeData?.icon && <div className={`rb:size-3 rb:bg-cover ${nodeOptions[0].nodeData.icon}`} />}
|
||||
{nodeOptions[0]?.nodeData?.name || nodeId}
|
||||
</Flex>
|
||||
{nodeOptions.map((option) => {
|
||||
|
||||
@@ -6,53 +6,50 @@
|
||||
*/
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
import { $getRoot, $createParagraphNode, $createTextNode } from 'lexical';
|
||||
import { $getRoot, $createParagraphNode, $createTextNode, $isParagraphNode } from 'lexical';
|
||||
|
||||
interface Jinja2InitialValuePluginProps {
|
||||
value: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const Jinja2InitialValuePlugin: React.FC<Jinja2InitialValuePluginProps> = ({ value }) => {
|
||||
const Jinja2InitialValuePlugin: React.FC<Jinja2InitialValuePluginProps> = ({ value, onChange }) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const prevValueRef = useRef<string>('');
|
||||
const isUserInputRef = useRef(false);
|
||||
const internalValueRef = useRef<string | undefined>(undefined);
|
||||
const onChangeRef = useRef(onChange);
|
||||
onChangeRef.current = onChange;
|
||||
|
||||
useEffect(() => {
|
||||
return editor.registerUpdateListener(({ editorState, tags }) => {
|
||||
if (tags.has('programmatic')) return;
|
||||
if (internalValueRef.current === undefined) return;
|
||||
editorState.read(() => {
|
||||
const textContent = $getRoot().getTextContent();
|
||||
if (textContent !== prevValueRef.current) {
|
||||
isUserInputRef.current = true;
|
||||
prevValueRef.current = textContent;
|
||||
const paragraphs = $getRoot().getChildren()
|
||||
.filter($isParagraphNode)
|
||||
.map(p => p.getChildren().map(n => n.getTextContent()).join(''));
|
||||
const text = paragraphs.join('\n');
|
||||
if (text !== internalValueRef.current) {
|
||||
internalValueRef.current = text;
|
||||
onChangeRef.current?.(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [editor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (value === prevValueRef.current) return;
|
||||
if (value === undefined) return;
|
||||
if (value === internalValueRef.current) return;
|
||||
|
||||
if (isUserInputRef.current) {
|
||||
prevValueRef.current = value;
|
||||
isUserInputRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
prevValueRef.current = value;
|
||||
isUserInputRef.current = false;
|
||||
|
||||
queueMicrotask(() => {
|
||||
editor.update(() => {
|
||||
const root = $getRoot();
|
||||
root.clear();
|
||||
value.split('\n').forEach((line) => {
|
||||
const paragraph = $createParagraphNode();
|
||||
paragraph.append($createTextNode(line));
|
||||
root.append(paragraph);
|
||||
});
|
||||
}, { tag: 'programmatic' });
|
||||
});
|
||||
internalValueRef.current = value;
|
||||
editor.update(() => {
|
||||
const root = $getRoot();
|
||||
root.clear();
|
||||
value.split('\n').forEach((line) => {
|
||||
const paragraph = $createParagraphNode();
|
||||
paragraph.append($createTextNode(line));
|
||||
root.append(paragraph);
|
||||
});
|
||||
}, { tag: 'programmatic' });
|
||||
}, [value, editor]);
|
||||
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useEffect } from 'react';
|
||||
import { $getRoot, $isParagraphNode } from 'lexical';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
|
||||
const Jinjia2CharacterCountPlugin = ({ setCount }: { setCount: (count: number) => void }) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
useEffect(() => {
|
||||
return editor.registerUpdateListener(({ editorState }) => {
|
||||
editorState.read(() => {
|
||||
const root = $getRoot();
|
||||
const paragraphs = root.getChildren()
|
||||
.filter($isParagraphNode)
|
||||
.map(p => p.getChildren().map(n => n.getTextContent()).join(''));
|
||||
setCount(paragraphs.join('\n').length);
|
||||
});
|
||||
});
|
||||
}, [editor, setCount]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Jinjia2CharacterCountPlugin
|
||||
Reference in New Issue
Block a user