-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(lexical): add lexical to admin #1666
base: main
Are you sure you want to change the base?
Changes from all commits
d89d1af
b6fbce9
59e6089
35d0287
4208052
1ae16b9
634313c
2b75992
114fbc0
26a667f
a1f5828
8c8f392
e441293
61e92cc
6ec9356
bf8f7b6
ef3f20b
e0f045b
c32f29d
213705b
63415d0
2529cb4
8fcf7a8
9302080
1df9979
2262699
1bcb443
c409792
360491b
77dfb76
18dd2c4
a752d7b
5ccb661
b0721a7
87bba05
b49d786
ff9ce11
91ebc5a
30ac697
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
on: push | ||
on: | ||
name: test endpoints | ||
jobs: | ||
test: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
{ | ||
"presets": ["@babel/preset-env", "@babel/preset-react"], | ||
"presets": ["@babel/preset-env", ["@babel/preset-react", {"runtime": "automatic"}]], | ||
"plugins": ["@babel/plugin-proposal-class-properties"] | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import React, { useState } from "react"; | ||
import PropTypes from "prop-types"; | ||
import { LexicalComposer } from "@lexical/react/LexicalComposer"; | ||
import { ListPlugin } from "@lexical/react/LexicalListPlugin"; | ||
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; | ||
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin"; | ||
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; | ||
import { ContentEditable } from "@lexical/react/LexicalContentEditable"; | ||
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary"; | ||
import { $createParagraphNode, $getRoot } from "lexical"; | ||
import { editorConfig } from "./config"; | ||
import { Toolbar } from "./Toolbar"; | ||
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin"; | ||
import { | ||
$convertFromMarkdownString, | ||
$convertToMarkdownString, | ||
} from "@lexical/markdown"; | ||
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin"; | ||
import TRANSFORMERS from "./transformers"; | ||
|
||
import FloatingLinkEditorPlugin from "./plugins/FloatingLinkEditorPlugin"; | ||
import ListMaxIndentPlugin from "./plugins/ListMaxIndentPlugin"; | ||
import TabIndentationPlugin from "./plugins/TabIndentationPlugin"; | ||
import HorizontalRulePlugin from "./plugins/HorizontalRulePlugin"; | ||
|
||
export const Editor = ({ | ||
content, | ||
onChange, | ||
ariaLabel, | ||
ariaDescribedBy, | ||
lang, | ||
}: { | ||
content: string; | ||
onChange: (value: string) => void; | ||
ariaLabel?: string; | ||
ariaDescribedBy?: string; | ||
lang?: string; | ||
}) => { | ||
const [floatingAnchorElem, setFloatingAnchorElem] = useState< | ||
HTMLDivElement | undefined | ||
>(undefined); | ||
|
||
const editorId = "editor-" + Math.random().toString(36).substr(2, 9); | ||
|
||
const onRef = (_floatingAnchorElem: HTMLDivElement) => { | ||
if (_floatingAnchorElem !== null) { | ||
setFloatingAnchorElem(_floatingAnchorElem); | ||
} | ||
}; | ||
|
||
if (typeof content !== "string") { | ||
content = ""; | ||
} | ||
return ( | ||
<div className="rich-text-wrapper"> | ||
<LexicalComposer | ||
initialConfig={{ | ||
...editorConfig, | ||
editorState: () => { | ||
if (!content) { | ||
const root = $getRoot(); | ||
const paragraphNode = $createParagraphNode(); | ||
root.append(paragraphNode); | ||
return; | ||
} | ||
$convertFromMarkdownString(content, TRANSFORMERS); | ||
}, | ||
}} | ||
> | ||
<Toolbar editorId={editorId} /> | ||
<RichTextPlugin | ||
contentEditable={ | ||
<div | ||
className="editor relative" | ||
ref={onRef} | ||
{...(lang && { lang: lang })} | ||
> | ||
<ContentEditable | ||
className="editor-input focus:outline-blue-focus" | ||
id={editorId} | ||
ariaLabel="Content editor: edit or create your content here. To apply formatting, | ||
select the desired text and press shift+tab to return to the toolbar and select | ||
an option. You can also use markdown formatting directly in the editor." | ||
ariaDescribedBy={ariaDescribedBy && ariaDescribedBy} | ||
Check warning Code scanning / CodeQL Identical operands Warning
Operands
ariaDescribedBy Error loading related location Loading ariaDescribedBy Error loading related location Loading |
||
/> | ||
</div> | ||
} | ||
placeholder={null} | ||
ErrorBoundary={LexicalErrorBoundary} | ||
/> | ||
<HistoryPlugin /> | ||
<OnChangePlugin | ||
onChange={(editorState) => { | ||
editorState.read(() => { | ||
// Read the contents of the EditorState here. | ||
const markdown = $convertToMarkdownString(TRANSFORMERS); | ||
|
||
// Add two spaces to previous line for linebreaks (this is not handled properly by $convertToMarkdownString) | ||
const lines = markdown.split("\n"); | ||
lines.forEach((currentLine, i) => { | ||
if (i > 0) { | ||
const previousLine = lines[i - 1]; | ||
if (previousLine !== "" && currentLine !== "") { | ||
lines[i - 1] = previousLine.trim() + " "; | ||
} | ||
} | ||
}); | ||
onChange(lines.join("\n")); | ||
}); | ||
}} | ||
/> | ||
<LinkPlugin /> | ||
<FloatingLinkEditorPlugin anchorElem={floatingAnchorElem} /> | ||
<ListPlugin /> | ||
<ListMaxIndentPlugin maxDepth={5} /> | ||
{/* <TabIndentationPlugin /> */} | ||
<MarkdownShortcutPlugin transformers={TRANSFORMERS} /> | ||
<HorizontalRulePlugin /> | ||
</LexicalComposer> | ||
</div> | ||
); | ||
}; | ||
|
||
Editor.propTypes = { | ||
id: PropTypes.string, | ||
content: PropTypes.string, | ||
onChange: PropTypes.func, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import React, { useState, useEffect } from "react"; | ||
import { Editor } from "./Editor"; | ||
import { $convertToMarkdownString } from "@lexical/markdown"; | ||
import TRANSFORMERS from "./transformers"; | ||
|
||
type Language = "en" | "fr"; | ||
|
||
export const RichTextEditor = ({ | ||
path, | ||
content, | ||
id, | ||
ariaLabel, | ||
ariaDescribedBy, | ||
}: { | ||
path: string; | ||
content: string; | ||
lang: Language; | ||
id: string; | ||
ariaLabel?: string; | ||
ariaDescribedBy?: string; | ||
}) => { | ||
var textInput = React.createRef(); | ||
Check notice Code scanning / CodeQL Unused variable, import, function or class Note
Unused variable textInput.
|
||
const [value, setValue] = useState(content); | ||
const [text, setText] = useState(""); | ||
|
||
useEffect(() => { | ||
setValue(content); | ||
}, [content]); | ||
|
||
const updateValue = () => { | ||
setText($convertToMarkdownString(TRANSFORMERS)); | ||
}; | ||
|
||
return ( | ||
<div className="w-full bg-white"> | ||
<Editor | ||
content={value} | ||
onChange={updateValue} | ||
ariaLabel={""} | ||
ariaDescribedBy={""} | ||
/> | ||
<input id={id} name={id} value={text} type="hidden" /> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React, { useId, Children } from "react"; | ||
|
||
export const ToolTip = ({ | ||
children, | ||
text, | ||
}: { | ||
children: React.ReactElement; | ||
text: string; | ||
}) => { | ||
const id = `tooltip-${useId()}`; | ||
|
||
return ( | ||
<span className="relative"> | ||
{Children.map(children, (child) => { | ||
return React.cloneElement(child, { | ||
...child.props, | ||
className: child.props.className + " peer", | ||
"aria-labelledby": id, | ||
}); | ||
})} | ||
<span | ||
id={id} | ||
className={`invisible whitespace-nowrap peer-hover:visible peer-focus:visible bg-gray-800 text-white rounded absolute p-1 text-sm -top-10 w-36 text-center -left-14 after:content-[''] after:absolute after:left-1/2 after:top-[100%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-b-transparent after:border-t-gray-700`} | ||
> | ||
{text} | ||
</span> | ||
</span> | ||
); | ||
}; |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class Note