Skip to content

Commit 622fc1b

Browse files
committed
feat(editor): add new methods to MarkdownEditorRef and improve keyboard handling
implement setMarkdown, getBlocks, and setBlocks methods in EditorCore update QuotePlugin validation to use maxLength instead of maxDepth enhance KeyboardHandler to properly handle empty block deletion track cursor position in ParagraphComponent for better backspace handling
1 parent a7f63b8 commit 622fc1b

File tree

5 files changed

+54
-17
lines changed

5 files changed

+54
-17
lines changed

components/editor/core/EditorCore.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,12 @@ export const EditorCore = forwardRef<ExtendedMarkdownEditorRef, ExtendedMarkdown
470470
useImperativeHandle(ref, () => ({
471471
// Base MarkdownEditorRef methods
472472
getMarkdown: () => getMarkdown(),
473+
setMarkdown: (markdown: string) => setMarkdown(markdown),
474+
getBlocks: () => blocks,
475+
setBlocks: (newBlocks: EditorBlock[]) => {
476+
// TODO: Implement setBlocks
477+
// Not yet implemented
478+
},
473479
focus: () => {
474480
// Focus the currently focused block, or focus the last block
475481
if (focusedBlockId) {
@@ -535,11 +541,6 @@ export const EditorCore = forwardRef<ExtendedMarkdownEditorRef, ExtendedMarkdown
535541
exportToMarkdown: () => getMarkdown(),
536542
importFromMarkdown: (markdown: string) => setMarkdown(markdown),
537543
exportToPlainText: () => blocks.map(b => b.content).join('\n'),
538-
getBlocks: () => blocks,
539-
setBlocks: (newBlocks: EditorBlock[]) => {
540-
// TODO: Implement setBlocks
541-
// Not yet implemented
542-
},
543544

544545
// Editor state
545546
getEditorState: () => state,

components/editor/core/KeyboardHandler.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,42 @@ export const KeyboardHandler: React.FC<KeyboardHandlerProps> = ({
131131
}
132132

133133
// Handle Backspace key
134-
if (key === 'Backspace' && cursorPosition === 0) {
134+
// Trigger handler at cursor position 0, or if content is empty (for deletion)
135+
const isContentEmpty = block.content.trim() === '';
136+
const shouldHandleBackspace = cursorPosition === 0 || isContentEmpty;
137+
138+
if (key === 'Backspace' && shouldHandleBackspace) {
135139
if (controller?.handleBackspace) {
136140
const result = controller.handleBackspace(block);
137-
141+
142+
// If result is explicitly null and content is empty, delete the block
143+
if (result === null && isContentEmpty) {
144+
if (event.preventDefault) event.preventDefault();
145+
146+
// Delete the block
147+
editor.deleteBlock(block.id);
148+
149+
// Focus previous block
150+
if (currentBlockIndex > 0) {
151+
const prevBlock = blocks[currentBlockIndex - 1];
152+
if (prevBlock) {
153+
editor.focusBlock(prevBlock.id);
154+
}
155+
}
156+
157+
return;
158+
}
159+
138160
if (result) {
139161
if (event.preventDefault) event.preventDefault();
140-
162+
141163
if (typeof result === 'object' && result !== null) {
142164
// Update the block
143165
editor.updateBlock(block.id, result);
144166
} else {
145167
// Delete the block
146168
editor.deleteBlock(block.id);
147-
169+
148170
// Focus previous block
149171
if (currentBlockIndex > 0) {
150172
const prevBlock = blocks[currentBlockIndex - 1];
@@ -153,7 +175,7 @@ export const KeyboardHandler: React.FC<KeyboardHandlerProps> = ({
153175
}
154176
}
155177
}
156-
178+
157179
return;
158180
}
159181
}

components/editor/plugins/built-in/ParagraphPlugin.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { forwardRef, memo, useImperativeHandle, useRef } from 'react';
1+
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
22
import { StyleSheet, TextInput, View } from 'react-native';
33
import { Colors } from '../../../../constants/Colors';
44
import { useColorScheme } from '../../../../hooks/useColorScheme';
@@ -24,6 +24,7 @@ const ParagraphComponent = forwardRef<TextInput, BlockComponentProps>(({
2424
style
2525
}, ref) => {
2626
const inputRef = useRef<TextInput>(null);
27+
const [cursorPosition, setCursorPosition] = useState(0);
2728
const colorScheme = useColorScheme();
2829
const colors = Colors[colorScheme ?? 'light'];
2930

@@ -38,6 +39,13 @@ const ParagraphComponent = forwardRef<TextInput, BlockComponentProps>(({
3839
const pluginInstance = new ParagraphPlugin();
3940
const controller = pluginInstance.controller;
4041

42+
const handleSelectionChange = (event: any) => {
43+
const selection = event.nativeEvent.selection;
44+
if (selection) {
45+
setCursorPosition(selection.start);
46+
}
47+
};
48+
4149
const handleTextChange = (text: string) => {
4250
if (onBlockChange) {
4351
onBlockChange({ content: text });
@@ -53,7 +61,7 @@ const ParagraphComponent = forwardRef<TextInput, BlockComponentProps>(({
5361
<KeyboardHandler
5462
block={block}
5563
controller={controller}
56-
cursorPosition={0}
64+
cursorPosition={cursorPosition}
5765
>
5866
{({ onKeyPress, preventNewlines }: { onKeyPress: (event: any) => void; preventNewlines?: boolean }) => (
5967
<View style={[styles.container, style]}>
@@ -63,6 +71,7 @@ const ParagraphComponent = forwardRef<TextInput, BlockComponentProps>(({
6371
onChangeText={handleTextChange}
6472
onFocus={onFocus}
6573
onBlur={onBlur}
74+
onSelectionChange={handleSelectionChange}
6675
onKeyPress={onKeyPress}
6776
placeholder="Type something..."
6877
placeholderTextColor={colors.textSecondary}
@@ -174,12 +183,14 @@ export class ParagraphPlugin extends BlockPlugin {
174183
}
175184

176185
protected handleBackspace(block: EditorBlock): EditorBlock | null {
177-
// If paragraph is empty and backspace is pressed, convert to previous block type
186+
// If paragraph is empty and backspace is pressed, delete the block
187+
// We return null to let the KeyboardHandler handle block deletion
178188
if (block.content.trim() === '') {
179-
return null; // Let editor handle deletion
189+
return null; // Let KeyboardHandler handle block deletion
180190
}
181-
182-
return block;
191+
192+
// Return null to let default behavior handle non-empty paragraphs
193+
return null;
183194
}
184195

185196
protected transformContent(content: string): string {

components/editor/plugins/built-in/QuotePlugin.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ export class QuotePlugin extends BlockPlugin {
288288
readonly settings = {
289289
allowedParents: ['root', 'callout'] as EditorBlockType[],
290290
validation: {
291-
maxDepth: 5
291+
maxLength: 10000
292292
},
293293
defaultMeta: {
294294
depth: 1

types/editor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ export type EditorMode = 'edit' | 'raw' | 'preview';
8686

8787
export type MarkdownEditorRef = {
8888
getMarkdown: () => string;
89+
setMarkdown: (markdown: string) => void;
90+
getBlocks: () => EditorBlock[];
91+
setBlocks: (blocks: EditorBlock[]) => void;
8992
focus: () => void;
9093
insertBlock: (type: EditorBlockType, index?: number) => void;
9194
deleteBlock: (id: string) => void;

0 commit comments

Comments
 (0)