mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-02 10:18:42 +00:00
Resizable image support
This commit is contained in:
@@ -112,6 +112,7 @@
|
|||||||
"react-window": "1.8.11",
|
"react-window": "1.8.11",
|
||||||
"recharts": "^3.1.2",
|
"recharts": "^3.1.2",
|
||||||
"styled-components": "^6.1.14",
|
"styled-components": "^6.1.14",
|
||||||
|
"tiptap-extension-resizable-image": "^2.1.0",
|
||||||
"undici": "^6.24.0",
|
"undici": "^6.24.0",
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import '@mantine/tiptap/styles.css';
|
|||||||
import { useHotkeys } from '@mantine/hooks';
|
import { useHotkeys } from '@mantine/hooks';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Image from '@tiptap/extension-image';
|
|
||||||
import { useEditor } from '@tiptap/react';
|
import { useEditor } from '@tiptap/react';
|
||||||
import StarterKit from '@tiptap/starter-kit';
|
import StarterKit from '@tiptap/starter-kit';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
import { ResizableImage } from 'tiptap-extension-resizable-image';
|
||||||
|
import 'tiptap-extension-resizable-image/styles.css';
|
||||||
|
|
||||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||||
import type { ModelType } from '@lib/enums/ModelType';
|
import type { ModelType } from '@lib/enums/ModelType';
|
||||||
@@ -143,51 +144,16 @@ export default function NotesEditor({
|
|||||||
StarterKit.configure({
|
StarterKit.configure({
|
||||||
link: { openOnClick: false }
|
link: { openOnClick: false }
|
||||||
}),
|
}),
|
||||||
Image
|
ResizableImage.configure({
|
||||||
|
// Paste and drop are handled by the extension's built-in plugin
|
||||||
|
onUpload: async (file: File) => {
|
||||||
|
const src = await uploadFileRef.current(file);
|
||||||
|
return { src, 'data-keep-ratio': true };
|
||||||
|
}
|
||||||
|
})
|
||||||
],
|
],
|
||||||
content: '',
|
content: '',
|
||||||
onUpdate: () => setIsDirty(true),
|
onUpdate: () => setIsDirty(true)
|
||||||
editorProps: {
|
|
||||||
handleDrop: (view, event) => {
|
|
||||||
const files = event.dataTransfer?.files;
|
|
||||||
if (!files?.length) return false;
|
|
||||||
const imageFiles = Array.from(files).filter((f) =>
|
|
||||||
f.type.startsWith('image/')
|
|
||||||
);
|
|
||||||
if (!imageFiles.length) return false;
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
const coords = view.posAtCoords({
|
|
||||||
left: event.clientX,
|
|
||||||
top: event.clientY
|
|
||||||
});
|
|
||||||
imageFiles.forEach((file) => {
|
|
||||||
uploadFileRef.current(file).then((url) => {
|
|
||||||
if (!url || !coords) return;
|
|
||||||
const node = view.state.schema.nodes.image?.create({ src: url });
|
|
||||||
if (node) view.dispatch(view.state.tr.insert(coords.pos, node));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
handlePaste: (view, event) => {
|
|
||||||
const files = event.clipboardData?.files;
|
|
||||||
if (!files?.length) return false;
|
|
||||||
const imageFiles = Array.from(files).filter((f) =>
|
|
||||||
f.type.startsWith('image/')
|
|
||||||
);
|
|
||||||
if (!imageFiles.length) return false;
|
|
||||||
|
|
||||||
imageFiles.forEach((file) => {
|
|
||||||
uploadFileRef.current(file).then((url) => {
|
|
||||||
if (!url) return;
|
|
||||||
const node = view.state.schema.nodes.image?.create({ src: url });
|
|
||||||
if (node) view.dispatch(view.state.tr.replaceSelectionWith(node));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch the available notes for the given model type and ID
|
// Fetch the available notes for the given model type and ID
|
||||||
@@ -360,8 +326,12 @@ export default function NotesEditor({
|
|||||||
async (file: File | null) => {
|
async (file: File | null) => {
|
||||||
if (!file || !editor) return;
|
if (!file || !editor) return;
|
||||||
try {
|
try {
|
||||||
const url = await uploadFile(file);
|
const src = await uploadFile(file);
|
||||||
editor.chain().focus().setImage({ src: url }).run();
|
editor
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.setResizableImage({ src, 'data-keep-ratio': true })
|
||||||
|
.run();
|
||||||
} catch {
|
} catch {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: t`Error`,
|
title: t`Error`,
|
||||||
@@ -514,7 +484,7 @@ export default function NotesEditor({
|
|||||||
color='green'
|
color='green'
|
||||||
leftSection={<IconCirclePlus />}
|
leftSection={<IconCirclePlus />}
|
||||||
onClick={createNote.open}
|
onClick={createNote.open}
|
||||||
disabled={!canEdit || isDirty}
|
// disabled={!canEdit || isDirty}
|
||||||
>
|
>
|
||||||
{t`Add Note`}
|
{t`Add Note`}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5482,6 +5482,11 @@ tinyglobby@^0.2.13:
|
|||||||
fdir "^6.5.0"
|
fdir "^6.5.0"
|
||||||
picomatch "^4.0.4"
|
picomatch "^4.0.4"
|
||||||
|
|
||||||
|
tiptap-extension-resizable-image@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiptap-extension-resizable-image/-/tiptap-extension-resizable-image-2.1.0.tgz#26573bcddfeeb06067be6f7c5ace2573b14661b4"
|
||||||
|
integrity sha512-uvdY/mTHMZ3EF3Z+b61y1htOUg8oF4TiepNFF1pYRg/KITYoYSteF5/L2/qtlZF9SMrzzwnrtRFQFzn/qBj7qQ==
|
||||||
|
|
||||||
to-regex-range@^5.0.1:
|
to-regex-range@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||||
|
|||||||
Reference in New Issue
Block a user