update
This commit is contained in:
@ -8,6 +8,26 @@
|
|||||||
- **Solution**: Changed the selector to be more specific: `.block.${blockType}` which ensures the correct element is found for each block type
|
- **Solution**: Changed the selector to be more specific: `.block.${blockType}` which ensures the correct element is found for each block type
|
||||||
- **Result**: All block types now highlight consistently when selected
|
- **Result**: All block types now highlight consistently when selected
|
||||||
|
|
||||||
|
### Enter Key Block Creation Fix ✅
|
||||||
|
- **Issue**: "When pressing enter and jumping to new block then typing something: The cursor is not at the beginning of the new block and there is content"
|
||||||
|
- **Root Cause**: Block handlers were rendering content with template syntax `${block.content || ''}` in their render methods, which violates the static HTML principle
|
||||||
|
- **Solution**:
|
||||||
|
- Removed all `${block.content}` from render methods in paragraph, heading, quote, and code block handlers
|
||||||
|
- Content is now set programmatically in the setup() method only when needed
|
||||||
|
- Fixed `setCursorToStart` and `setCursorToEnd` to always find elements fresh instead of relying on cached `blockElement`
|
||||||
|
- **Result**: New empty blocks remain truly empty, cursor positioning works correctly
|
||||||
|
|
||||||
|
### Backspace Key Deletion Fix ✅
|
||||||
|
- **Issue**: "After typing in a new block, pressing backspace deletes the whole block instead of just the last character"
|
||||||
|
- **Root Cause**:
|
||||||
|
1. `getCursorPositionInElement` was using `element.contains()` which doesn't work across Shadow DOM boundaries
|
||||||
|
2. The backspace handler was checking `block.content === ''` which only contains the stored content, not the actual DOM content
|
||||||
|
- **Solution**:
|
||||||
|
1. Fixed `getCursorPositionInElement` to use `containsAcrossShadowDOM` for proper Shadow DOM support
|
||||||
|
2. Updated backspace handler to get actual content from DOM using `blockComponent.getContent()` instead of relying on stored `block.content`
|
||||||
|
3. Added debug logging to track cursor position and content state
|
||||||
|
- **Result**: Backspace now correctly deletes individual characters instead of the whole block
|
||||||
|
|
||||||
## Completed Phases
|
## Completed Phases
|
||||||
|
|
||||||
### Phase 1: Infrastructure ✅
|
### Phase 1: Infrastructure ✅
|
||||||
|
@ -25,7 +25,7 @@ export class CodeBlockHandler extends BaseBlockHandler {
|
|||||||
data-block-id="${block.id}"
|
data-block-id="${block.id}"
|
||||||
data-block-type="${block.type}"
|
data-block-type="${block.type}"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
>${block.content || ''}</div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ export class HeadingBlockHandler extends BaseBlockHandler {
|
|||||||
data-placeholder="${placeholder}"
|
data-placeholder="${placeholder}"
|
||||||
data-block-id="${block.id}"
|
data-block-id="${block.id}"
|
||||||
data-block-type="${block.type}"
|
data-block-type="${block.type}"
|
||||||
>${block.content || ''}</div>
|
></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ export class ParagraphBlockHandler extends BaseBlockHandler {
|
|||||||
data-placeholder="${placeholder}"
|
data-placeholder="${placeholder}"
|
||||||
data-block-id="${block.id}"
|
data-block-id="${block.id}"
|
||||||
data-block-type="${block.type}"
|
data-block-type="${block.type}"
|
||||||
>${block.content || ''}</div>
|
></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ export class QuoteBlockHandler extends BaseBlockHandler {
|
|||||||
data-placeholder="${placeholder}"
|
data-placeholder="${placeholder}"
|
||||||
data-block-id="${block.id}"
|
data-block-id="${block.id}"
|
||||||
data-block-type="${block.type}"
|
data-block-type="${block.type}"
|
||||||
>${block.content || ''}</div>
|
></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1196,6 +1196,12 @@ export class DeesWysiwygBlock extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public focusWithCursor(position: 'start' | 'end' | number = 'end'): void {
|
public focusWithCursor(position: 'start' | 'end' | number = 'end'): void {
|
||||||
|
console.log('focusWithCursor called', {
|
||||||
|
blockId: this.block?.id,
|
||||||
|
blockType: this.block?.type,
|
||||||
|
position
|
||||||
|
});
|
||||||
|
|
||||||
// Check if we have a registered handler for this block type
|
// Check if we have a registered handler for this block type
|
||||||
const handler = BlockRegistry.getHandler(this.block.type);
|
const handler = BlockRegistry.getHandler(this.block.type);
|
||||||
if (handler && handler.focusWithCursor) {
|
if (handler && handler.focusWithCursor) {
|
||||||
@ -1389,9 +1395,10 @@ export class DeesWysiwygBlock extends DeesElement {
|
|||||||
return handler.setCursorToStart(container, context);
|
return handler.setCursorToStart(container, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always find the element fresh, don't rely on cached blockElement
|
||||||
const editableElement = this.block?.type === 'code'
|
const editableElement = this.block?.type === 'code'
|
||||||
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
|
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
|
||||||
: this.blockElement;
|
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
|
||||||
if (editableElement) {
|
if (editableElement) {
|
||||||
WysiwygBlocks.setCursorToStart(editableElement);
|
WysiwygBlocks.setCursorToStart(editableElement);
|
||||||
}
|
}
|
||||||
@ -1406,9 +1413,10 @@ export class DeesWysiwygBlock extends DeesElement {
|
|||||||
return handler.setCursorToEnd(container, context);
|
return handler.setCursorToEnd(container, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always find the element fresh, don't rely on cached blockElement
|
||||||
const editableElement = this.block?.type === 'code'
|
const editableElement = this.block?.type === 'code'
|
||||||
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
|
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
|
||||||
: this.blockElement;
|
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
|
||||||
if (editableElement) {
|
if (editableElement) {
|
||||||
WysiwygBlocks.setCursorToEnd(editableElement);
|
WysiwygBlocks.setCursorToEnd(editableElement);
|
||||||
}
|
}
|
||||||
|
@ -306,6 +306,19 @@ export class WysiwygKeyboardHandler {
|
|||||||
|
|
||||||
const cursorPos = WysiwygSelection.getCursorPositionInElement(target, ...shadowRoots);
|
const cursorPos = WysiwygSelection.getCursorPositionInElement(target, ...shadowRoots);
|
||||||
|
|
||||||
|
const actualContent = blockComponent.getContent ? blockComponent.getContent() : target.textContent;
|
||||||
|
|
||||||
|
console.log('Backspace handler cursor position:', {
|
||||||
|
blockId: block.id,
|
||||||
|
storedBlockContent: block.content,
|
||||||
|
actualDOMContent: actualContent,
|
||||||
|
targetTextContent: target.textContent,
|
||||||
|
cursorPos,
|
||||||
|
isAtBeginning: cursorPos === 0,
|
||||||
|
isStoredEmpty: block.content === '',
|
||||||
|
isActuallyEmpty: actualContent === '' || actualContent.trim() === ''
|
||||||
|
});
|
||||||
|
|
||||||
// Check if cursor is at the beginning of the block
|
// Check if cursor is at the beginning of the block
|
||||||
if (cursorPos === 0) {
|
if (cursorPos === 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -335,7 +348,8 @@ export class WysiwygKeyboardHandler {
|
|||||||
|
|
||||||
if (block.type === 'code' && prevBlock.type !== 'code') {
|
if (block.type === 'code' && prevBlock.type !== 'code') {
|
||||||
// Can't merge code into non-code block
|
// Can't merge code into non-code block
|
||||||
if (block.content === '') {
|
const actualContent = blockComponent.getContent ? blockComponent.getContent() : block.content;
|
||||||
|
if (actualContent === '' || actualContent.trim() === '') {
|
||||||
blockOps.removeBlock(block.id);
|
blockOps.removeBlock(block.id);
|
||||||
await blockOps.focusBlock(prevBlock.id, 'end');
|
await blockOps.focusBlock(prevBlock.id, 'end');
|
||||||
}
|
}
|
||||||
@ -376,16 +390,21 @@ export class WysiwygKeyboardHandler {
|
|||||||
// Focus previous block at merge point
|
// Focus previous block at merge point
|
||||||
await blockOps.focusBlock(prevBlock.id, mergePoint);
|
await blockOps.focusBlock(prevBlock.id, mergePoint);
|
||||||
}
|
}
|
||||||
} else if (block.content === '' && this.component.blocks.length > 1) {
|
} else if (this.component.blocks.length > 1) {
|
||||||
// Empty block - just remove it
|
// Check if block is actually empty by getting current content from DOM
|
||||||
e.preventDefault();
|
const currentContent = blockComponent.getContent ? blockComponent.getContent() : block.content;
|
||||||
const prevBlock = blockOps.getPreviousBlock(block.id);
|
|
||||||
|
|
||||||
if (prevBlock) {
|
if (currentContent === '' || currentContent.trim() === '') {
|
||||||
blockOps.removeBlock(block.id);
|
// Empty block - just remove it
|
||||||
|
e.preventDefault();
|
||||||
|
const prevBlock = blockOps.getPreviousBlock(block.id);
|
||||||
|
|
||||||
if (prevBlock.type !== 'divider') {
|
if (prevBlock) {
|
||||||
await blockOps.focusBlock(prevBlock.id, 'end');
|
blockOps.removeBlock(block.id);
|
||||||
|
|
||||||
|
if (prevBlock.type !== 'divider') {
|
||||||
|
await blockOps.focusBlock(prevBlock.id, 'end');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -655,7 +674,14 @@ export class WysiwygKeyboardHandler {
|
|||||||
if (prevBlock) {
|
if (prevBlock) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
|
const nonEditableTypes = ['divider', 'image', 'youtube', 'markdown', 'html', 'attachment'];
|
||||||
await blockOps.focusBlock(prevBlock.id, nonEditableTypes.includes(prevBlock.type) ? undefined : 'end');
|
const position = nonEditableTypes.includes(prevBlock.type) ? undefined : 'end';
|
||||||
|
console.log('ArrowLeft: Navigating to previous block', {
|
||||||
|
currentBlockId: block.id,
|
||||||
|
prevBlockId: prevBlock.id,
|
||||||
|
prevBlockType: prevBlock.type,
|
||||||
|
focusPosition: position
|
||||||
|
});
|
||||||
|
await blockOps.focusBlock(prevBlock.id, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, let the browser handle normal left arrow navigation
|
// Otherwise, let the browser handle normal left arrow navigation
|
||||||
|
@ -122,9 +122,24 @@ export class WysiwygSelection {
|
|||||||
range.selectNodeContents(element);
|
range.selectNodeContents(element);
|
||||||
|
|
||||||
// Handle case where selection is in a text node that's a child of the element
|
// Handle case where selection is in a text node that's a child of the element
|
||||||
if (element.contains(selectionInfo.startContainer)) {
|
// Use our Shadow DOM-aware contains method
|
||||||
|
const isContained = this.containsAcrossShadowDOM(element, selectionInfo.startContainer);
|
||||||
|
|
||||||
|
console.log('getCursorPositionInElement debug:', {
|
||||||
|
element: element.tagName,
|
||||||
|
elementText: element.textContent,
|
||||||
|
selectionContainer: selectionInfo.startContainer,
|
||||||
|
selectionOffset: selectionInfo.startOffset,
|
||||||
|
isContained,
|
||||||
|
elementShadowRoot: element.getRootNode(),
|
||||||
|
selectionShadowRoot: selectionInfo.startContainer.getRootNode()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isContained) {
|
||||||
range.setEnd(selectionInfo.startContainer, selectionInfo.startOffset);
|
range.setEnd(selectionInfo.startContainer, selectionInfo.startOffset);
|
||||||
return range.toString().length;
|
const position = range.toString().length;
|
||||||
|
console.log('Cursor position calculated:', position);
|
||||||
|
return position;
|
||||||
} else {
|
} else {
|
||||||
// Selection might be in shadow DOM or different context
|
// Selection might be in shadow DOM or different context
|
||||||
// Try to find the equivalent position in the element
|
// Try to find the equivalent position in the element
|
||||||
@ -133,8 +148,10 @@ export class WysiwygSelection {
|
|||||||
|
|
||||||
// If the selection is at the beginning or end, handle those cases
|
// If the selection is at the beginning or end, handle those cases
|
||||||
if (selectionInfo.startOffset === 0) {
|
if (selectionInfo.startOffset === 0) {
|
||||||
|
console.log('Fallback: returning 0 (beginning)');
|
||||||
return 0;
|
return 0;
|
||||||
} else if (selectionInfo.startOffset === selectionText.length) {
|
} else if (selectionInfo.startOffset === selectionText.length) {
|
||||||
|
console.log('Fallback: returning text length:', text.length);
|
||||||
return text.length;
|
return text.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user