mirror of https://github.com/fantasticit/think.git
refactor: rename banner to callout
This commit is contained in:
parent
1786e21fc8
commit
20db8fcf57
|
@ -0,0 +1,14 @@
|
|||
import { Icon } from '@douyinfe/semi-ui';
|
||||
|
||||
export const IconCallout: React.FC<{ style?: React.CSSProperties }> = ({ style = {} }) => {
|
||||
return (
|
||||
<Icon
|
||||
style={style}
|
||||
svg={
|
||||
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
|
||||
<path d="M4 2a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4Zm16 2v6H4V4h16ZM3 16a1 1 0 0 1 1-1h16a1 1 0 1 1 0 2H4a1 1 0 0 1-1-1Zm1 4a1 1 0 1 0 0 2h8a1 1 0 1 0 0-2H4Z"></path>
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -50,3 +50,4 @@ export * from './IconSup';
|
|||
export * from './IconGlobe';
|
||||
export * from './IconCountdown';
|
||||
export * from './IconDrawBoard';
|
||||
export * from './IconCallout';
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { BannerWrapper } from '../wrappers/banner';
|
||||
import { CalloutWrapper } from '../wrappers/callout';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
banner: {
|
||||
setBanner: (attrs) => ReturnType;
|
||||
setCallout: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const Banner = Node.create({
|
||||
name: 'banner',
|
||||
export const Callout = Node.create({
|
||||
name: 'callout',
|
||||
content: 'paragraph+',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
|
@ -20,17 +20,6 @@ export const Banner = Node.create({
|
|||
|
||||
addAttributes() {
|
||||
return {
|
||||
type: {
|
||||
default: 'info',
|
||||
rendered: false,
|
||||
parseHTML: getDatasetAttribute('type'),
|
||||
renderHTML: (attributes) => {
|
||||
return {
|
||||
'data-type': attributes.type,
|
||||
'class': `banner banner-${attributes.type}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
emoji: {
|
||||
default: '✅',
|
||||
},
|
||||
|
@ -49,7 +38,7 @@ export const Banner = Node.create({
|
|||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {
|
||||
class: 'banner',
|
||||
class: 'callout',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -68,14 +57,14 @@ export const Banner = Node.create({
|
|||
|
||||
addCommands() {
|
||||
return {
|
||||
setBanner:
|
||||
(attributes) =>
|
||||
setCallout:
|
||||
() =>
|
||||
({ commands, editor }) => {
|
||||
const { type = null } = editor.getAttributes(this.name);
|
||||
if (type) {
|
||||
commands.lift(this.name);
|
||||
} else {
|
||||
return commands.toggleWrap(this.name, attributes);
|
||||
return commands.toggleWrap(this.name);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -94,6 +83,6 @@ export const Banner = Node.create({
|
|||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(BannerWrapper);
|
||||
return ReactNodeViewRenderer(CalloutWrapper);
|
||||
},
|
||||
});
|
|
@ -115,19 +115,19 @@ export const Paste = Extension.create({
|
|||
|
||||
return false;
|
||||
},
|
||||
// clipboardTextSerializer: (slice) => {
|
||||
// const doc = slice.content;
|
||||
clipboardTextSerializer: (slice) => {
|
||||
const doc = slice.content;
|
||||
|
||||
// if (!doc) {
|
||||
// return '';
|
||||
// }
|
||||
if (!doc) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// const content = prosemirrorToMarkdown({
|
||||
// content: doc,
|
||||
// });
|
||||
const content = prosemirrorToMarkdown({
|
||||
content: doc,
|
||||
});
|
||||
|
||||
// return content;
|
||||
// },
|
||||
return content;
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Node } from './node';
|
||||
|
||||
export class Banner extends Node {
|
||||
type = 'banner';
|
||||
export class Callout extends Node {
|
||||
type = 'callout';
|
||||
|
||||
matching() {
|
||||
return this.DOMNode.nodeName === 'DIV' && this.DOMNode.classList.contains('banner');
|
||||
return this.DOMNode.nodeName === 'DIV' && this.DOMNode.classList.contains('callout');
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
// 自定义节点
|
||||
import { Iframe } from './nodes/iframe';
|
||||
import { Attachment } from './nodes/attachment';
|
||||
import { Banner } from './nodes/banner';
|
||||
import { Callout } from './nodes/callout';
|
||||
import { Status } from './nodes/status';
|
||||
import { DocumentReference } from './nodes/document-reference';
|
||||
import { DocumentChildren } from './nodes/document-children';
|
||||
|
@ -55,7 +55,7 @@ export class Renderer {
|
|||
this.nodes = [
|
||||
Attachment,
|
||||
Countdown,
|
||||
Banner,
|
||||
Callout,
|
||||
Iframe,
|
||||
Status,
|
||||
Mention,
|
||||
|
|
|
@ -8,7 +8,7 @@ import katex from './markdownKatex';
|
|||
import tasklist from './markdownTaskList';
|
||||
import splitMixedLists from './markedownSplitMixedList';
|
||||
import markdownUnderline from './markdownUnderline';
|
||||
import markdownBanner from './markdownBanner';
|
||||
import markdownCallout from './markdownCallout';
|
||||
import { markdownItTable } from './markdownTable';
|
||||
import { createMarkdownContainer } from './markdownItContainer';
|
||||
|
||||
|
@ -33,7 +33,7 @@ const markdown = markdownit('commonmark')
|
|||
.use(emoji)
|
||||
.use(katex)
|
||||
// 以下为自定义节点
|
||||
.use(markdownBanner)
|
||||
.use(markdownCallout)
|
||||
.use(markdownAttachment)
|
||||
.use(markdownCountdown)
|
||||
.use(markdownIframe)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import container from 'markdown-it-container';
|
||||
|
||||
export const typesAvailable = ['info', 'warning', 'danger', 'success'];
|
||||
const typesAvailable = ['callout'];
|
||||
|
||||
const buildRender = (type) => (tokens, idx, options, env, slf) => {
|
||||
const tag = tokens[idx];
|
||||
|
||||
if (tag.nesting === 1) {
|
||||
tag.attrSet('data-type', type);
|
||||
tag.attrJoin('class', `banner banner-${type}`);
|
||||
tag.attrJoin('class', `callout`);
|
||||
}
|
||||
|
||||
return slf.renderToken(tokens, idx, options, env, slf);
|
||||
|
@ -16,7 +15,7 @@ const buildRender = (type) => (tokens, idx, options, env, slf) => {
|
|||
/**
|
||||
* @param {object} md Markdown object
|
||||
*/
|
||||
export default function markdownBanner(md) {
|
||||
export default function markdownCallout(md) {
|
||||
// create a custom container to each callout type
|
||||
typesAvailable.forEach((type) => {
|
||||
md.use(container, type, {
|
|
@ -1,8 +1,8 @@
|
|||
import { MarkdownSerializer as ProseMirrorMarkdownSerializer, defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { Attachment } from '../../extensions/attachment';
|
||||
import { Banner } from '../../extensions/banner';
|
||||
import { Bold } from '../../extensions/bold';
|
||||
import { BulletList } from '../../extensions/bullet-list';
|
||||
import { Callout } from '../../extensions/callout';
|
||||
import { Code } from '../../extensions/code';
|
||||
import { CodeBlock } from '../../extensions/code-block';
|
||||
import { Countdown } from '../../extensions/countdown';
|
||||
|
@ -92,14 +92,6 @@ const SerializerConfig = {
|
|||
|
||||
nodes: {
|
||||
[Attachment.name]: renderCustomContainer('attachment'),
|
||||
[Banner.name]: (state, node) => {
|
||||
state.write(`:::${node.attrs.type || 'info'}\n`);
|
||||
state.ensureNewLine();
|
||||
state.renderContent(node);
|
||||
state.ensureNewLine();
|
||||
state.write(':::');
|
||||
state.closeBlock(node);
|
||||
},
|
||||
blockquote: (state, node) => {
|
||||
if (node.attrs.multiline) {
|
||||
state.write('>>>');
|
||||
|
@ -113,6 +105,14 @@ const SerializerConfig = {
|
|||
}
|
||||
},
|
||||
[BulletList.name]: defaultMarkdownSerializer.nodes.bullet_list,
|
||||
[Callout.name]: (state, node) => {
|
||||
state.write(`:::callout\n`);
|
||||
state.ensureNewLine();
|
||||
state.renderContent(node);
|
||||
state.ensureNewLine();
|
||||
state.write(':::');
|
||||
state.closeBlock(node);
|
||||
},
|
||||
[CodeBlock.name]: (state, node) => {
|
||||
state.write(`\`\`\`${node.attrs.language || ''}\n`);
|
||||
state.text(node.textContent, false);
|
||||
|
|
|
@ -32,7 +32,7 @@ import { Blockquote } from './menus/blockquote';
|
|||
import { HorizontalRule } from './menus/horizontal-rule';
|
||||
import { Search } from './menus/search';
|
||||
|
||||
import { Banner } from './menus/banner';
|
||||
import { Callout } from './menus/callout';
|
||||
import { Countdonw } from './menus/countdown';
|
||||
import { DocumentReference } from './menus/document-reference';
|
||||
import { Image } from './menus/image';
|
||||
|
@ -88,7 +88,7 @@ export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
|
|||
<HorizontalRule editor={editor} />
|
||||
<Search editor={editor} />
|
||||
|
||||
<Banner editor={editor} />
|
||||
<Callout editor={editor} />
|
||||
<Countdonw editor={editor} />
|
||||
<DocumentReference editor={editor} />
|
||||
<Image editor={editor} />
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { BannerBubbleMenu } from './bubble';
|
||||
|
||||
export const Banner: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BannerBubbleMenu editor={editor} />
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,7 +5,7 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { IconDrawBoard } from 'components/icons';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Divider } from '../../divider';
|
||||
import { Banner } from '../../extensions/banner';
|
||||
import { Callout } from '../../extensions/callout';
|
||||
import { deleteNode } from '../../utils/delete-node';
|
||||
import styles from './bubble.module.scss';
|
||||
import { useCallback } from 'react';
|
||||
|
@ -16,13 +16,13 @@ const TEXT_COLORS = ['#d83931', '#de7802', '#dc9b04', '#2ea121', '#245bdb', '#64
|
|||
const BORDER_COLORS = ['#fbbfbc', '#fed4a4', '#fff67a', '#b7edb1', '#bacefd', '#cdb2fa', '#dee0e3'];
|
||||
const BACKGROUND_COLORS = ['#fef1f1', '#feead2', '#ffc', '#d9f5d6', '#e1eaff', '#ece2fe', '#f2f3f5'];
|
||||
|
||||
export const BannerBubbleMenu: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
export const CalloutBubbleMenu: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
const setColor = useCallback(
|
||||
(key, color) => {
|
||||
return () => {
|
||||
editor
|
||||
.chain()
|
||||
.updateAttributes(Banner.name, {
|
||||
.updateAttributes(Callout.name, {
|
||||
[key]: color,
|
||||
})
|
||||
.focus()
|
||||
|
@ -37,18 +37,17 @@ export const BannerBubbleMenu: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
className={'bubble-menu'}
|
||||
editor={editor}
|
||||
pluginKey="banner-bubble-menu"
|
||||
shouldShow={() => editor.isActive(Banner.name)}
|
||||
shouldShow={() => editor.isActive(Callout.name)}
|
||||
matchRenderContainer={(node) => node && node.id === 'js-bannber-container'}
|
||||
>
|
||||
<Space>
|
||||
<Popover
|
||||
spacing={10}
|
||||
visible
|
||||
style={{ padding: '0 12px 12px', overflow: 'hidden' }}
|
||||
content={
|
||||
<>
|
||||
<section className={styles.colorWrap}>
|
||||
<Text type="tertiary">字体颜色</Text>
|
||||
<Text type="secondary">字体颜色</Text>
|
||||
<div>
|
||||
{TEXT_COLORS.map((color) => (
|
||||
<div className={styles.color} style={{ color: color }} onClick={setColor('textColor', color)}>
|
||||
|
@ -58,7 +57,7 @@ export const BannerBubbleMenu: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
</div>
|
||||
</section>
|
||||
<section className={styles.colorWrap}>
|
||||
<Text type="tertiary">边框颜色</Text>
|
||||
<Text type="secondary">边框颜色</Text>
|
||||
|
||||
<div>
|
||||
{BORDER_COLORS.map((color) => (
|
||||
|
@ -71,7 +70,7 @@ export const BannerBubbleMenu: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
</div>
|
||||
</section>
|
||||
<section className={styles.colorWrap}>
|
||||
<Text type="tertiary">背景颜色</Text>
|
||||
<Text type="secondary">背景颜色</Text>
|
||||
<div>
|
||||
{BACKGROUND_COLORS.map((color) => (
|
||||
<div
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { CalloutBubbleMenu } from './bubble';
|
||||
|
||||
export const Callout: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CalloutBubbleMenu editor={editor} />
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -15,6 +15,7 @@ import {
|
|||
IconAttachment,
|
||||
IconMath,
|
||||
IconCountdown,
|
||||
IconCallout,
|
||||
} from 'components/icons';
|
||||
import { GridSelect } from 'components/grid-select';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
|
@ -90,8 +91,8 @@ export const Insert: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
<IconStatus /> 状态
|
||||
</Dropdown.Item>
|
||||
|
||||
<Dropdown.Item onClick={() => editor.chain().focus().setBanner({ type: 'info' }).run()}>
|
||||
<IconInfo /> 信息框
|
||||
<Dropdown.Item onClick={() => editor.chain().focus().setCallout().run()}>
|
||||
<IconCallout /> 高亮块
|
||||
</Dropdown.Item>
|
||||
|
||||
<Dropdown.Divider />
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
IconAttachment,
|
||||
IconMath,
|
||||
IconCountdown,
|
||||
IconCallout,
|
||||
} from 'components/icons';
|
||||
import { createCountdown } from './countdown/service';
|
||||
import { createOrToggleLink } from './link/service';
|
||||
|
@ -237,14 +238,14 @@ export const QUICK_INSERT_ITEMS = [
|
|||
},
|
||||
|
||||
{
|
||||
key: '信息框',
|
||||
key: '高亮块',
|
||||
label: (
|
||||
<Space>
|
||||
<IconInfo />
|
||||
信息框
|
||||
<IconCallout />
|
||||
高亮块
|
||||
</Space>
|
||||
),
|
||||
command: (editor: Editor) => editor.chain().focus().setBanner({ type: 'info' }).run(),
|
||||
command: (editor: Editor) => editor.chain().focus().setCallout().run(),
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Attachment } from './extensions/attachment';
|
||||
import { BackgroundColor } from './extensions/background-color';
|
||||
import { Banner } from './extensions/banner';
|
||||
import { Blockquote } from './extensions/blockquote';
|
||||
import { Bold } from './extensions/bold';
|
||||
import { BulletList } from './extensions/bullet-list';
|
||||
import { Callout } from './extensions/callout';
|
||||
import { Code } from './extensions/code';
|
||||
import { CodeBlock } from './extensions/code-block';
|
||||
import { Color } from './extensions/color';
|
||||
|
@ -57,10 +57,10 @@ import { Paste } from './extensions/paste';
|
|||
export const BaseKit = [
|
||||
Attachment,
|
||||
BackgroundColor,
|
||||
Banner,
|
||||
Blockquote,
|
||||
Bold,
|
||||
BulletList,
|
||||
Callout,
|
||||
Code,
|
||||
CodeBlock,
|
||||
Color,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.wrap {
|
||||
line-height: 0;
|
||||
margin-top: 0.75em;
|
||||
|
||||
.innerWrap {
|
||||
display: flex;
|
||||
|
@ -28,7 +29,7 @@
|
|||
}
|
||||
|
||||
p {
|
||||
margin-top: .25em;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
p:first-child {
|
|
@ -1,12 +1,10 @@
|
|||
import { useCallback } from 'react';
|
||||
import { NodeViewWrapper, NodeViewContent } from '@tiptap/react';
|
||||
import { Popover } from '@douyinfe/semi-ui';
|
||||
import cls from 'classnames';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import { EmojiPicker } from 'components/emoji-picker';
|
||||
import styles from './index.module.scss';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
export const BannerWrapper = ({ node, updateAttributes }) => {
|
||||
export const CalloutWrapper = ({ node, updateAttributes }) => {
|
||||
const { emoji, textColor, borderColor, backgroundColor } = node.attrs;
|
||||
|
||||
const onSelectEmoji = useCallback((emoji) => {
|
Loading…
Reference in New Issue