update: card cursor select

This commit is contained in:
yanmao 2021-12-31 01:08:31 +08:00
parent d27bcc7722
commit acc7ae7e41
24 changed files with 208 additions and 87 deletions

View File

@ -98,17 +98,7 @@ export default () => {
readonly={isReadonly}
onLoad={setEngine}
toc={true}
ot={{
url: `${wsUrl}${member ? '?uid=' + member.id : ''}`,
docId: 'demo',
onReady: (member) => {
if (member)
localStorage.setItem(
'member',
JSON.stringify(member),
);
},
}}
ot={false}
onSave={onSave}
/>
</Context.Provider>

View File

@ -422,7 +422,7 @@ class CardModel implements CardModelInterface {
}
}
select(card: CardInterface, event?: MouseEvent) {
select(card: CardInterface, event?: MouseEvent | KeyboardEvent) {
const editor = this.editor;
if (!isEngine(editor)) return;
if (
@ -433,8 +433,12 @@ class CardModel implements CardModelInterface {
if (
(range.startNode.closest(EDITABLE_SELECTOR).length > 0 &&
(!event ||
!event?.target ||
!this.closest(event.target as Node, false))) ||
(event instanceof MouseEvent &&
(!event.target ||
!this.closest(
event.target as Node,
false,
))))) ||
card.isEditable ||
card.isMaximize
)

View File

@ -11,37 +11,44 @@ class Down {
inline(component: CardInterface, event: KeyboardEvent) {
const { change, card } = this.engine;
const range = change.range.get();
event.preventDefault();
card.focusNextBlock(component, range, false);
change.range.select(range);
return false;
const next = component.root.next();
if (next) {
event.preventDefault();
card.focusNextBlock(component, range, false);
change.range.select(range);
return false;
}
return;
}
block(component: CardInterface, event: KeyboardEvent) {
const { change, card } = this.engine;
const range = change.range.get();
event.preventDefault();
card.focusNextBlock(component, range, false);
change.range.select(range);
return false;
const next = component.root.next();
if (next) {
event.preventDefault();
card.focusNextBlock(component, range, false);
change.range.select(range);
return false;
}
return;
}
trigger(event: KeyboardEvent) {
const { change, block, card } = this.engine;
const range = change.range.get();
const singleCard = card.getSingleCard(range);
if (!singleCard) {
if (range.collapsed) {
const closetBlock = block.closest(range.startNode);
const next = closetBlock.next();
if (next?.isCard()) {
const cardComponent = card.find(next);
if (cardComponent && cardComponent.onSelectDown) {
return cardComponent.onSelectDown(event);
}
if (range.collapsed) {
const closetBlock = block.closest(range.startNode);
const next = closetBlock.next();
if (next?.isCard()) {
const cardComponent = card.find(next);
if (cardComponent && cardComponent.onSelectDown) {
return cardComponent.onSelectDown(event);
}
}
}
if (!singleCard) {
return true;
}
if (isHotkey('shift+down', event)) {

View File

@ -37,6 +37,8 @@ class Left {
event.preventDefault();
if (isCenter) {
card.select(false);
card.toolbarModel?.hide();
card.activate(false);
} else if (range.collapsed) {
const cardComponent = this.engine.card.find(range.startNode);
if (cardComponent && cardComponent.onSelectLeft) {
@ -44,9 +46,11 @@ class Left {
}
}
if (!isCenter && singleSelectable !== false) {
this.engine.card.select(card);
this.engine.card.select(card, event);
} else {
card.focus(range, true);
card.select(false);
card.toolbarModel?.hide();
change.range.select(range);
}
return false;
@ -60,10 +64,14 @@ class Left {
// 左侧光标
const cardLeft = range.commonAncestorNode.closest(CARD_LEFT_SELECTOR);
if (cardLeft.length > 0) {
event.preventDefault();
card.focusPrevBlock(component, range, false);
change.range.select(range);
return false;
const prev = component.root.prev();
if (prev) {
event.preventDefault();
card.focusPrevBlock(component, range, false);
change.range.select(range);
return false;
}
return;
}
// 右侧光标
const cardRight = range.commonAncestorNode.closest(CARD_RIGHT_SELECTOR);
@ -75,12 +83,14 @@ class Left {
}
}
event.preventDefault();
card.select(component);
card.select(component, event);
return false;
}
if (card.getSingleSelectedCard(range)) {
event.preventDefault();
component.focus(range, true);
component.select(false);
component.toolbarModel?.hide();
change.range.select(range);
return false;
}

View File

@ -21,6 +21,8 @@ class Right {
event.preventDefault();
if (isCenter) {
card.select(false);
card.select(false);
card.toolbarModel?.hide();
} else if (range.collapsed) {
const cardComponent = this.engine.card.find(range.startNode);
if (cardComponent && cardComponent.onSelectRight) {
@ -28,9 +30,11 @@ class Right {
}
}
if (!isCenter && singleSelectable !== false) {
this.engine.card.select(card);
this.engine.card.select(card, event);
} else {
card.focus(range, false);
card.select(false);
card.toolbarModel?.hide();
change.range.select(range);
}
return false;
@ -63,20 +67,26 @@ class Right {
}
}
event.preventDefault();
card.select(component);
card.select(component, event);
return false;
}
// 右侧光标
const cardRight = range.commonAncestorNode.closest(CARD_RIGHT_SELECTOR);
if (cardRight.length > 0) {
event.preventDefault();
card.focusNextBlock(component, range, false);
change.range.select(range);
return false;
const next = component.root.next();
if (next) {
event.preventDefault();
card.focusNextBlock(component, range, false);
change.range.select(range);
return false;
}
return;
}
if (this.engine.card.getSingleSelectedCard(range)) {
event.preventDefault();
component.focus(range, false);
component.select(false);
component.toolbarModel?.hide();
change.range.select(range);
return false;
}

View File

@ -11,36 +11,44 @@ class Up {
inline(component: CardInterface, event: KeyboardEvent) {
const { change, card } = this.engine;
const range = change.range.get();
event.preventDefault();
card.focusPrevBlock(component, range, false);
change.range.select(range);
return false;
const prev = component.root.prev();
if (prev) {
event.preventDefault();
card.focusPrevBlock(component, range, false);
change.range.select(range);
return false;
}
return;
}
block(component: CardInterface, event: KeyboardEvent) {
const { change, card } = this.engine;
const range = change.range.get();
event.preventDefault();
card.focusPrevBlock(component, range, false);
change.range.select(range);
return false;
const prev = component.root.prev();
if (prev) {
event.preventDefault();
card.focusPrevBlock(component, range, false);
change.range.select(range);
return false;
}
return;
}
trigger(event: KeyboardEvent) {
const { change, card, block } = this.engine;
const range = change.range.get();
const singleCard = card.getSingleCard(range);
if (!singleCard) {
if (range.collapsed) {
const closetBlock = block.closest(range.startNode);
const prev = closetBlock.prev();
if (prev?.isCard()) {
const cardComponent = card.find(prev);
if (cardComponent && cardComponent.onSelectUp) {
return cardComponent.onSelectUp(event);
}
if (range.collapsed) {
const closetBlock = block.closest(range.startNode);
const prev = closetBlock.prev();
if (prev?.isCard()) {
const cardComponent = card.find(prev);
if (cardComponent && cardComponent.onSelectUp) {
return cardComponent.onSelectUp(event);
}
}
}
const singleCard = card.getSingleCard(range);
if (!singleCard) {
return true;
}
if (isHotkey('shift+up', event)) {

View File

@ -413,7 +413,8 @@ class ChangeModel implements ChangeInterface {
}
let nextNode = firstNode.next();
let beforeNode = firstNode;
nodeApi.insert(firstNode, range);
const newRange = nodeApi.insert(firstNode, range);
if (newRange) range = newRange;
while (nextNode && !nodeApi.isBlock(nextNode)) {
if (range.startContainer.nodeType === Node.TEXT_NODE)
range.enlargeToElementNode().collapse(false);

View File

@ -226,8 +226,9 @@ class ChangeRange implements ChangeRangeInterface {
if (node.isCustomize(endNode) && endOffset === 0) {
range.setEnd(endNode, 1);
}
const otStopped = this.engine.ot.isStopped();
// 空节点添加br
if (startNode.name === 'p') {
if (startNode.name === 'p' && !otStopped) {
if (startChildNodes.length === 0) startNode.append('<br />');
else if (
startChildNodes.length > 1 &&
@ -239,6 +240,7 @@ class ChangeRange implements ChangeRangeInterface {
}
if (
!range.collapsed &&
!otStopped &&
endNode.name === 'p' &&
endNode.children().length === 0
) {
@ -248,6 +250,7 @@ class ChangeRange implements ChangeRangeInterface {
// 列表节点没有子节点
if (
node.isList(startNode) &&
!otStopped &&
(startChildren.length === 0 || startChildren[0].nodeName === 'BR')
) {
const newNode = $('<p><br /></p>');
@ -257,7 +260,7 @@ class ChangeRange implements ChangeRangeInterface {
startNode = newNode;
}
// 空列表添加br
if (startNode.name === 'li') {
if (startNode.name === 'li' && !otStopped) {
if (startChildNodes.length === 0) {
startNode.append('<br />');
} else if (
@ -281,7 +284,7 @@ class ChangeRange implements ChangeRangeInterface {
startNode.last()?.remove();
}
}
if (!range.collapsed && endNode.name === 'li') {
if (!range.collapsed && endNode.name === 'li' && !otStopped) {
const endChildNodes = endNode.children();
if (endChildNodes.length === 0) {
endNode.append('<br />');
@ -309,6 +312,7 @@ class ChangeRange implements ChangeRangeInterface {
if (
startNode.isEditable() &&
!otStopped &&
startNode.children().length === 0 &&
!this.engine.ot.isStopped
) {

View File

@ -656,6 +656,13 @@ class NodeModel implements NodeModelInterface {
if (nodeDom.length === 0) return range;
} else range.insertNode(node);
}
if (
node.nodeType === Node.ELEMENT_NODE &&
((node as Element).hasAttribute(READY_CARD_KEY) ||
(node as Element).hasAttribute(CARD_KEY))
) {
return range.collapse(false);
}
return range
.select(
node,

View File

@ -93,6 +93,7 @@ class Position {
points[0] === rect.points[0] && points[1] === rect.points[1]
);
});
this.#container.attributes('data-placement', align);
this.#onUpdate({ ...rect, align });
}
};

View File

@ -108,7 +108,7 @@ class Range implements RangeInterface {
} else if (startNode.name === 'br') {
startNode.remove();
}
return this.base.insertNode(node);
this.base.insertNode(node);
}
isPointInRange(node: Node | NodeInterface, offset: number): boolean {

View File

@ -28,15 +28,26 @@ export default class Button implements ButtonInterface {
}
}
getPlacement() {
const dataPlacement =
this.root.closest('.data-toolbar').attributes('data-placement') ||
'top';
return dataPlacement.startsWith('top') ? 'top' : 'bottom';
}
render(container: NodeInterface) {
const { title, didMount, onClick } = this.options;
container.append(this.root);
if (title) {
this.root.on('mouseenter', () => {
const placement = this.getPlacement();
Tooltip.show(
this.root,
typeof title === 'function' ? title() : title,
{
placement,
},
);
});
this.root.on('mouseleave', () => {

View File

@ -238,6 +238,7 @@
align-items: center;
padding: 0 4px;
cursor: pointer;
width: max-content;
}
.data-toolbar-switch:hover {

View File

@ -32,6 +32,11 @@ class Toolbar implements ToolbarInterface {
this.root = $(template());
}
getPlacement() {
const dataPlacement = this.root.attributes('data-placement') || 'top';
return dataPlacement.startsWith('top') ? 'top' : 'bottom';
}
addItems(node: NodeInterface) {
this.options.items.forEach((options) => {
let item;
@ -59,9 +64,13 @@ class Toolbar implements ToolbarInterface {
const { title } = nodeOptions;
if (title) {
nodeItem.on('mouseenter', () => {
const placement = this.getPlacement();
Tooltip.show(
nodeItem,
typeof title === 'function' ? title() : title,
{
placement,
},
);
});
nodeItem.on('mouseleave', () => {

View File

@ -38,7 +38,10 @@ class Tooltip {
const left = Math.round(
window.pageXOffset + nodeRect.left + nodeWidth / 2 - width / 2,
);
const top = Math.round(window.pageYOffset + nodeRect.top - height - 2);
let top = Math.round(window.pageYOffset + nodeRect.top - height - 2);
if (options.placement === 'bottom') {
top += nodeRect.height + height + 2;
}
root.css({
left: left + 'px',
top: top + 'px',

View File

@ -521,8 +521,9 @@ export interface CardModelInterface {
/**
*
* @param card
* @param event
*/
select(card: CardInterface): void;
select(card: CardInterface, event?: MouseEvent | KeyboardEvent): void;
/**
*
* @param card

View File

@ -36,19 +36,44 @@ export default class Popup {
const range = Range.from(this.#editor)
?.cloneRange()
.shrinkToTextNode();
const selection = window.getSelection();
if (
!range ||
!selection ||
!selection.focusNode ||
range.collapsed ||
this.#editor.card.getSingleSelectedCard(range) ||
(!range.commonAncestorNode.inEditor() &&
!range.commonAncestorNode.isRoot())
) {
this.hide();
return;
}
const subRanges = range.getSubRanges();
const next = range.startNode.next();
if (
next?.isElement() &&
Math.abs(range.endOffset - range.startOffset) === 1
) {
const component = this.#editor.card.closest(next);
if (component) {
this.hide();
return;
}
}
const prev = range.startNode.prev();
if (
prev?.isElement() &&
Math.abs(range.startOffset - range.endOffset) === 1
) {
const component = this.#editor.card.closest(prev);
if (component) {
this.hide();
return;
}
}
const subRanges = range.getSubRanges(true);
const activeCard = this.#editor.card.active;
if (subRanges.length === 0 || (activeCard && !activeCard.isEditable)) {
this.hide();

View File

@ -132,15 +132,14 @@ class CodeBlockEditor implements CodeBlockEditorInterface {
},
});
this.codeMirror.on('keydown', (editor, event) => {
console.log(editor.getCursor(), editor.lineCount());
const lineCount = editor.lineCount();
const { line, ch } = editor.getCursor();
const { onUpFocus, onDownFocus, onLeftFocus, onRightFocus } =
this.options;
const content = editor.getLine(line);
// 在最后一行
if (line === lineCount - 1) {
const content = editor.getLine(line);
if (ch !== content.length) return;
if (line === lineCount - 1 && ch === content.length) {
// 按下下键
if (isHotkey('down', event) || isHotkey('ctrl+n', event)) {
if (onDownFocus) onDownFocus(event);

View File

@ -94,9 +94,13 @@ class CodeBlcok<V extends CodeBlockValue = CodeBlockValue> extends Card<V> {
const cardComponent = prev ? card.find(prev) : undefined;
if (cardComponent?.onSelectUp) {
cardComponent.onSelectUp(event);
} else {
} else if (prev) {
card.focusPrevBlock(this, range, false);
change.range.select(range);
} else {
this.focus(range, true);
change.range.select(range);
return;
}
this.activate(false);
this.toolbarModel?.hide();
@ -110,9 +114,13 @@ class CodeBlcok<V extends CodeBlockValue = CodeBlockValue> extends Card<V> {
const cardComponent = next ? card.find(next) : undefined;
if (cardComponent?.onSelectDown) {
cardComponent.onSelectDown(event);
} else {
} else if (next) {
card.focusNextBlock(this, range, false);
change.range.select(range);
} else {
this.focus(range, false);
change.range.select(range);
return;
}
this.activate(false);
this.toolbarModel?.hide();

View File

@ -96,15 +96,14 @@ class CodeBlockEditor implements CodeBlockEditorInterface {
if (onFocus) onFocus();
});
this.codeMirror.on('keydown', (editor, event) => {
console.log(editor.getCursor(), editor.lineCount());
const lineCount = editor.lineCount();
const { line, ch } = editor.getCursor();
const { onUpFocus, onDownFocus, onLeftFocus, onRightFocus } =
this.options;
const content = editor.getLine(line);
// 在最后一行
if (line === lineCount - 1) {
const content = editor.getLine(line);
if (ch !== content.length) return;
if (line === lineCount - 1 && ch === content.length) {
// 按下下键
if (isHotkey('down', event) || isHotkey('ctrl+n', event)) {
if (onDownFocus) onDownFocus(event);

View File

@ -91,9 +91,13 @@ class CodeBlcok<V extends CodeBlockValue = CodeBlockValue> extends Card<V> {
const cardComponent = prev ? card.find(prev) : undefined;
if (cardComponent?.onSelectUp) {
cardComponent.onSelectUp(event);
} else {
} else if (prev) {
card.focusPrevBlock(this, range, false);
change.range.select(range);
} else {
this.focus(range, true);
change.range.select(range);
return;
}
this.activate(false);
this.toolbarModel?.hide();
@ -107,9 +111,13 @@ class CodeBlcok<V extends CodeBlockValue = CodeBlockValue> extends Card<V> {
const cardComponent = next ? card.find(next) : undefined;
if (cardComponent?.onSelectDown) {
cardComponent.onSelectDown(event);
} else {
} else if (next) {
card.focusNextBlock(this, range, false);
change.range.select(range);
} else {
this.focus(range, false);
change.range.select(range);
return;
}
this.activate(false);
this.toolbarModel?.hide();

View File

@ -4,6 +4,8 @@
line-height: 24px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.data-file .data-file-icon {
flex: auto;

View File

@ -100,11 +100,13 @@ class TableComponent<V extends TableValue = TableValue>
this.editor.event.listeners['keydown:tab'].unshift(
(event: KeyboardEvent) => {
if (!isEngine(this.editor)) return;
const { change, block, node } = this.editor;
const { change, block, node, card } = this.editor;
const range = change.range.get();
const td = range.endNode.closest('td');
if (td.length === 0) return;
const component = card.closest(td, true);
if (!component?.equal(this.root)) return;
const closestBlock = block.closest(range.endNode);
if (
td.length > 0 &&
@ -150,6 +152,8 @@ class TableComponent<V extends TableValue = TableValue>
const range = change.range.get();
const td = range.endNode.closest('td');
if (td.length === 0) return;
const component = card.closest(td, true);
if (!component?.equal(this.root)) return;
const contentElement = td.find('.table-main-content');
if (!contentElement) return;
const tdRect = contentElement
@ -216,6 +220,8 @@ class TableComponent<V extends TableValue = TableValue>
const range = change.range.get();
const td = range.endNode.closest('td');
if (td.length === 0) return;
const component = card.closest(td, true);
if (!component?.equal(this.root)) return;
const contentElement = td.find('.table-main-content');
if (!contentElement) return;
const tdRect = contentElement
@ -276,11 +282,13 @@ class TableComponent<V extends TableValue = TableValue>
// 左键选择
this.editor.on('keydown:left', () => {
if (!isEngine(this.editor)) return;
const { change } = this.editor;
const { change, card } = this.editor;
const range = change.range.get();
const td = range.endNode.closest('td');
if (td.length === 0) return;
const component = card.closest(td, true);
if (!component?.equal(this.root)) return;
const contentElement = td.find('.table-main-content');
if (!contentElement) return;
if (td.length > 0) {
@ -292,11 +300,13 @@ class TableComponent<V extends TableValue = TableValue>
// 右键选择
this.editor.on('keydown:right', () => {
if (!isEngine(this.editor)) return;
const { change } = this.editor;
const { change, card } = this.editor;
const range = change.range.get();
const td = range.endNode.closest('td');
if (td.length === 0) return;
const component = card.closest(td, true);
if (!component?.equal(this.root)) return;
const contentElement = td.find('.table-main-content');
if (!contentElement) return;
if (td.length > 0) {

View File

@ -469,12 +469,15 @@ div[data-card-key="table"].card-selected .data-table, div[data-card-key="table"]
}
.table-wrapper .table-main-content {
margin: 4px 8px;
margin: 4px 4px;
padding: 0 2px;
position: relative;
z-index: 3;
overflow: hidden;
text-overflow: ellipsis;
}
.table-wrapper .table-main-content * {
.table-wrapper .table-main-content [data-card-key] {
max-width: 100%;
}