lexical: remove unused nodes
This commit is contained in:
parent
bc3662afc7
commit
e712d7bb52
5 changed files with 6 additions and 574 deletions
|
@ -51,15 +51,10 @@
|
||||||
"@fontsource/roboto-mono": "^5.0.0",
|
"@fontsource/roboto-mono": "^5.0.0",
|
||||||
"@gamestdio/websocket": "^0.3.2",
|
"@gamestdio/websocket": "^0.3.2",
|
||||||
"@lexical/clipboard": "^0.11.3",
|
"@lexical/clipboard": "^0.11.3",
|
||||||
"@lexical/code": "^0.11.3",
|
|
||||||
"@lexical/hashtag": "^0.11.3",
|
"@lexical/hashtag": "^0.11.3",
|
||||||
"@lexical/html": "^0.11.3",
|
|
||||||
"@lexical/link": "^0.11.3",
|
"@lexical/link": "^0.11.3",
|
||||||
"@lexical/list": "^0.11.3",
|
|
||||||
"@lexical/react": "^0.11.3",
|
"@lexical/react": "^0.11.3",
|
||||||
"@lexical/rich-text": "^0.11.3",
|
|
||||||
"@lexical/selection": "^0.11.3",
|
"@lexical/selection": "^0.11.3",
|
||||||
"@lexical/table": "^0.11.3",
|
|
||||||
"@lexical/utils": "^0.11.3",
|
"@lexical/utils": "^0.11.3",
|
||||||
"@popperjs/core": "^2.11.5",
|
"@popperjs/core": "^2.11.5",
|
||||||
"@reach/combobox": "^0.18.0",
|
"@reach/combobox": "^0.18.0",
|
||||||
|
|
|
@ -1,359 +0,0 @@
|
||||||
/**
|
|
||||||
* This source code is derived from code from Meta Platforms, Inc.
|
|
||||||
* and affiliates, licensed under the MIT license located in the
|
|
||||||
* LICENSE file in the /app/soapbox/features/compose/editor directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
||||||
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
|
|
||||||
import { mergeRegister } from '@lexical/utils';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { List as ImmutableList } from 'immutable';
|
|
||||||
import {
|
|
||||||
$getNodeByKey,
|
|
||||||
$getSelection,
|
|
||||||
$isNodeSelection,
|
|
||||||
$setSelection,
|
|
||||||
CLICK_COMMAND,
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
DRAGSTART_COMMAND,
|
|
||||||
KEY_BACKSPACE_COMMAND,
|
|
||||||
KEY_DELETE_COMMAND,
|
|
||||||
KEY_ENTER_COMMAND,
|
|
||||||
KEY_ESCAPE_COMMAND,
|
|
||||||
SELECTION_CHANGE_COMMAND,
|
|
||||||
} from 'lexical';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
|
||||||
import { HStack, IconButton } from 'soapbox/components/ui';
|
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
|
||||||
import { normalizeAttachment } from 'soapbox/normalizers';
|
|
||||||
|
|
||||||
import { $isImageNode } from './image-node';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
GridSelection,
|
|
||||||
LexicalEditor,
|
|
||||||
NodeKey,
|
|
||||||
NodeSelection,
|
|
||||||
RangeSelection,
|
|
||||||
} from 'lexical';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const imageCache = new Set();
|
|
||||||
|
|
||||||
const useSuspenseImage = (src: string) => {
|
|
||||||
if (!imageCache.has(src)) {
|
|
||||||
throw new Promise((resolve) => {
|
|
||||||
const img = new Image();
|
|
||||||
img.src = src;
|
|
||||||
img.onload = () => {
|
|
||||||
imageCache.add(src);
|
|
||||||
resolve(null);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const LazyImage = ({
|
|
||||||
altText,
|
|
||||||
className,
|
|
||||||
imageRef,
|
|
||||||
src,
|
|
||||||
}: {
|
|
||||||
altText: string
|
|
||||||
className: string | null
|
|
||||||
imageRef: {current: null | HTMLImageElement}
|
|
||||||
src: string
|
|
||||||
}): JSX.Element => {
|
|
||||||
useSuspenseImage(src);
|
|
||||||
return (
|
|
||||||
<img
|
|
||||||
className={className || undefined}
|
|
||||||
src={src}
|
|
||||||
alt={altText}
|
|
||||||
ref={imageRef}
|
|
||||||
draggable='false'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ImageComponent = ({
|
|
||||||
src,
|
|
||||||
altText,
|
|
||||||
nodeKey,
|
|
||||||
}: {
|
|
||||||
altText: string
|
|
||||||
nodeKey: NodeKey
|
|
||||||
src: string
|
|
||||||
}): JSX.Element => {
|
|
||||||
const intl = useIntl();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const imageRef = useRef<null | HTMLImageElement>(null);
|
|
||||||
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
||||||
const [isSelected, setSelected, clearSelection] =
|
|
||||||
useLexicalNodeSelection(nodeKey);
|
|
||||||
const [editor] = useLexicalComposerContext();
|
|
||||||
const [selection, setSelection] = useState<
|
|
||||||
RangeSelection | NodeSelection | GridSelection | null
|
|
||||||
>(null);
|
|
||||||
const activeEditorRef = useRef<LexicalEditor | null>(null);
|
|
||||||
|
|
||||||
const [hovered, setHovered] = useState(false);
|
|
||||||
const [focused, setFocused] = useState(false);
|
|
||||||
const [dirtyDescription, setDirtyDescription] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const deleteNode = useCallback(
|
|
||||||
() => {
|
|
||||||
editor.update(() => {
|
|
||||||
const node = $getNodeByKey(nodeKey);
|
|
||||||
if ($isImageNode(node)) {
|
|
||||||
node.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[nodeKey],
|
|
||||||
);
|
|
||||||
|
|
||||||
const previewImage = () => {
|
|
||||||
const image = normalizeAttachment({
|
|
||||||
type: 'image',
|
|
||||||
url: src,
|
|
||||||
altText,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(image), index: 0 }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDelete = useCallback(
|
|
||||||
(payload: KeyboardEvent) => {
|
|
||||||
if (isSelected && $isNodeSelection($getSelection())) {
|
|
||||||
const event: KeyboardEvent = payload;
|
|
||||||
event.preventDefault();
|
|
||||||
deleteNode();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
[isSelected, nodeKey],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onEnter = useCallback(
|
|
||||||
(event: KeyboardEvent) => {
|
|
||||||
const latestSelection = $getSelection();
|
|
||||||
const buttonElem = buttonRef.current;
|
|
||||||
if (isSelected && $isNodeSelection(latestSelection) && latestSelection.getNodes().length === 1) {
|
|
||||||
if (buttonElem !== null && buttonElem !== document.activeElement) {
|
|
||||||
event.preventDefault();
|
|
||||||
buttonElem.focus();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
[isSelected],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onEscape = useCallback(
|
|
||||||
(event: KeyboardEvent) => {
|
|
||||||
if (buttonRef.current === event.target) {
|
|
||||||
$setSelection(null);
|
|
||||||
editor.update(() => {
|
|
||||||
setSelected(true);
|
|
||||||
const parentRootElement = editor.getRootElement();
|
|
||||||
if (parentRootElement !== null) {
|
|
||||||
parentRootElement.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
[editor, setSelected],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleKeyDown: React.KeyboardEventHandler = (e) => {
|
|
||||||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
|
||||||
handleInputBlur();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputBlur = () => {
|
|
||||||
setFocused(false);
|
|
||||||
|
|
||||||
if (dirtyDescription !== null) {
|
|
||||||
editor.update(() => {
|
|
||||||
const node = $getNodeByKey(nodeKey);
|
|
||||||
if ($isImageNode(node)) {
|
|
||||||
node.setAltText(dirtyDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
setDirtyDescription(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputChange: React.ChangeEventHandler<HTMLTextAreaElement> = e => {
|
|
||||||
setDirtyDescription(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
|
||||||
setHovered(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
|
||||||
setHovered(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputFocus = () => {
|
|
||||||
setFocused(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
setFocused(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let isMounted = true;
|
|
||||||
const unregister = mergeRegister(
|
|
||||||
editor.registerUpdateListener(({ editorState }) => {
|
|
||||||
if (isMounted) {
|
|
||||||
setSelection(editorState.read(() => $getSelection()));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
editor.registerCommand(
|
|
||||||
SELECTION_CHANGE_COMMAND,
|
|
||||||
(_, activeEditor) => {
|
|
||||||
activeEditorRef.current = activeEditor;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
),
|
|
||||||
editor.registerCommand<MouseEvent>(
|
|
||||||
CLICK_COMMAND,
|
|
||||||
(payload) => {
|
|
||||||
const event = payload;
|
|
||||||
|
|
||||||
if (event.target === imageRef.current) {
|
|
||||||
if (event.shiftKey) {
|
|
||||||
setSelected(!isSelected);
|
|
||||||
} else {
|
|
||||||
clearSelection();
|
|
||||||
setSelected(true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
),
|
|
||||||
editor.registerCommand(
|
|
||||||
DRAGSTART_COMMAND,
|
|
||||||
(event) => {
|
|
||||||
if (event.target === imageRef.current) {
|
|
||||||
// TODO This is just a temporary workaround for FF to behave like other browsers.
|
|
||||||
// Ideally, this handles drag & drop too (and all browsers).
|
|
||||||
event.preventDefault();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
),
|
|
||||||
editor.registerCommand(
|
|
||||||
KEY_DELETE_COMMAND,
|
|
||||||
onDelete,
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
),
|
|
||||||
editor.registerCommand(
|
|
||||||
KEY_BACKSPACE_COMMAND,
|
|
||||||
onDelete,
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
),
|
|
||||||
editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW),
|
|
||||||
editor.registerCommand(
|
|
||||||
KEY_ESCAPE_COMMAND,
|
|
||||||
onEscape,
|
|
||||||
COMMAND_PRIORITY_LOW,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
unregister();
|
|
||||||
};
|
|
||||||
}, [
|
|
||||||
clearSelection,
|
|
||||||
editor,
|
|
||||||
isSelected,
|
|
||||||
nodeKey,
|
|
||||||
onDelete,
|
|
||||||
onEnter,
|
|
||||||
onEscape,
|
|
||||||
setSelected,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const active = hovered || focused;
|
|
||||||
const description = dirtyDescription || (dirtyDescription !== '' && altText) || '';
|
|
||||||
const draggable = isSelected && $isNodeSelection(selection);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Suspense fallback={null}>
|
|
||||||
<>
|
|
||||||
<div className='relative' draggable={draggable} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} onClick={handleClick} role='button'>
|
|
||||||
<HStack className='absolute right-2 top-2 z-10' space={2}>
|
|
||||||
<IconButton
|
|
||||||
onClick={previewImage}
|
|
||||||
src={require('@tabler/icons/zoom-in.svg')}
|
|
||||||
theme='dark'
|
|
||||||
className='!p-1.5 hover:scale-105 hover:bg-gray-900'
|
|
||||||
iconClassName='h-5 w-5'
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
onClick={deleteNode}
|
|
||||||
src={require('@tabler/icons/x.svg')}
|
|
||||||
theme='dark'
|
|
||||||
className='!p-1.5 hover:scale-105 hover:bg-gray-900'
|
|
||||||
iconClassName='h-5 w-5'
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
|
|
||||||
<div className={clsx('compose-form__upload-description', { active })}>
|
|
||||||
<label>
|
|
||||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span>
|
|
||||||
|
|
||||||
<textarea
|
|
||||||
placeholder={intl.formatMessage(messages.description)}
|
|
||||||
value={description}
|
|
||||||
onFocus={handleInputFocus}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<LazyImage
|
|
||||||
className={
|
|
||||||
clsx('cursor-default', {
|
|
||||||
'select-none': isSelected,
|
|
||||||
'cursor-grab active:cursor-grabbing': isSelected && $isNodeSelection(selection),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
src={src}
|
|
||||||
altText={altText}
|
|
||||||
imageRef={imageRef}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImageComponent;
|
|
|
@ -1,179 +0,0 @@
|
||||||
/**
|
|
||||||
* This source code is derived from code from Meta Platforms, Inc.
|
|
||||||
* and affiliates, licensed under the MIT license located in the
|
|
||||||
* LICENSE file in the /app/soapbox/features/compose/editor directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { $applyNodeReplacement, DecoratorNode } from 'lexical';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { Suspense } from 'react';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
DOMConversionMap,
|
|
||||||
DOMConversionOutput,
|
|
||||||
DOMExportOutput,
|
|
||||||
EditorConfig,
|
|
||||||
LexicalNode,
|
|
||||||
NodeKey,
|
|
||||||
SerializedLexicalNode,
|
|
||||||
Spread,
|
|
||||||
} from 'lexical';
|
|
||||||
|
|
||||||
const ImageComponent = React.lazy(() => import('./image-component'));
|
|
||||||
|
|
||||||
interface ImagePayload {
|
|
||||||
altText?: string
|
|
||||||
key?: NodeKey
|
|
||||||
src: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const convertImageElement = (domNode: Node): null | DOMConversionOutput => {
|
|
||||||
if (domNode instanceof HTMLImageElement) {
|
|
||||||
const { alt: altText, src } = domNode;
|
|
||||||
const node = $createImageNode({ altText, src });
|
|
||||||
return { node };
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SerializedImageNode = Spread<
|
|
||||||
{
|
|
||||||
altText: string
|
|
||||||
src: string
|
|
||||||
},
|
|
||||||
SerializedLexicalNode
|
|
||||||
>;
|
|
||||||
|
|
||||||
class ImageNode extends DecoratorNode<JSX.Element> {
|
|
||||||
|
|
||||||
__src: string;
|
|
||||||
__altText: string;
|
|
||||||
|
|
||||||
static getType(): string {
|
|
||||||
return 'image';
|
|
||||||
}
|
|
||||||
|
|
||||||
static clone(node: ImageNode): ImageNode {
|
|
||||||
return new ImageNode(
|
|
||||||
node.__src,
|
|
||||||
node.__altText,
|
|
||||||
node.__key,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedImageNode): ImageNode {
|
|
||||||
const { altText, src } =
|
|
||||||
serializedNode;
|
|
||||||
const node = $createImageNode({
|
|
||||||
altText,
|
|
||||||
src,
|
|
||||||
});
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
exportDOM(): DOMExportOutput {
|
|
||||||
const element = document.createElement('img');
|
|
||||||
element.setAttribute('src', this.__src);
|
|
||||||
element.setAttribute('alt', this.__altText);
|
|
||||||
return { element };
|
|
||||||
}
|
|
||||||
|
|
||||||
static importDOM(): DOMConversionMap | null {
|
|
||||||
return {
|
|
||||||
img: (node: Node) => ({
|
|
||||||
conversion: convertImageElement,
|
|
||||||
priority: 0,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
src: string,
|
|
||||||
altText: string,
|
|
||||||
key?: NodeKey,
|
|
||||||
) {
|
|
||||||
super(key);
|
|
||||||
this.__src = src;
|
|
||||||
this.__altText = altText;
|
|
||||||
}
|
|
||||||
|
|
||||||
exportJSON(): SerializedImageNode {
|
|
||||||
return {
|
|
||||||
altText: this.getAltText(),
|
|
||||||
src: this.getSrc(),
|
|
||||||
type: 'image',
|
|
||||||
version: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// View
|
|
||||||
|
|
||||||
createDOM(config: EditorConfig): HTMLElement {
|
|
||||||
const span = document.createElement('span');
|
|
||||||
const theme = config.theme;
|
|
||||||
const className = theme.image;
|
|
||||||
if (className !== undefined) {
|
|
||||||
span.className = className;
|
|
||||||
}
|
|
||||||
return span;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDOM(): false {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSrc(): string {
|
|
||||||
return this.__src;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAltText(): string {
|
|
||||||
return this.__altText;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAltText(altText: string): void {
|
|
||||||
const writable = this.getWritable();
|
|
||||||
|
|
||||||
if (altText !== undefined) {
|
|
||||||
writable.__altText = altText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decorate(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Suspense fallback={null}>
|
|
||||||
<ImageComponent
|
|
||||||
src={this.__src}
|
|
||||||
altText={this.__altText}
|
|
||||||
nodeKey={this.getKey()}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const $createImageNode = ({
|
|
||||||
altText = '',
|
|
||||||
src,
|
|
||||||
key,
|
|
||||||
}: ImagePayload): ImageNode => {
|
|
||||||
return $applyNodeReplacement(
|
|
||||||
new ImageNode(
|
|
||||||
src,
|
|
||||||
altText,
|
|
||||||
key,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const $isImageNode = (
|
|
||||||
node: LexicalNode | null | undefined,
|
|
||||||
): node is ImageNode => node instanceof ImageNode;
|
|
||||||
|
|
||||||
export {
|
|
||||||
type ImagePayload,
|
|
||||||
type SerializedImageNode,
|
|
||||||
ImageNode,
|
|
||||||
$createImageNode,
|
|
||||||
$isImageNode,
|
|
||||||
};
|
|
|
@ -4,25 +4,15 @@
|
||||||
* LICENSE file in the /app/soapbox/features/compose/editor directory.
|
* LICENSE file in the /app/soapbox/features/compose/editor directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CodeHighlightNode, CodeNode } from '@lexical/code';
|
|
||||||
import { HashtagNode } from '@lexical/hashtag';
|
import { HashtagNode } from '@lexical/hashtag';
|
||||||
import { AutoLinkNode, LinkNode } from '@lexical/link';
|
import { AutoLinkNode } from '@lexical/link';
|
||||||
import { ListItemNode, ListNode } from '@lexical/list';
|
|
||||||
import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
||||||
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
|
|
||||||
|
|
||||||
import { useFeatures, useInstance } from 'soapbox/hooks';
|
|
||||||
|
|
||||||
import { EmojiNode } from './emoji-node';
|
import { EmojiNode } from './emoji-node';
|
||||||
import { ImageNode } from './image-node';
|
|
||||||
import { MentionNode } from './mention-node';
|
import { MentionNode } from './mention-node';
|
||||||
|
|
||||||
import type { Klass, LexicalNode } from 'lexical';
|
import type { Klass, LexicalNode } from 'lexical';
|
||||||
|
|
||||||
const useNodes = () => {
|
const useNodes = () => {
|
||||||
const features = useFeatures();
|
|
||||||
const instance = useInstance();
|
|
||||||
|
|
||||||
const nodes: Array<Klass<LexicalNode>> = [
|
const nodes: Array<Klass<LexicalNode>> = [
|
||||||
AutoLinkNode,
|
AutoLinkNode,
|
||||||
HashtagNode,
|
HashtagNode,
|
||||||
|
@ -30,21 +20,6 @@ const useNodes = () => {
|
||||||
MentionNode,
|
MentionNode,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (features.richText) {
|
|
||||||
nodes.push(
|
|
||||||
CodeHighlightNode,
|
|
||||||
CodeNode,
|
|
||||||
HorizontalRuleNode,
|
|
||||||
LinkNode,
|
|
||||||
ListItemNode,
|
|
||||||
ListNode,
|
|
||||||
QuoteNode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance.pleroma.getIn(['metadata', 'markup', 'allow_headings'])) nodes.push(HeadingNode);
|
|
||||||
if (instance.pleroma.getIn(['metadata', 'markup', 'allow_inline_images'])) nodes.push(ImageNode);
|
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -1709,7 +1709,7 @@
|
||||||
"@lexical/selection" "0.11.3"
|
"@lexical/selection" "0.11.3"
|
||||||
"@lexical/utils" "0.11.3"
|
"@lexical/utils" "0.11.3"
|
||||||
|
|
||||||
"@lexical/code@0.11.3", "@lexical/code@^0.11.3":
|
"@lexical/code@0.11.3":
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/@lexical/code/-/code-0.11.3.tgz#4a5ef193655557859c63dd4a54012c78580585fa"
|
resolved "https://registry.yarnpkg.com/@lexical/code/-/code-0.11.3.tgz#4a5ef193655557859c63dd4a54012c78580585fa"
|
||||||
integrity sha512-BIMPd2op65iP4N9SkKIUVodZoWeSsnk6skNJ8UHBO/Rg0ZxyAqxLpnBhEgHq2QOoTBbEW6OEFtkc7/+f9LINZg==
|
integrity sha512-BIMPd2op65iP4N9SkKIUVodZoWeSsnk6skNJ8UHBO/Rg0ZxyAqxLpnBhEgHq2QOoTBbEW6OEFtkc7/+f9LINZg==
|
||||||
|
@ -1736,7 +1736,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@lexical/utils" "0.11.3"
|
"@lexical/utils" "0.11.3"
|
||||||
|
|
||||||
"@lexical/html@0.11.3", "@lexical/html@^0.11.3":
|
"@lexical/html@0.11.3":
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/@lexical/html/-/html-0.11.3.tgz#c02b38f512eb808726922c8215dd2374df6b77cc"
|
resolved "https://registry.yarnpkg.com/@lexical/html/-/html-0.11.3.tgz#c02b38f512eb808726922c8215dd2374df6b77cc"
|
||||||
integrity sha512-+8AYnxxml9PneZLkGfdTenqDjE2yD1ZfCmQLrD/L1TEn22OjZh4uvKVHb13wEhgUZTuLKF0PNdnuecko9ON/aQ==
|
integrity sha512-+8AYnxxml9PneZLkGfdTenqDjE2yD1ZfCmQLrD/L1TEn22OjZh4uvKVHb13wEhgUZTuLKF0PNdnuecko9ON/aQ==
|
||||||
|
@ -1750,7 +1750,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@lexical/utils" "0.11.3"
|
"@lexical/utils" "0.11.3"
|
||||||
|
|
||||||
"@lexical/list@0.11.3", "@lexical/list@^0.11.3":
|
"@lexical/list@0.11.3":
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/@lexical/list/-/list-0.11.3.tgz#d158ac4b4b42d772b30a1cd2a42ca0462a5a154e"
|
resolved "https://registry.yarnpkg.com/@lexical/list/-/list-0.11.3.tgz#d158ac4b4b42d772b30a1cd2a42ca0462a5a154e"
|
||||||
integrity sha512-Cs9071wDfqi4j1VgodceiR1jTHj13eCoEJDhr3e/FW0x5we7vfbTMtWlOWbveIoryAh+rQNgiD5e8SrAm6Zs3g==
|
integrity sha512-Cs9071wDfqi4j1VgodceiR1jTHj13eCoEJDhr3e/FW0x5we7vfbTMtWlOWbveIoryAh+rQNgiD5e8SrAm6Zs3g==
|
||||||
|
@ -1815,7 +1815,7 @@
|
||||||
"@lexical/yjs" "0.11.3"
|
"@lexical/yjs" "0.11.3"
|
||||||
react-error-boundary "^3.1.4"
|
react-error-boundary "^3.1.4"
|
||||||
|
|
||||||
"@lexical/rich-text@0.11.3", "@lexical/rich-text@^0.11.3":
|
"@lexical/rich-text@0.11.3":
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/@lexical/rich-text/-/rich-text-0.11.3.tgz#9501789bfe9671c220da7e95bf8589fa92cecf8c"
|
resolved "https://registry.yarnpkg.com/@lexical/rich-text/-/rich-text-0.11.3.tgz#9501789bfe9671c220da7e95bf8589fa92cecf8c"
|
||||||
integrity sha512-fBFs6wMS7GFLbk+mzIWtwpP+EmnTZZ5bHpveuQ5wXONBuUuLcsYF5KO7UhLxXNLmiViV6lxatZPavEzgZdW7oQ==
|
integrity sha512-fBFs6wMS7GFLbk+mzIWtwpP+EmnTZZ5bHpveuQ5wXONBuUuLcsYF5KO7UhLxXNLmiViV6lxatZPavEzgZdW7oQ==
|
||||||
|
@ -1825,7 +1825,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@lexical/selection/-/selection-0.11.3.tgz#f7250fae305a84c6e264a413f5feab056aeabfa3"
|
resolved "https://registry.yarnpkg.com/@lexical/selection/-/selection-0.11.3.tgz#f7250fae305a84c6e264a413f5feab056aeabfa3"
|
||||||
integrity sha512-15lQpcKT/vd7XZ5pnF1nb+kpKb72e9Yi1dVqieSxTeXkzt1cAZFKP3NB4RlhOKCv1N+glSBnjSxRwgsFfbD+NQ==
|
integrity sha512-15lQpcKT/vd7XZ5pnF1nb+kpKb72e9Yi1dVqieSxTeXkzt1cAZFKP3NB4RlhOKCv1N+glSBnjSxRwgsFfbD+NQ==
|
||||||
|
|
||||||
"@lexical/table@0.11.3", "@lexical/table@^0.11.3":
|
"@lexical/table@0.11.3":
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/@lexical/table/-/table-0.11.3.tgz#9b1980d828d7a588aaffa4cb8c6bb61401729034"
|
resolved "https://registry.yarnpkg.com/@lexical/table/-/table-0.11.3.tgz#9b1980d828d7a588aaffa4cb8c6bb61401729034"
|
||||||
integrity sha512-EyRnN39CSPsMceADBR7Kf+sBHNpNQlPEkn/52epeDSnakR6s80woyrA3kIzKo6mLB4afvoqdYc7RfR96M9JLIA==
|
integrity sha512-EyRnN39CSPsMceADBR7Kf+sBHNpNQlPEkn/52epeDSnakR6s80woyrA3kIzKo6mLB4afvoqdYc7RfR96M9JLIA==
|
||||||
|
|
Loading…
Reference in a new issue