Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ docs
packages/gamut-icons/src/icons
packages/gamut-patterns/src/patterns
.nx
packages/gamut/src/LexicalEditor
packages/gamut/src/LexicalEditor/**/*.tsx
packages/gamut/src/LexicalEditor/**/*.js
20 changes: 15 additions & 5 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,34 @@ module.exports = {
'plugin:react/jsx-runtime',
],

plugins: ['eslint-plugin-gamut'],
plugins: [],

rules: {
'gamut/prefer-themed': 'error',
'gamut/no-css-standalone': 'error',
'gamut/import-paths': 'error',
'gamut/prefer-themed': 'warn',
'gamut/no-css-standalone': 'warn',
'gamut/import-paths': 'warn',
'import/no-extraneous-dependencies': 'off',
"@typescript-eslint/no-unused-vars": ["warn", { "varsIgnorePattern": "^React$" }]

},

overrides: [
{
files: ['**/typings/*', '*.d.ts'],
rules: {
'@typescript-eslint/no-namespace': 'off',
"@typescript-eslint/no-unused-vars": ["warn", { "varsIgnorePattern": "^React$" }]

},
},
{
files: ['*.mdx'],
rules: {
'gamut/import-paths': 'off',
"react/*": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/no-unused-vars": ["warn", { "varsIgnorePattern": "^React$" }]

},
},
{
Expand All @@ -46,13 +54,15 @@ module.exports = {
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/restrict-plus-operands': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
"@typescript-eslint/no-unused-vars": ["warn", { "varsIgnorePattern": "^React$" }]

},
},
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
plugins: ['lodash'],
rules: {
'lodash/import-scope': ['error', 'method'],
'lodash/import-scope': ['warn', 'method'],
},
},
],
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"lerna": "7.2.0",
"lint-staged": "14.0.1",
"lodash": "^4.17.5",
"lodash-es": "^4.17.21",
"micromatch": "^4.0.5",
"mutationobserver-shim": "^0.3.3",
"nx": "16.8.1",
Expand All @@ -67,7 +68,9 @@
"ts-jest": "29.1.1",
"ts-node": "10.9.1",
"tslib": "2.4.0",
"typescript": "5.1.3"
"typescript": "5.1.3",
"y-websocket": "^2.0.4",
"yjs": "^13.6.19"
},
"devDependencies": {
"eslint-plugin-lodash": "^7.4.0",
Expand Down
15 changes: 15 additions & 0 deletions packages/gamut/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
"version": "57.2.2",
"author": "Codecademy Engineering <[email protected]>",
"dependencies": {
"@lexical/clipboard": "0.13.1",
"@lexical/code": "0.13.1",
"@lexical/file": "0.13.1",
"@lexical/hashtag": "0.13.1",
"@lexical/link": "0.13.1",
"@lexical/list": "0.13.1",
"@lexical/mark": "0.13.1",
"@lexical/overflow": "0.13.1",
"@lexical/plain-text": "0.13.1",
"@lexical/react": "0.13.1",
"@lexical/rich-text": "0.13.1",
"@lexical/selection": "0.13.1",
"@lexical/table": "0.13.1",
"@lexical/utils": "0.13.1",
"lexical": "0.13.1",
"@codecademy/gamut-icons": "9.31.0",
"@codecademy/gamut-illustrations": "0.48.0",
"@codecademy/gamut-patterns": "0.9.14",
Expand Down
202 changes: 202 additions & 0 deletions packages/gamut/src/LexicalEditor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
import {CharacterLimitPlugin} from '@lexical/react/LexicalCharacterLimitPlugin';
import {CheckListPlugin} from '@lexical/react/LexicalCheckListPlugin';
import {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin';
// import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {HashtagPlugin} from '@lexical/react/LexicalHashtagPlugin';
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
import {HorizontalRulePlugin} from '@lexical/react/LexicalHorizontalRulePlugin';
import {ListPlugin} from '@lexical/react/LexicalListPlugin';
import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
import {TabIndentationPlugin} from '@lexical/react/LexicalTabIndentationPlugin';
import {useEffect, useState} from 'react';
// import {CAN_USE_DOM} from './shared/canUseDOM';

// import {createWebsocketProvider} from './collaboration';
import {useSettings} from './context/SettingsContext';
import {useSharedHistoryContext} from './context/SharedHistoryContext';
// import ActionsPlugin from './plugins/ActionsPlugin';
import AutocompletePlugin from './plugins/AutocompletePlugin';
// import AutoEmbedPlugin from './plugins/AutoEmbedPlugin';
import AutoLinkPlugin from './plugins/AutoLinkPlugin';
// import CodeActionMenuPlugin from './plugins/CodeActionMenuPlugin';
import CodeHighlightPlugin from './plugins/CodeHighlightPlugin';
import ComponentPickerPlugin from './plugins/ComponentPickerPlugin';
import ContextMenuPlugin from './plugins/ContextMenuPlugin';
import FloatingLinkEditorPlugin from './plugins/FloatingLinkEditorPlugin';
import FloatingTextFormatToolbarPlugin from './plugins/FloatingTextFormatToolbarPlugin';
import KeywordsPlugin from './plugins/KeywordsPlugin';
import {LayoutPlugin} from './plugins/LayoutPlugin/LayoutPlugin';
import LinkPlugin from './plugins/LinkPlugin';
import ListMaxIndentLevelPlugin from './plugins/ListMaxIndentLevelPlugin';
import MarkdownShortcutPlugin from './plugins/MarkdownShortcutPlugin';
import {MaxLengthPlugin} from './plugins/MaxLengthPlugin';
import MentionsPlugin from './plugins/MentionsPlugin';
import TabFocusPlugin from './plugins/TabFocusPlugin';
import TableCellActionMenuPlugin from './plugins/TableActionMenuPlugin';
import TableOfContentsPlugin from './plugins/TableOfContentsPlugin';
import ToolbarPlugin from './plugins/ToolbarPlugin';
import TreeViewPlugin from './plugins/TreeViewPlugin';
import ContentEditable from './ui/ContentEditable';

const skipCollaborationInit =
// @ts-expect-error
window.parent != null && window.parent.frames.right === window;

export default function Editor(): JSX.Element {
const {historyState} = useSharedHistoryContext();
const {
settings: {
isCollab,
isAutocomplete,
isMaxLength,
isCharLimit,
isCharLimitUtf8,
isRichText,
showTreeView,
showTableOfContents,
shouldUseLexicalContextMenu,
},
} = useSettings();
const text = isCollab
? 'Enter some collaborative rich text...'
: isRichText
? 'Enter some rich text...'
: 'Enter some plain text...';
const [floatingAnchorElem, setFloatingAnchorElem] =
useState<HTMLDivElement | null>(null);
const [isLinkEditMode, setIsLinkEditMode] = useState<boolean>(false);

const onRef = (_floatingAnchorElem: HTMLDivElement) => {
if (_floatingAnchorElem !== null) {
setFloatingAnchorElem(_floatingAnchorElem);
}
};

useEffect(() => {
const updateViewPortWidth = () => {
// const isNextSmallWidthViewport =
// CAN_USE_DOM && window.matchMedia('(max-width: 1025px)').matches;

// if (isNextSmallWidthViewport !== isSmallWidthViewport) {
// setIsSmallWidthViewport(isNextSmallWidthViewport);
// }
};
updateViewPortWidth();
window.addEventListener('resize', updateViewPortWidth);

return () => {
window.removeEventListener('resize', updateViewPortWidth);
};
}, []);

return (
<>
{isRichText && <ToolbarPlugin setIsLinkEditMode={setIsLinkEditMode} />}
<div
className={`editor-container ${showTreeView ? 'tree-view' : ''} ${
!isRichText ? 'plain-text' : ''
}`}>
{isMaxLength && <MaxLengthPlugin maxLength={30} />}
<AutoFocusPlugin />
<ClearEditorPlugin />
<ComponentPickerPlugin />
{/* <AutoEmbedPlugin /> */}

<MentionsPlugin />
<HashtagPlugin />
<KeywordsPlugin />
<AutoLinkPlugin />
{isRichText ? (
<>
{/* {isCollab ? (
// <CollaborationPlugin
// id="main"
// providerFactory={createWebsocketProvider}
// shouldBootstrap={!skipCollaborationInit}
// />
) : (
<HistoryPlugin externalHistoryState={historyState} />
)} */}
<RichTextPlugin
contentEditable={
<div className="editor-scroller">
<div className="editor" ref={onRef}>
<ContentEditable />
</div>
</div>
}
placeholder={<></>}
ErrorBoundary={LexicalErrorBoundary}
/>
<MarkdownShortcutPlugin />
<CodeHighlightPlugin />
<ListPlugin />
<CheckListPlugin />
<ListMaxIndentLevelPlugin maxDepth={7} />
{/* <TablePlugin
hasCellMerge={tableCellMerge}
hasCellBackgroundColor={tableCellBackgroundColor}
/> */}
<LinkPlugin />
<HorizontalRulePlugin />
<TabFocusPlugin />
<TabIndentationPlugin />
<LayoutPlugin />
{floatingAnchorElem && (
<>
{/* <CodeActionMenuPlugin anchorElem={floatingAnchorElem} /> */}
<FloatingLinkEditorPlugin
anchorElem={floatingAnchorElem}
isLinkEditMode={isLinkEditMode}
setIsLinkEditMode={setIsLinkEditMode}
/>
<TableCellActionMenuPlugin
anchorElem={floatingAnchorElem}
cellMerge={true}
/>
<FloatingTextFormatToolbarPlugin
anchorElem={floatingAnchorElem}
setIsLinkEditMode={setIsLinkEditMode}
/>
</>
)}
</>
) : (
<>
<PlainTextPlugin
contentEditable={<ContentEditable />}
placeholder={<></>}
ErrorBoundary={LexicalErrorBoundary}
/>
<HistoryPlugin externalHistoryState={historyState} />
</>
)}
{(isCharLimit || isCharLimitUtf8) && (
<CharacterLimitPlugin
charset={isCharLimit ? 'UTF-16' : 'UTF-8'}
maxLength={5}
/>
)}
{isAutocomplete && <AutocompletePlugin />}
<div>{showTableOfContents && <TableOfContentsPlugin />}</div>
{shouldUseLexicalContextMenu && <ContextMenuPlugin />}
{/* <ActionsPlugin
isRichText={isRichText}
shouldPreserveNewLinesInMarkdown={shouldPreserveNewLinesInMarkdown}
/> */}
</div>
{showTreeView && <TreeViewPlugin />}
</>
);
}
Loading