feat(wysiwyg): implement backspace
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
WysiwygKeyboardHandler,
|
||||
WysiwygDragDropHandler,
|
||||
WysiwygModalManager,
|
||||
WysiwygHistory,
|
||||
DeesSlashMenu,
|
||||
DeesFormattingMenu
|
||||
} from './index.js';
|
||||
@@ -89,6 +90,7 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
private inputHandler: WysiwygInputHandler;
|
||||
private keyboardHandler: WysiwygKeyboardHandler;
|
||||
private dragDropHandler: WysiwygDragDropHandler;
|
||||
private history: WysiwygHistory;
|
||||
|
||||
public static styles = [
|
||||
...DeesInputBase.baseStyles,
|
||||
@@ -103,6 +105,7 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
this.inputHandler = new WysiwygInputHandler(this);
|
||||
this.keyboardHandler = new WysiwygKeyboardHandler(this);
|
||||
this.dragDropHandler = new WysiwygDragDropHandler(this);
|
||||
this.history = new WysiwygHistory();
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
@@ -143,6 +146,27 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
}
|
||||
});
|
||||
|
||||
// Add global keyboard listener for undo/redo
|
||||
this.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
// Check if the event is from within our editor
|
||||
const target = e.target as HTMLElement;
|
||||
if (!this.contains(target) && !this.shadowRoot?.contains(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle undo/redo
|
||||
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && e.key === 'z') {
|
||||
e.preventDefault();
|
||||
this.undo();
|
||||
} else if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'z') {
|
||||
e.preventDefault();
|
||||
this.redo();
|
||||
}
|
||||
});
|
||||
|
||||
// Save initial state to history
|
||||
this.history.saveState(this.blocks, this.selectedBlockId);
|
||||
|
||||
// Render blocks programmatically
|
||||
this.renderBlocksProgrammatically();
|
||||
}
|
||||
@@ -516,6 +540,9 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
this.value = WysiwygConverters.getMarkdownOutput(this.blocks);
|
||||
}
|
||||
this.changeSubject.next(this.value);
|
||||
|
||||
// Save to history (debounced)
|
||||
this.saveToHistory(true);
|
||||
}
|
||||
|
||||
public getValue(): string {
|
||||
@@ -879,4 +906,88 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the last action
|
||||
*/
|
||||
private undo(): void {
|
||||
console.log('Undo triggered');
|
||||
const state = this.history.undo();
|
||||
if (state) {
|
||||
this.restoreState(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redo the next action
|
||||
*/
|
||||
private redo(): void {
|
||||
console.log('Redo triggered');
|
||||
const state = this.history.redo();
|
||||
if (state) {
|
||||
this.restoreState(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore editor state from history
|
||||
*/
|
||||
private restoreState(state: { blocks: IBlock[]; selectedBlockId: string | null; cursorPosition?: { blockId: string; offset: number } }): void {
|
||||
// Update blocks
|
||||
this.blocks = state.blocks;
|
||||
this.selectedBlockId = state.selectedBlockId;
|
||||
|
||||
// Re-render blocks
|
||||
this.renderBlocksProgrammatically();
|
||||
|
||||
// Restore cursor position if available
|
||||
if (state.cursorPosition) {
|
||||
setTimeout(() => {
|
||||
const blockWrapper = this.shadowRoot?.querySelector(`[data-block-id="${state.cursorPosition!.blockId}"]`);
|
||||
const blockComponent = blockWrapper?.querySelector('dees-wysiwyg-block') as any;
|
||||
if (blockComponent) {
|
||||
blockComponent.focusWithCursor(state.cursorPosition!.offset);
|
||||
}
|
||||
}, 50);
|
||||
} else if (state.selectedBlockId) {
|
||||
// Just focus the selected block
|
||||
setTimeout(() => {
|
||||
this.blockOperations.focusBlock(state.selectedBlockId!);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// Update value
|
||||
this.updateValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current state to history with cursor position
|
||||
*/
|
||||
public saveToHistory(debounce: boolean = true): void {
|
||||
// Get current cursor position if a block is focused
|
||||
let cursorPosition: { blockId: string; offset: number } | undefined;
|
||||
|
||||
if (this.selectedBlockId) {
|
||||
const blockWrapper = this.shadowRoot?.querySelector(`[data-block-id="${this.selectedBlockId}"]`);
|
||||
const blockComponent = blockWrapper?.querySelector('dees-wysiwyg-block') as any;
|
||||
if (blockComponent && typeof blockComponent.getCursorPosition === 'function') {
|
||||
const editableElement = blockComponent.shadowRoot?.querySelector('.block') as HTMLElement;
|
||||
if (editableElement) {
|
||||
const offset = blockComponent.getCursorPosition(editableElement);
|
||||
if (offset !== null) {
|
||||
cursorPosition = {
|
||||
blockId: this.selectedBlockId,
|
||||
offset
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debounce) {
|
||||
this.history.saveState(this.blocks, this.selectedBlockId, cursorPosition);
|
||||
} else {
|
||||
this.history.saveCheckpoint(this.blocks, this.selectedBlockId, cursorPosition);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user