mirror of https://github.com/fantasticit/think.git
feat: add zoom handler
parent
53c37ae052
commit
e7259db747
|
@ -30,4 +30,16 @@
|
|||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.handlerWrap {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 2;
|
||||
padding: 2px 4px;
|
||||
background-color: var(--semi-color-bg-2);
|
||||
border: 1px solid var(--node-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,17 @@ import React from 'react';
|
|||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import VisibilitySensor from 'react-visibility-sensor';
|
||||
|
||||
import { Space, Spin, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Space, Spin, Typography } from '@douyinfe/semi-ui';
|
||||
|
||||
import { NodeViewWrapper } from '@tiptap/react';
|
||||
import { Excalidraw } from 'tiptap/core/extensions/excalidraw';
|
||||
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from 'tiptap/core/menus/mind/constant';
|
||||
import { clamp, getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { IconMind } from 'components/icons';
|
||||
import { IconMind, IconZoomIn, IconZoomOut } from 'components/icons';
|
||||
import { Resizeable } from 'components/resizeable';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import deepEqual from 'deep-equal';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
|
||||
|
@ -30,6 +32,7 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const [loading, toggleLoading] = useToggle(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [visible, toggleVisible] = useToggle(false);
|
||||
const [zoom, setZoomState] = useState(100);
|
||||
|
||||
const onResize = useCallback(
|
||||
(size) => {
|
||||
|
@ -47,6 +50,14 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => {
|
|||
[toggleVisible]
|
||||
);
|
||||
|
||||
const setZoom = useCallback((type: 'minus' | 'plus') => {
|
||||
return () => {
|
||||
setZoomState((currentZoom) =>
|
||||
clamp(type === 'minus' ? currentZoom - ZOOM_STEP : currentZoom + ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let isUnmount = false;
|
||||
|
||||
|
@ -114,6 +125,8 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => {
|
|||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
transform: `scale(${zoom / 100})`,
|
||||
transition: `all ease-in-out .3s`,
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: Svg?.outerHTML ?? '' }}
|
||||
/>
|
||||
|
@ -127,6 +140,27 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => {
|
|||
绘图
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div className={styles.handlerWrap}>
|
||||
<Tooltip content="缩小">
|
||||
<Button
|
||||
size="small"
|
||||
theme="borderless"
|
||||
type="tertiary"
|
||||
icon={<IconZoomOut />}
|
||||
onClick={setZoom('minus')}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip content="放大">
|
||||
<Button
|
||||
size="small"
|
||||
theme="borderless"
|
||||
type="tertiary"
|
||||
icon={<IconZoomIn />}
|
||||
onClick={setZoom('plus')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</Resizeable>
|
||||
</VisibilitySensor>
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
.wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--node-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.handlerWrap {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 2;
|
||||
padding: 2px 4px;
|
||||
background-color: var(--semi-color-bg-2);
|
||||
border: 1px solid var(--node-border-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { LazyLoadImage } from 'react-lazy-load-image-component';
|
||||
|
||||
import { Spin, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Spin, Typography } from '@douyinfe/semi-ui';
|
||||
|
||||
import { NodeViewWrapper } from '@tiptap/react';
|
||||
import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from 'tiptap/core/menus/mind/constant';
|
||||
import {
|
||||
clamp,
|
||||
extractFileExtension,
|
||||
extractFilename,
|
||||
getEditorContainerDOMSize,
|
||||
getImageWidthHeight,
|
||||
} from 'tiptap/prose-utils';
|
||||
|
||||
import { IconZoomIn, IconZoomOut } from 'components/icons';
|
||||
import { Resizeable } from 'components/resizeable';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import { uploadFile } from 'services/file';
|
||||
|
||||
|
@ -25,6 +29,7 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => {
|
|||
const { width: maxWidth } = getEditorContainerDOMSize(editor);
|
||||
const $upload = useRef<HTMLInputElement>();
|
||||
const [loading, toggleLoading] = useToggle(false);
|
||||
const [zoom, setZoomState] = useState(100);
|
||||
|
||||
const onResize = useCallback(
|
||||
(size) => {
|
||||
|
@ -64,6 +69,14 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => {
|
|||
[updateAttributes, toggleLoading]
|
||||
);
|
||||
|
||||
const setZoom = useCallback((type: 'minus' | 'plus') => {
|
||||
return () => {
|
||||
setZoomState((currentZoom) =>
|
||||
clamp(type === 'minus' ? currentZoom - ZOOM_STEP : currentZoom + ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!src && !hasTrigger) {
|
||||
selectFile();
|
||||
|
@ -93,7 +106,44 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => {
|
|||
</Spin>
|
||||
</div>
|
||||
) : (
|
||||
<LazyLoadImage src={src} alt={alt} width={'100%'} height={'100%'} />
|
||||
<div className={styles.wrap}>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
maxHeight: '100%',
|
||||
padding: 24,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
transform: `scale(${zoom / 100})`,
|
||||
transition: `all ease-in-out .3s`,
|
||||
}}
|
||||
>
|
||||
<LazyLoadImage src={src} alt={alt} width={'100%'} height={'100%'} />
|
||||
</div>
|
||||
|
||||
<div className={styles.handlerWrap}>
|
||||
<Tooltip content="缩小">
|
||||
<Button
|
||||
size="small"
|
||||
theme="borderless"
|
||||
type="tertiary"
|
||||
icon={<IconZoomOut />}
|
||||
onClick={setZoom('minus')}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip content="放大">
|
||||
<Button
|
||||
size="small"
|
||||
theme="borderless"
|
||||
type="tertiary"
|
||||
icon={<IconZoomIn />}
|
||||
onClick={setZoom('plus')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Resizeable>
|
||||
</NodeViewWrapper>
|
||||
|
|
|
@ -10,7 +10,7 @@ export class UpdateWikiDto {
|
|||
|
||||
@IsString({ message: '知识库描述类型错误(正确类型为:String)' })
|
||||
@IsNotEmpty({ message: '知识库描述不能为空' })
|
||||
@MinLength(3, { message: '知识库描述至少3个字符' })
|
||||
@MinLength(1, { message: '知识库描述至少1个字符' })
|
||||
@IsOptional()
|
||||
description: string;
|
||||
|
||||
|
|
Loading…
Reference in New Issue