From 65b7a1451a7559a45c8a1cd32df48eba2b8ebe04 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sat, 21 May 2022 22:09:25 +0800 Subject: [PATCH] feat: add toolbar for mind --- .../src/tiptap/editor/menus/mind/modal.tsx | 194 +---------- .../editor/menus/mind/style.module.scss | 58 +-- .../editor/menus/mind/toolbar/image.tsx | 56 +++ .../menus/mind/toolbar/index.module.scss | 46 +++ .../editor/menus/mind/toolbar/index.tsx | 329 ++++++++++++++++++ .../tiptap/editor/menus/mind/toolbar/link.tsx | 50 +++ 6 files changed, 500 insertions(+), 233 deletions(-) create mode 100644 packages/client/src/tiptap/editor/menus/mind/toolbar/image.tsx create mode 100644 packages/client/src/tiptap/editor/menus/mind/toolbar/index.module.scss create mode 100644 packages/client/src/tiptap/editor/menus/mind/toolbar/index.tsx create mode 100644 packages/client/src/tiptap/editor/menus/mind/toolbar/link.tsx diff --git a/packages/client/src/tiptap/editor/menus/mind/modal.tsx b/packages/client/src/tiptap/editor/menus/mind/modal.tsx index 4573e8b..fd85887 100644 --- a/packages/client/src/tiptap/editor/menus/mind/modal.tsx +++ b/packages/client/src/tiptap/editor/menus/mind/modal.tsx @@ -1,86 +1,35 @@ -import { IconHelpCircle, IconMinus, IconPlus } from '@douyinfe/semi-icons'; -import { Button, Descriptions, Modal, Popover, Space, Spin, Typography } from '@douyinfe/semi-ui'; -import cls from 'classnames'; -import { IconDrawBoard, IconMindCenter, IconStructure } from 'components/icons'; -import { Tooltip } from 'components/tooltip'; +import { Modal, Spin, Typography } from '@douyinfe/semi-ui'; import { useToggle } from 'hooks/use-toggle'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { load, renderMind } from 'thirtypart/kityminder'; import { Editor } from 'tiptap/editor'; -import { clamp } from 'tiptap/prose-utils'; import { cancelSubject, OPEN_MIND_SETTING_MODAL, subject } from '../_event'; -import { MAX_ZOOM, MIN_ZOOM, TEMPLATES, THEMES, ZOOM_STEP } from './constant'; import styles from './style.module.scss'; +import { Toolbar } from './toolbar'; type IProps = { editor: Editor }; const { Text } = Typography; -const HELP_MESSAGE = [ - { key: '新增同级节点', value: 'Enter 键' }, - { key: '新增子节点', value: 'Tab 键' }, - { key: '编辑节点文字', value: '双击节点' }, - { key: '编辑节点菜单', value: '在节点右键' }, -]; - -const HELP_MESSAGE_STYLE = { - width: '200px', -}; - export const MindSettingModal: React.FC = ({ editor }) => { - const $mind = useRef(null); - const [initialData, setInitialData] = useState({ template: '', theme: '' }); - const [template, setTemplateState] = useState(''); - const [theme, setThemeState] = useState(''); + const [mind, setMind] = useState(null); + const [initialData, setInitialData] = useState({}); const [visible, toggleVisible] = useToggle(false); const [loading, toggleLoading] = useToggle(true); const [error, setError] = useState(null); - const setZoom = useCallback((type: 'minus' | 'plus') => { - return () => { - const mind = $mind.current; - if (!mind) return; - const currentZoom = mind.getZoomValue(); - const nextZoom = clamp(type === 'minus' ? currentZoom - ZOOM_STEP : currentZoom + ZOOM_STEP, MIN_ZOOM, MAX_ZOOM); - mind.zoom(nextZoom); - }; - }, []); - - const setCenter = useCallback(() => { - const mind = $mind.current; - if (!mind) return; - mind.execCommand('camera'); - }, []); - - const setTemplate = useCallback((template) => { - const mind = $mind.current; - if (!mind) return; - mind.execCommand('template', template); - setTemplateState(template); - }, []); - - const setTheme = useCallback((theme) => { - const mind = $mind.current; - if (!mind) return; - mind.execCommand('theme', theme); - setThemeState(theme); - }, []); - - const setMind = useCallback( + const renderMindEditor = useCallback( (div) => { if (!div) return; - if ($mind.current) { - $mind.current.destroy(); - $mind.current = null; - } - - $mind.current = renderMind({ + const mindInstance = renderMind({ container: div, data: initialData, isEditable: true, }); + + setMind(mindInstance); }, [initialData] ); @@ -89,32 +38,23 @@ export const MindSettingModal: React.FC = ({ editor }) => { load() .catch(setError) .finally(() => toggleLoading(false)); - - return () => { - if ($mind.current) { - $mind.current.destroy(); - $mind.current = null; - } - }; }, [toggleLoading]); const save = useCallback(() => { - if (!$mind.current) { + if (!mind) { toggleVisible(false); return; } - const data = $mind.current.exportJson(); + const data = mind.exportJson(); editor.chain().focus().setMind({ data }).run(); toggleVisible(false); - }, [editor, toggleVisible]); + }, [editor, toggleVisible, mind]); useEffect(() => { const handler = (data) => { toggleVisible(true); if (data) { setInitialData(data.data); - setTemplateState(data.data.template); - setThemeState(data.data.theme); } }; @@ -151,116 +91,10 @@ export const MindSettingModal: React.FC = ({ editor }) => { )} {error && {(error && error.message) || '未知错误'}} -
+
- - - 布局 -
-
    - {TEMPLATES.map((item) => { - return ( -
  • setTemplate(item.value)} - > - {item.label} -
  • - ); - })} -
-
- - } - > -
diff --git a/packages/client/src/tiptap/editor/menus/mind/style.module.scss b/packages/client/src/tiptap/editor/menus/mind/style.module.scss index 41e63e5..b3ebee4 100644 --- a/packages/client/src/tiptap/editor/menus/mind/style.module.scss +++ b/packages/client/src/tiptap/editor/menus/mind/style.module.scss @@ -1,61 +1,13 @@ .toolbarWrap { position: absolute; - right: 24px; - bottom: 24px; + top: 0; + left: 0; z-index: 100; - padding: 2px 4px; + width: 100%; + padding: 6px 24px; overflow-x: auto; color: #fff; background-color: var(--semi-color-nav-bg); - border: 1px solid var(--semi-color-border); - border-radius: 3px; + border-bottom: 1px solid var(--semi-color-border); align-items: center; - box-shadow: var(--box-shadow); -} - -.sectionWrap { - margin-top: 16px; - - > div { - display: flex; - width: 168px; - margin-top: 8px; - flex-wrap: wrap; - - ul { - display: flex; - padding: 0; - margin: 0; - list-style: none; - flex-wrap: wrap; - - li { - width: 80px; - height: 30px; - padding: 0 5px; - font-size: 12px; - line-height: 30px; - text-align: center; - text-decoration: none; - cursor: pointer; - border: 1px solid rgb(28 31 35 / 8%); - - * { - font-size: inherit; - } - - &.active { - border: 1px solid rgb(0 101 255); - } - - &:nth-of-type(2n) { - margin-left: 8px; - } - - &:nth-of-type(n + 3) { - margin-top: 8px; - } - } - } - } } diff --git a/packages/client/src/tiptap/editor/menus/mind/toolbar/image.tsx b/packages/client/src/tiptap/editor/menus/mind/toolbar/image.tsx new file mode 100644 index 0000000..9b738b4 --- /dev/null +++ b/packages/client/src/tiptap/editor/menus/mind/toolbar/image.tsx @@ -0,0 +1,56 @@ +import { IconFile } from '@douyinfe/semi-icons'; +import { Button, Dropdown, Form, Tooltip } from '@douyinfe/semi-ui'; +import { FormApi } from '@douyinfe/semi-ui/lib/es/form'; +import { Upload } from 'components/upload'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +export const Image = ({ disabled, image, setImage }) => { + const $form = useRef(); + const [initialState, setInitialState] = useState({ image }); + + const setImageUrl = useCallback((url) => { + $form.current.setValue('image', url); + }, []); + + const handleOk = useCallback(() => { + $form.current.validate().then((values) => { + setImage(values.image); + }); + }, [setImage]); + + useEffect(() => { + setInitialState({ image }); + }, [image]); + + return ( + +
($form.current = formApi)} + labelPosition="left" + onSubmit={handleOk} + layout="horizontal" + > + + + + + + } + > + + + + + + } + > + + +