Merge remote-tracking branch 'soapbox/main' into lexical
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
179
src/features/compose/editor/nodes/image-node.tsx
Normal file
179
src/features/compose/editor/nodes/image-node.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
Reference in New Issue
Block a user