Skip to content

Commit e14fd69

Browse files
committed
添加卡片前后节点支持,优化表格溢出处理逻辑
1 parent 62855d8 commit e14fd69

File tree

9 files changed

+225
-30
lines changed

9 files changed

+225
-30
lines changed

src/MarkdownEditor/editor/elements/index.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,29 @@ export const MElement = (props: RenderElementProps) => {
9393
return <FootnoteDefinition {...props} />;
9494
case 'footnoteReference':
9595
return <FootnoteReference {...props} />;
96+
case 'card-before':
97+
return (
98+
<span
99+
style={{
100+
minWidth: 2,
101+
}}
102+
{...props.attributes}
103+
>
104+
{props.children}
105+
</span>
106+
);
107+
case 'card-after':
108+
return (
109+
<span
110+
style={{
111+
minWidth: 2,
112+
alignSelf: 'end',
113+
}}
114+
{...props.attributes}
115+
>
116+
{props.children}
117+
</span>
118+
);
96119
default:
97120
return <Paragraph {...props} />;
98121
}

src/MarkdownEditor/editor/elements/table.tsx

Lines changed: 93 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const Table = observer((props: RenderElementProps) => {
7474
});
7575

7676
const tableRef = React.useRef<NodeEntry<TableNode>>();
77+
const overflowShadowContainerRef = React.useRef<HTMLTableElement>(null);
7778
const tableCellRef = useRef<NodeEntry<TableCellNode>>();
7879

7980
useEffect(() => {
@@ -141,48 +142,115 @@ export const Table = observer((props: RenderElementProps) => {
141142
}
142143
}
143144
}, [store.tableCellNode, store.editor, setState]);
145+
const tableTargetRef = useRef<HTMLTableElement>(null);
146+
useEffect(() => {
147+
if (!tableTargetRef.current) {
148+
return;
149+
}
150+
const observerRoot = (tableTargetRef as any).current?.parentNode;
151+
const observerTarget = (tableTargetRef as any).current;
152+
const overflowShadowContainer = overflowShadowContainerRef.current!;
153+
overflowShadowContainer.classList.add('overflow-shadow-container');
154+
overflowShadowContainer.classList.add('card-table-wrap');
155+
const options = {
156+
root: observerRoot,
157+
threshold: 1,
158+
};
159+
160+
new IntersectionObserver(([entry]) => {
161+
if (entry.intersectionRatio !== 1) {
162+
overflowShadowContainer.classList.add(
163+
'is-overflowing',
164+
'is-scrolled-left',
165+
);
166+
} else {
167+
overflowShadowContainer.classList.remove('is-overflowing');
168+
}
169+
}, options).observe(observerTarget);
144170

171+
let handleScrollX = (e: any) => {
172+
if (e.target.scrollLeft < 1) {
173+
overflowShadowContainer.classList.add('is-scrolled-left');
174+
} else {
175+
overflowShadowContainer.classList.remove('is-scrolled-left');
176+
}
177+
if (
178+
Math.abs(
179+
e.target.scrollLeft +
180+
e.target.offsetWidth -
181+
observerTarget.offsetWidth,
182+
) <= 1
183+
) {
184+
overflowShadowContainer.classList.add('is-scrolled-right');
185+
} else {
186+
overflowShadowContainer.classList.remove('is-scrolled-right');
187+
}
188+
};
189+
190+
observerRoot.addEventListener('scroll', handleScrollX);
191+
192+
return () => {
193+
observerRoot.removeEventListener('scroll', handleScrollX);
194+
};
195+
}, []);
145196
return useMemo(() => {
197+
const [pre, ...row] = props.children;
198+
const after = row.pop();
199+
146200
return (
147201
<div
148-
className={'ant-md-editor-drag-el ant-md-editor-table'}
149202
{...props.attributes}
150203
data-be={'table'}
151204
onDragStart={store.dragStart}
152205
onMouseUp={handleClickTable}
206+
ref={overflowShadowContainerRef}
153207
style={{
154-
maxWidth: '100%',
155-
...(store.editor?.children?.length === 1
156-
? {}
157-
: {
158-
border: '1px solid #e8e8e8',
159-
borderRadius: 16,
160-
}),
208+
display: 'flex',
209+
gap: 1,
161210
}}
162211
>
163-
{store.tableAttrVisible && (
164-
<TableAttr
165-
state={state}
166-
setState={setState}
167-
tableRef={tableRef}
168-
tableCellRef={tableCellRef}
169-
/>
170-
)}
171-
<DragHandle />
212+
{pre}
172213
<div
214+
className={'ant-md-editor-drag-el ant-md-editor-table'}
173215
style={{
174-
width: '100%',
175-
borderCollapse: 'collapse',
176-
tableLayout: 'fixed',
177-
borderSpacing: 0,
178216
maxWidth: '100%',
179-
overflow: 'auto',
217+
...(store.editor?.children?.length === 1
218+
? {}
219+
: {
220+
border: '1px solid #e8e8e8',
221+
borderRadius: 16,
222+
}),
180223
}}
181224
>
182-
<table>
183-
<tbody>{props.children}</tbody>
184-
</table>
225+
{store.tableAttrVisible && (
226+
<TableAttr
227+
state={state}
228+
setState={setState}
229+
tableRef={tableRef}
230+
tableCellRef={tableCellRef}
231+
/>
232+
)}
233+
<DragHandle />
234+
235+
<div
236+
style={{
237+
width: '100%',
238+
maxWidth: '100%',
239+
overflow: 'auto',
240+
}}
241+
>
242+
<table
243+
style={{
244+
borderCollapse: 'collapse',
245+
borderSpacing: 0,
246+
}}
247+
ref={tableTargetRef}
248+
>
249+
<tbody>{row}</tbody>
250+
</table>
251+
</div>
185252
</div>
253+
{after}
186254
</div>
187255
);
188256
}, [

src/MarkdownEditor/editor/parser/parserMarkdown.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,17 @@ const parseTableOrChart = (
226226

227227
const node: TableNode | ChartNode = {
228228
type: isChart ? 'chart' : 'table',
229-
children,
229+
children: [
230+
{
231+
type: 'card-before',
232+
children: [{ text: '' }],
233+
},
234+
...children,
235+
{
236+
type: 'card-after',
237+
children: [{ text: '' }],
238+
},
239+
],
230240
otherProps,
231241
};
232242
return node;

src/MarkdownEditor/editor/plugins/hotKeyCommands/enter.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,25 @@ export class EnterKey {
4949
});
5050
if (node) {
5151
let [el, path] = node;
52+
53+
if (el.type === 'card-before') {
54+
Transforms.insertNodes(this.editor, EditorUtils.p, {
55+
at: Path.parent(path),
56+
select: true,
57+
});
58+
e.preventDefault();
59+
return;
60+
}
61+
62+
if (el.type === 'card-after') {
63+
console.log(Path.parent(path));
64+
Transforms.insertNodes(this.editor, EditorUtils.p, {
65+
at: Path.next(Path.parent(path)),
66+
select: true,
67+
});
68+
e.preventDefault();
69+
return;
70+
}
5271
switch (el.type as NodeTypes) {
5372
case 'table-cell':
5473
e.preventDefault();

src/MarkdownEditor/editor/plugins/index.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { Editor, Node, Path, Transforms } from 'slate';
1+
import { Editor, Node, Path, Range, Transforms } from 'slate';
22

33
export const inlineNode = new Set(['break']);
44

55
const voidNode = new Set(['hr', 'break']);
66

7+
function hasRange(editor: Editor, range: { anchor: any; focus: any }): boolean {
8+
const { anchor, focus } = range;
9+
return (
10+
Editor.hasPath(editor, anchor.path) && Editor.hasPath(editor, focus.path)
11+
);
12+
}
13+
714
/**
815
* 为编辑器添加 Markdown 支持的插件。
916
*
@@ -25,7 +32,7 @@ const voidNode = new Set(['hr', 'break']);
2532
* 该插件还根据 `store.manual` 的值决定是否手动处理某些操作。
2633
*/
2734
export const withMarkdown = (editor: Editor) => {
28-
const { isInline, isVoid, apply } = editor;
35+
const { isInline, isVoid, apply, deleteBackward } = editor;
2936

3037
editor.isInline = (element) => {
3138
return inlineNode.has(element.type) || isInline(element);
@@ -157,5 +164,23 @@ export const withMarkdown = (editor: Editor) => {
157164
apply(operation);
158165
};
159166

167+
editor.deleteBackward = (unit: any) => {
168+
const { selection } = editor;
169+
170+
if (
171+
selection &&
172+
hasRange(editor, selection) &&
173+
Range.isCollapsed(selection)
174+
) {
175+
const node = Node.get(editor, Path.parent(selection.anchor.path));
176+
if (node.type === 'card-before' || node.type === 'card-after') {
177+
return;
178+
}
179+
}
180+
181+
console.log('deleteBackward', unit);
182+
deleteBackward(unit);
183+
};
184+
160185
return editor;
161186
};

src/MarkdownEditor/editor/style.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@ const genStyle: GenerateStyle<ChatTokenType> = (token) => {
255255
'th:last-child,td:last-child': {
256256
borderRight: 'none',
257257
},
258+
'th:last-child': {
259+
borderTopRightRadius: 16,
260+
},
261+
'th:first-child': {
262+
borderTopLeftRadius: 16,
263+
},
258264
'tr:last-child th,tr:last-child td': {
259265
borderBottom: 'none',
260266
},

src/MarkdownEditor/editor/tools/ToolBar/ReadonlyBaseBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const ReadonlyBaseBar = observer(
4040
const listDom = useMemo(() => {
4141
let list = [];
4242

43-
if (store?.editorProps?.comment?.enable) {
43+
if (store?.editorProps?.comment?.onSubmit) {
4444
list.push(
4545
<div
4646
role="button"

src/MarkdownEditor/el.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,20 @@ export type FootnoteDefinitionNode<T = Record<string, any>> = {
5151
h?: number;
5252
};
5353

54+
export type CardBeforeNode = {
55+
type: 'card-before';
56+
children: BaseElement['children'];
57+
};
58+
59+
export type CardAfterNode = {
60+
type: 'card-after';
61+
children: BaseElement['children'];
62+
};
63+
5464
export type TableNode<T = Record<string, any>> = {
5565
contextProps?: T;
5666
type: 'table';
57-
children: TableRowNode[];
67+
children: (TableRowNode | CardBeforeNode | CardAfterNode)[];
5868
otherProps?: {
5969
showSource?: boolean;
6070
config: ChartTypeConfig | ChartTypeConfig[];

src/MarkdownEditor/index.css

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,37 @@
5454
right: -7px;
5555
pointer-events: all;
5656
}
57+
58+
.markdown-editor .overflow-shadow-container::before,
59+
.markdown-editor .overflow-shadow-container::after {
60+
content: '';
61+
position: absolute;
62+
top: 13px;
63+
bottom: 8px;
64+
width: 10px;
65+
opacity: 0;
66+
transition: opacity 0.1s;
67+
z-index: 100;
68+
pointer-events: none;
69+
user-select: none;
70+
height: calc(100% - 32px);
71+
}
72+
73+
.markdown-editor .overflow-shadow-container::after {
74+
right: -4px;
75+
background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.1));
76+
}
77+
78+
.markdown-editor
79+
.overflow-shadow-container.is-overflowing:not(.is-scrolled-right)::after {
80+
opacity: 1;
81+
}
82+
83+
.markdown-editor
84+
.overflow-shadow-container.is-overflowing:not(.is-scrolled-left)::before {
85+
opacity: 1;
86+
}
87+
.markdown-editor .overflow-shadow-container::before {
88+
left: 3px;
89+
background: linear-gradient(to left, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.1));
90+
}

0 commit comments

Comments
 (0)