diff --git a/packages/client/src/components/icons/IconStructure.tsx b/packages/client/src/components/icons/IconStructure.tsx new file mode 100644 index 00000000..ea6bc7cb --- /dev/null +++ b/packages/client/src/components/icons/IconStructure.tsx @@ -0,0 +1,14 @@ +import { Icon } from '@douyinfe/semi-ui'; + +export const IconStructure: React.FC<{ style?: React.CSSProperties }> = ({ style = {} }) => { + return ( + + + + } + /> + ); +}; diff --git a/packages/client/src/components/icons/index.tsx b/packages/client/src/components/icons/index.tsx index 2596cf10..380417df 100644 --- a/packages/client/src/components/icons/index.tsx +++ b/packages/client/src/components/icons/index.tsx @@ -51,3 +51,4 @@ export * from './IconGlobe'; export * from './IconCountdown'; export * from './IconDrawBoard'; export * from './IconCallout'; +export * from './IconStructure'; diff --git a/packages/client/src/tiptap/extensions/mind.ts b/packages/client/src/tiptap/extensions/mind.ts index ed85aeb5..a359c64e 100644 --- a/packages/client/src/tiptap/extensions/mind.ts +++ b/packages/client/src/tiptap/extensions/mind.ts @@ -6,7 +6,7 @@ import { getDatasetAttribute } from '../utils/dataset'; const DEFAULT_MIND_DATA = { root: { data: { text: '中心节点' }, children: [] }, template: 'default', - theme: 'fresh-blue', + theme: 'classic', version: '1.4.43', }; @@ -39,6 +39,18 @@ export const Mind = Node.create({ default: DEFAULT_MIND_DATA, parseHTML: getDatasetAttribute('data', true), }, + template: { + default: 'default', + parseHTML: getDatasetAttribute('template'), + }, + theme: { + default: 'classic', + parseHTML: getDatasetAttribute('theme'), + }, + zoom: { + default: 100, + parseHTML: getDatasetAttribute('zoom'), + }, }; }, diff --git a/packages/client/src/tiptap/menus/mind/bubble.module.scss b/packages/client/src/tiptap/menus/mind/bubble.module.scss new file mode 100644 index 00000000..4f4d1b07 --- /dev/null +++ b/packages/client/src/tiptap/menus/mind/bubble.module.scss @@ -0,0 +1,42 @@ +.sectionWrap { + margin-top: 16px; + + > div { + display: flex; + flex-wrap: wrap; + margin-top: 8px; + width: 168px; + + ul { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-wrap: wrap; + + li { + width: 80px; + height: 30px; + text-align: center; + line-height: 30px; + padding: 0 5px; + font-size: 12px; + cursor: pointer; + text-decoration: none; + border: 1px solid rgb(28 31 35 / 8%); + + &.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/menus/mind/bubble.tsx b/packages/client/src/tiptap/menus/mind/bubble.tsx index 241c432d..f1d6900e 100644 --- a/packages/client/src/tiptap/menus/mind/bubble.tsx +++ b/packages/client/src/tiptap/menus/mind/bubble.tsx @@ -1,18 +1,63 @@ import { useCallback } from 'react'; import { useRouter } from 'next/router'; -import { Space, Button, List, Popover, Typography } from '@douyinfe/semi-ui'; +import cls from 'classnames'; +import { Space, Button, List, Popover, Typography, RadioGroup, Radio } from '@douyinfe/semi-ui'; import { IconEdit, IconDelete } from '@douyinfe/semi-icons'; import { Tooltip } from 'components/tooltip'; -import { DataRender } from 'components/data-render'; -import { IconDocument } from 'components/icons'; -import { useWikiTocs } from 'data/wiki'; +import { IconStructure, IconDrawBoard, IconZoomIn, IconZoomOut } from 'components/icons'; import { BubbleMenu } from '../../views/bubble-menu'; import { Mind } from '../../extensions/mind'; import { Divider } from '../../divider'; +import { clamp } from '../../utils/clamp'; +import { TEMPLATES, THEMES, MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from './constant'; +import styles from './bubble.module.scss'; const { Text } = Typography; export const MindBubbleMenu = ({ editor }) => { + const { template, theme, zoom } = editor.getAttributes(Mind.name); + + const setZoom = useCallback( + (type: 'minus' | 'plus') => { + return () => { + editor + .chain() + .updateAttributes(Mind.name, { + zoom: clamp(type === 'minus' ? parseInt(zoom) - ZOOM_STEP : parseInt(zoom) + ZOOM_STEP, MIN_ZOOM, MAX_ZOOM), + }) + .focus() + .run(); + }; + }, + [editor, zoom] + ); + + const setTemplate = useCallback( + (template) => { + editor + .chain() + .updateAttributes(Mind.name, { + template, + }) + .focus() + .run(); + }, + [editor] + ); + + const setTheme = useCallback( + (theme) => { + editor + .chain() + .updateAttributes(Mind.name, { + theme, + }) + .focus() + .run(); + }, + [editor] + ); + const deleteNode = useCallback(() => editor.chain().deleteSelection().run(), [editor]); return ( @@ -24,6 +69,82 @@ export const MindBubbleMenu = ({ editor }) => { tippyOptions={{ maxWidth: 'calc(100vw - 100px)' }} > + +