From 94425befcf8c57248b0110bf299fc83fac9198b8 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sun, 13 Mar 2022 12:13:42 +0800 Subject: [PATCH] feat: width, height support for image --- .../src/components/resizeable/resizeable.tsx | 7 +++ .../components/tiptap/extensions/image.tsx | 6 +- .../components/tiptap/markdown/serializer.tsx | 5 ++ .../src/components/tiptap/menus/image.tsx | 60 ++++++++++++++++++- .../components/tiptap/menus/media-insert.tsx | 7 ++- .../src/components/tiptap/utils/image.ts | 15 +++++ .../client/src/components/upload/index.tsx | 4 +- packages/config/.gitignore | 39 ++++++++++++ 8 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 packages/client/src/components/tiptap/utils/image.ts create mode 100644 packages/config/.gitignore diff --git a/packages/client/src/components/resizeable/resizeable.tsx b/packages/client/src/components/resizeable/resizeable.tsx index d67a6012..39d99f50 100644 --- a/packages/client/src/components/resizeable/resizeable.tsx +++ b/packages/client/src/components/resizeable/resizeable.tsx @@ -54,6 +54,13 @@ export const Resizeable: React.FC = ({ width, height, onChange, children }); }, []); + useEffect(() => { + Object.assign($container.current.style, { + width: `${width}px`, + height: `${height}px`, + }); + }, [width, height]); + return (
{ const isEditable = editor.isEditable; @@ -12,7 +10,7 @@ const Render = ({ editor, node, updateAttributes }) => { updateAttributes({ height: size.height, width: size.width }); }; - const content = src && {title}; + const content = src && {alt}; return ( diff --git a/packages/client/src/components/tiptap/markdown/serializer.tsx b/packages/client/src/components/tiptap/markdown/serializer.tsx index 163deda0..00110b08 100644 --- a/packages/client/src/components/tiptap/markdown/serializer.tsx +++ b/packages/client/src/components/tiptap/markdown/serializer.tsx @@ -132,6 +132,11 @@ const defaultSerializerConfig = { state.write(`documentChildren$`); state.closeBlock(node); }, + [Mind.name]: (state, node) => { + state.ensureNewLine(); + state.write(`mind$`); + state.closeBlock(node); + }, // [DescriptionList.name]: renderHTMLNode("dl", true), // [DescriptionItem.name]: (state, node, parent, index) => { // if (index === 1) state.ensureNewLine(); diff --git a/packages/client/src/components/tiptap/menus/image.tsx b/packages/client/src/components/tiptap/menus/image.tsx index 96c12a2a..38456390 100644 --- a/packages/client/src/components/tiptap/menus/image.tsx +++ b/packages/client/src/components/tiptap/menus/image.tsx @@ -1,4 +1,5 @@ -import { Space, Button, Tooltip } from '@douyinfe/semi-ui'; +import React, { useEffect, useState } from 'react'; +import { Space, Button, Tooltip, InputNumber, Typography } from '@douyinfe/semi-ui'; import { IconAlignLeft, IconAlignCenter, @@ -10,8 +11,21 @@ import { Upload } from 'components/upload'; import { BubbleMenu } from '../components/bubble-menu'; import { Divider } from '../components/divider'; import { Image } from '../extensions/image'; +import { getImageOriginSize } from '../utils/image'; + +const { Text } = Typography; export const ImageBubbleMenu = ({ editor }) => { + const attrs = editor.getAttributes(Image.name); + const { width: currentWidth, height: currentHeight } = attrs; + const [width, setWidth] = useState(currentWidth); + const [height, setHeight] = useState(currentHeight); + + useEffect(() => { + setWidth(parseInt(currentWidth)); + setHeight(parseInt(currentHeight)); + }, [currentWidth, currentHeight]); + return ( { /> + + { + const value = (e.target as HTMLInputElement).value; + editor + .chain() + .updateAttributes(Image.name, { + width: value, + }) + .setNodeSelection(editor.state.selection.from) + .focus() + .run(); + }} + /> + + { + const value = (e.target as HTMLInputElement).value; + editor + .chain() + .updateAttributes(Image.name, { + height: value, + }) + .setNodeSelection(editor.state.selection.from) + .focus() + .run(); + }} + /> + { + onOK={async (url, fileName) => { + const { width, height } = await getImageOriginSize(url); editor .chain() .updateAttributes(Image.name, { src: url, - alt: 'filename', + alt: fileName, + width, + height, }) .setNodeSelection(editor.state.selection.from) .focus() diff --git a/packages/client/src/components/tiptap/menus/media-insert.tsx b/packages/client/src/components/tiptap/menus/media-insert.tsx index 8b7fd505..595148a6 100644 --- a/packages/client/src/components/tiptap/menus/media-insert.tsx +++ b/packages/client/src/components/tiptap/menus/media-insert.tsx @@ -16,6 +16,7 @@ import { } from 'components/icons'; import { GridSelect } from 'components/grid-select'; import { isTitleActive } from '../utils/active'; +import { getImageOriginSize } from '../utils/image'; export const MediaInsertMenu: React.FC<{ editor: any }> = ({ editor }) => { if (!editor) { @@ -65,7 +66,11 @@ export const MediaInsertMenu: React.FC<{ editor: any }> = ({ editor }) => { editor.chain().focus().setImage({ src: url }).run()} + onOK={async (url, fileName) => { + const { width, height } = await getImageOriginSize(url); + console.log('upload', width, height); + editor.chain().focus().setImage({ src: url, alt: fileName, width, height }).run(); + }} > {() => '图片'} diff --git a/packages/client/src/components/tiptap/utils/image.ts b/packages/client/src/components/tiptap/utils/image.ts new file mode 100644 index 00000000..3f4cf74e --- /dev/null +++ b/packages/client/src/components/tiptap/utils/image.ts @@ -0,0 +1,15 @@ +export function getImageOriginSize( + src: string +): Promise<{ width: number | string; height: number | string }> { + return new Promise((resolve) => { + const image = document.createElement('img'); + image.onload = function () { + console.log(image.width, image.height); + resolve({ width: image.width, height: image.height }); + }; + image.onerror = function () { + resolve({ width: 'auto', height: 'auto' }); + }; + image.src = src; + }); +} diff --git a/packages/client/src/components/upload/index.tsx b/packages/client/src/components/upload/index.tsx index 8e7d25f3..ce07c194 100644 --- a/packages/client/src/components/upload/index.tsx +++ b/packages/client/src/components/upload/index.tsx @@ -5,7 +5,7 @@ import { useAsyncLoading } from 'hooks/useAsyncLoading'; import { uploadFile } from 'services/file'; interface IProps { - onOK: (arg: string, fileName: string) => void; + onOK: (arg: string, fileName: string, fileSize: number) => void; style?: React.CSSProperties; accept?: string; children?: (loading: boolean) => React.ReactNode; @@ -17,7 +17,7 @@ export const Upload: React.FC = ({ onOK, accept, style = {}, children }) const beforeUpload = ({ file }) => { uploadFileWithLoading(file.fileInstance).then((res: string) => { Toast.success('上传成功'); - onOK && onOK(res, file.name); + onOK && onOK(res, file.name, file.size); }); return false; }; diff --git a/packages/config/.gitignore b/packages/config/.gitignore new file mode 100644 index 00000000..f0e99ae1 --- /dev/null +++ b/packages/config/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo + +yaml/dev.yaml