Delete image button

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2023-07-21 23:04:27 +02:00
parent 856dd277f4
commit eb1feaa182
4 changed files with 35 additions and 15 deletions

View File

@ -152,7 +152,7 @@ const ComposeEditor = React.forwardRef<string, IComposeEditor>(({
contentEditable={ contentEditable={
<div className='editor' ref={onRef} onFocus={onFocus} onPaste={handlePaste}> <div className='editor' ref={onRef} onFocus={onFocus} onPaste={handlePaste}>
<ContentEditable <ContentEditable
className={clsx('mr-4 outline-none transition-[min-height] motion-reduce:transition-none', { className={clsx('mr-4 pb-8 outline-none transition-[min-height] motion-reduce:transition-none', {
'min-h-[40px]': condensed, 'min-h-[40px]': condensed,
'min-h-[100px]': !condensed, 'min-h-[100px]': !condensed,
})} })}

View File

@ -10,6 +10,7 @@
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'; import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
import { mergeRegister } from '@lexical/utils'; import { mergeRegister } from '@lexical/utils';
import clsx from 'clsx';
import { import {
$getNodeByKey, $getNodeByKey,
$getSelection, $getSelection,
@ -27,6 +28,8 @@ import {
import * as React from 'react'; import * as React from 'react';
import { Suspense, useCallback, useEffect, useRef, useState } from 'react'; import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
import { IconButton } from 'soapbox/components/ui';
import { $isImageNode } from './image-node'; import { $isImageNode } from './image-node';
import type { import type {
@ -94,15 +97,24 @@ export default function ImageComponent({
>(null); >(null);
const activeEditorRef = useRef<LexicalEditor | null>(null); const activeEditorRef = useRef<LexicalEditor | null>(null);
const deleteNode = useCallback(
() => {
editor.update(() => {
const node = $getNodeByKey(nodeKey);
if ($isImageNode(node)) {
node.remove();
}
});
},
[nodeKey],
);
const onDelete = useCallback( const onDelete = useCallback(
(payload: KeyboardEvent) => { (payload: KeyboardEvent) => {
if (isSelected && $isNodeSelection($getSelection())) { if (isSelected && $isNodeSelection($getSelection())) {
const event: KeyboardEvent = payload; const event: KeyboardEvent = payload;
event.preventDefault(); event.preventDefault();
const node = $getNodeByKey(nodeKey); deleteNode();
if ($isImageNode(node)) {
node.remove();
}
} }
return false; return false;
}, },
@ -235,12 +247,20 @@ export default function ImageComponent({
return ( return (
<Suspense fallback={null}> <Suspense fallback={null}>
<> <>
<div draggable={draggable}> <div className='relative' draggable={draggable}>
<IconButton
onClick={deleteNode}
src={require('@tabler/icons/x.svg')}
theme='dark'
className='absolute right-2 top-2 z-10 hover:scale-105 hover:bg-gray-900'
iconClassName='h-5 w-5'
/>
<LazyImage <LazyImage
className={ className={
isFocused clsx('cursor-default', {
? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` 'select-none': isFocused,
: null 'cursor-grab active:cursor-grabbing': isFocused && $isNodeSelection(selection),
})
} }
src={src} src={src}
altText={altText} altText={altText}

View File

@ -29,14 +29,14 @@ interface ImagePayload {
src: string src: string
} }
function convertImageElement(domNode: Node): null | DOMConversionOutput { const convertImageElement = (domNode: Node): null | DOMConversionOutput => {
if (domNode instanceof HTMLImageElement) { if (domNode instanceof HTMLImageElement) {
const { alt: altText, src } = domNode; const { alt: altText, src } = domNode;
const node = $createImageNode({ altText, src }); const node = $createImageNode({ altText, src });
return { node }; return { node };
} }
return null; return null;
} };
type SerializedImageNode = Spread< type SerializedImageNode = Spread<
{ {
@ -146,11 +146,11 @@ class ImageNode extends DecoratorNode<JSX.Element> {
} }
function $createImageNode({ const $createImageNode = ({
altText = '', altText = '',
src, src,
key, key,
}: ImagePayload): ImageNode { }: ImagePayload): ImageNode => {
return $applyNodeReplacement( return $applyNodeReplacement(
new ImageNode( new ImageNode(
src, src,
@ -158,7 +158,7 @@ function $createImageNode({
key, key,
), ),
); );
} };
const $isImageNode = ( const $isImageNode = (
node: LexicalNode | null | undefined, node: LexicalNode | null | undefined,

View File

@ -183,7 +183,7 @@ const BlockTypeFloatingToolbar = ({
const selection = $getSelection(); const selection = $getSelection();
if ($isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection)) { if ($isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection)) {
const selectionNode = selection.anchor.getNode(); const selectionNode = selection.anchor.getNode();
selectionNode.replace($createImageNode({ src })); selectionNode.replace($createImageNode({ altText: '', src }));
} }
}); });
}; };