Skip to content
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

Draft
wants to merge 39 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d89d1af
feat(lexical): add lexical to admin and try it out on the welcome ppage
andrewleith Sep 18, 2023
b6fbce9
chore(formatting): add missing semi-colon
andrewleith Sep 18, 2023
59e6089
chore: remove 1st version of lexical
andrewleith Sep 25, 2023
35d0287
chore: add generated assets
andrewleith Sep 25, 2023
4208052
chore: update react, react-dom; add webpack support for typescript
andrewleith Sep 25, 2023
1ae16b9
chore(test): fix test -remove expression that linter says conditional…
andrewleith Sep 25, 2023
634313c
chore: formatting
andrewleith Sep 25, 2023
2b75992
Merge branch 'main' into experiment/use-lexical-editor
andrewleith Sep 25, 2023
114fbc0
feat(lexical): make react app into a jinja component 🤯
andrewleith Sep 26, 2023
26a667f
chore: formatting
andrewleith Sep 26, 2023
a1f5828
chore: remove unused imports
andrewleith Sep 26, 2023
8c8f392
chore: formatting
andrewleith Sep 26, 2023
e441293
chore: removed unused images
andrewleith Sep 27, 2023
61e92cc
chore: remove more unused images
andrewleith Sep 27, 2023
6ec9356
chore: remove unused files
andrewleith Sep 27, 2023
bf8f7b6
style(gulp): make toolbar icons from fontawesome available
andrewleith Sep 27, 2023
ef3f20b
chore(build): re-gen js and css
andrewleith Sep 27, 2023
e0f045b
style(lexical): update toolbar css, use FA icons for toolbar
andrewleith Sep 27, 2023
c32f29d
chore: rebuild static assets
andrewleith Sep 27, 2023
213705b
chore: remove unsused id in query string
andrewleith Sep 27, 2023
63415d0
feat(tabbing): EXPERIMENTAL: add support for tabbing to indent
andrewleith Oct 3, 2023
2529cb4
feat(lexical): add HR support
andrewleith Oct 11, 2023
8fcf7a8
fix(lexical): fix heading structure to use h1/h2 like notify already …
andrewleith Nov 3, 2023
9302080
chore: asset update
andrewleith Nov 3, 2023
1df9979
feat(headings): only allow 2 levels of headings when typing out markdown
andrewleith Nov 6, 2023
2262699
feat(toolbar): make toolbar stay in viewport when scrolling
andrewleith Nov 6, 2023
1bcb443
style(toolbar): add missing icon
andrewleith Nov 6, 2023
c409792
a11y: fix up labels on various elements; disabled tabIndentationPlugin
andrewleith Nov 14, 2023
360491b
style(link editor): remove blue color
andrewleith Nov 14, 2023
77dfb76
feat(fable test): add a test page for fable testing; this commit shou…
andrewleith Nov 14, 2023
18dd2c4
chore: regen resources
andrewleith Nov 14, 2023
a752d7b
chore: formatting
andrewleith Nov 14, 2023
5ccb661
style(link editor): add edit icon to link editor
andrewleith Nov 14, 2023
b0721a7
feat(tooltips): emulate i18n library for now
andrewleith Nov 14, 2023
87bba05
TEMPORARY: remove endpoint test to see if PR env will deply
andrewleith Nov 14, 2023
b49d786
chore: regen static resources
andrewleith Nov 14, 2023
ff9ce11
task: avoid bug when link is the last thing in a template
andrewleith Nov 14, 2023
91ebc5a
Merge branch 'main' into experiment/use-lexical-editor
andrewleith May 8, 2024
30ac697
chore: regen tailwind
andrewleith May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_endpoints.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
on: push
on:
name: test endpoints
jobs:
test:
Expand Down
2 changes: 1 addition & 1 deletion app/.babelrc
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"]
}
2 changes: 2 additions & 0 deletions app/assets/javascripts/lexical.min.js

Large diffs are not rendered by default.

128 changes: 128 additions & 0 deletions app/assets/javascripts/lexical/Editor.tsx
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";

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import 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
and
ariaDescribedBy
are identical.
/>
</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,
};
45 changes: 45 additions & 0 deletions app/assets/javascripts/lexical/RichTextEditor.tsx
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>
);
};
29 changes: 29 additions & 0 deletions app/assets/javascripts/lexical/ToolTip.tsx
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>
);
};
Loading
Loading