feat(wysiwyg): Add more block types

This commit is contained in:
Juergen Kunz
2025-06-24 20:32:03 +00:00
parent 856d354b5a
commit 68b4e9ec8e
6 changed files with 998 additions and 54 deletions

View File

@ -233,8 +233,9 @@ export class WysiwygKeyboardHandler {
private async handleBackspace(e: KeyboardEvent, block: IBlock): Promise<void> {
const blockOps = this.component.blockOperations;
// Handle non-editable blocks (divider, image)
if (block.type === 'divider' || block.type === 'image') {
// Handle non-editable blocks
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(block.type)) {
e.preventDefault();
// If it's the only block, delete it and create a new paragraph
@ -311,13 +312,13 @@ export class WysiwygKeyboardHandler {
const prevBlock = blockOps.getPreviousBlock(block.id);
if (prevBlock) {
// If previous block is non-editable (divider/image), select it first
if (prevBlock.type === 'divider' || prevBlock.type === 'image') {
// If previous block is non-editable, select it first
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(prevBlock.type)) {
await blockOps.focusBlock(prevBlock.id);
return;
}
console.log('Backspace at start: Merging with previous block');
// Save checkpoint for undo
this.component.saveToHistory(false);
@ -397,8 +398,9 @@ export class WysiwygKeyboardHandler {
private async handleDelete(e: KeyboardEvent, block: IBlock): Promise<void> {
const blockOps = this.component.blockOperations;
// Handle non-editable blocks (divider, image) - same as backspace
if (block.type === 'divider' || block.type === 'image') {
// Handle non-editable blocks - same as backspace
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(block.type)) {
e.preventDefault();
// If it's the only block, delete it and create a new paragraph
@ -435,9 +437,10 @@ export class WysiwygKeyboardHandler {
blockOps.removeBlock(block.id);
// Focus the appropriate block
if (nextBlock && nextBlock.type !== 'divider' && nextBlock.type !== 'image') {
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nextBlock && !nonEditableTypes.includes(nextBlock.type)) {
await blockOps.focusBlock(nextBlock.id, 'start');
} else if (prevBlock && prevBlock.type !== 'divider' && prevBlock.type !== 'image') {
} else if (prevBlock && !nonEditableTypes.includes(prevBlock.type)) {
await blockOps.focusBlock(prevBlock.id, 'end');
} else if (nextBlock) {
// If next block is also non-editable, just select it
@ -474,7 +477,8 @@ export class WysiwygKeyboardHandler {
if (cursorPos === textLength) {
const nextBlock = blockOps.getNextBlock(block.id);
if (nextBlock && (nextBlock.type === 'divider' || nextBlock.type === 'image')) {
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nextBlock && nonEditableTypes.includes(nextBlock.type)) {
e.preventDefault();
await blockOps.focusBlock(nextBlock.id);
return;
@ -489,13 +493,14 @@ export class WysiwygKeyboardHandler {
*/
private async handleArrowUp(e: KeyboardEvent, block: IBlock): Promise<void> {
// For non-editable blocks, always navigate to previous block
if (block.type === 'divider' || block.type === 'image') {
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(block.type)) {
e.preventDefault();
const blockOps = this.component.blockOperations;
const prevBlock = blockOps.getPreviousBlock(block.id);
if (prevBlock) {
await blockOps.focusBlock(prevBlock.id, prevBlock.type === 'divider' || prevBlock.type === 'image' ? undefined : 'end');
await blockOps.focusBlock(prevBlock.id, nonEditableTypes.includes(prevBlock.type) ? undefined : 'end');
}
return;
}
@ -522,14 +527,13 @@ export class WysiwygKeyboardHandler {
// Check if we're on the first line
if (this.isOnFirstLine(selectionInfo, target, ...shadowRoots)) {
console.log('ArrowUp: On first line, navigating to previous block');
e.preventDefault();
const blockOps = this.component.blockOperations;
const prevBlock = blockOps.getPreviousBlock(block.id);
if (prevBlock) {
console.log('ArrowUp: Focusing previous block:', prevBlock.id);
await blockOps.focusBlock(prevBlock.id, prevBlock.type === 'divider' || prevBlock.type === 'image' ? undefined : 'end');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(prevBlock.id, nonEditableTypes.includes(prevBlock.type) ? undefined : 'end');
}
}
// Otherwise, let browser handle normal navigation
@ -540,13 +544,15 @@ export class WysiwygKeyboardHandler {
*/
private async handleArrowDown(e: KeyboardEvent, block: IBlock): Promise<void> {
// For non-editable blocks, always navigate to next block
if (block.type === 'divider' || block.type === 'image') {
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(block.type)) {
e.preventDefault();
const blockOps = this.component.blockOperations;
const nextBlock = blockOps.getNextBlock(block.id);
if (nextBlock) {
await blockOps.focusBlock(nextBlock.id, nextBlock.type === 'divider' || nextBlock.type === 'image' ? undefined : 'start');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(nextBlock.id, nonEditableTypes.includes(nextBlock.type) ? undefined : 'start');
}
return;
}
@ -573,14 +579,13 @@ export class WysiwygKeyboardHandler {
// Check if we're on the last line
if (this.isOnLastLine(selectionInfo, target, ...shadowRoots)) {
console.log('ArrowDown: On last line, navigating to next block');
e.preventDefault();
const blockOps = this.component.blockOperations;
const nextBlock = blockOps.getNextBlock(block.id);
if (nextBlock) {
console.log('ArrowDown: Focusing next block:', nextBlock.id);
await blockOps.focusBlock(nextBlock.id, nextBlock.type === 'divider' || nextBlock.type === 'image' ? undefined : 'start');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(nextBlock.id, nonEditableTypes.includes(nextBlock.type) ? undefined : 'start');
}
}
// Otherwise, let browser handle normal navigation
@ -607,13 +612,15 @@ export class WysiwygKeyboardHandler {
*/
private async handleArrowLeft(e: KeyboardEvent, block: IBlock): Promise<void> {
// For non-editable blocks, navigate to previous block
if (block.type === 'divider' || block.type === 'image') {
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(block.type)) {
e.preventDefault();
const blockOps = this.component.blockOperations;
const prevBlock = blockOps.getPreviousBlock(block.id);
if (prevBlock) {
await blockOps.focusBlock(prevBlock.id, prevBlock.type === 'divider' || prevBlock.type === 'image' ? undefined : 'end');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(prevBlock.id, nonEditableTypes.includes(prevBlock.type) ? undefined : 'end');
}
return;
}
@ -640,16 +647,15 @@ export class WysiwygKeyboardHandler {
// Check if cursor is at the beginning of the block
const cursorPos = WysiwygSelection.getCursorPositionInElement(target, ...shadowRoots);
console.log('ArrowLeft: Cursor position:', cursorPos, 'in block:', block.id);
if (cursorPos === 0) {
const blockOps = this.component.blockOperations;
const prevBlock = blockOps.getPreviousBlock(block.id);
console.log('ArrowLeft: At start, previous block:', prevBlock?.id);
if (prevBlock) {
e.preventDefault();
await blockOps.focusBlock(prevBlock.id, prevBlock.type === 'divider' || prevBlock.type === 'image' ? undefined : 'end');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(prevBlock.id, nonEditableTypes.includes(prevBlock.type) ? undefined : 'end');
}
}
// Otherwise, let the browser handle normal left arrow navigation
@ -660,13 +666,15 @@ export class WysiwygKeyboardHandler {
*/
private async handleArrowRight(e: KeyboardEvent, block: IBlock): Promise<void> {
// For non-editable blocks, navigate to next block
if (block.type === 'divider' || block.type === 'image') {
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
if (nonEditableTypes.includes(block.type)) {
e.preventDefault();
const blockOps = this.component.blockOperations;
const nextBlock = blockOps.getNextBlock(block.id);
if (nextBlock) {
await blockOps.focusBlock(nextBlock.id, nextBlock.type === 'divider' || nextBlock.type === 'image' ? undefined : 'start');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(nextBlock.id, nonEditableTypes.includes(nextBlock.type) ? undefined : 'start');
}
return;
}
@ -701,7 +709,8 @@ export class WysiwygKeyboardHandler {
if (nextBlock) {
e.preventDefault();
await blockOps.focusBlock(nextBlock.id, nextBlock.type === 'divider' || nextBlock.type === 'image' ? undefined : 'start');
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
await blockOps.focusBlock(nextBlock.id, nonEditableTypes.includes(nextBlock.type) ? undefined : 'start');
}
}
// Otherwise, let the browser handle normal right arrow navigation