Skip to content

Commit

Permalink
feat(edgeless): support copy by option + drag (toeverything#6166)
Browse files Browse the repository at this point in the history
  • Loading branch information
AyushAgrawal-A2 authored Jan 31, 2024
1 parent eb89aea commit 1554e65
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 36 deletions.
59 changes: 44 additions & 15 deletions packages/blocks/src/note-block/note-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
AFFINE_DRAG_HANDLE_WIDGET,
AffineDragHandleWidget,
} from '../page-block/widgets/drag-handle/drag-handle.js';
import { captureEventTarget } from '../page-block/widgets/drag-handle/utils.js';
import {
captureEventTarget,
getDuplicateBlocks,
} from '../page-block/widgets/drag-handle/utils.js';
import { KeymapController } from './keymap-controller.js';
import { type NoteBlockModel, NoteBlockSchema } from './note-model.js';

Expand All @@ -31,11 +34,15 @@ export class NoteBlockComponent extends BlockElement<NoteBlockModel> {
flavour: NoteBlockSchema.model.flavour,
edgeless: true,
onDragStart: ({ state, startDragging, anchorBlockPath }) => {
if (!anchorBlockPath) return false;
if (!anchorBlockPath) {
return false;
}

const element = captureEventTarget(state.raw.target);
const insideDragHandle = !!element?.closest(AFFINE_DRAG_HANDLE_WIDGET);
if (!insideDragHandle) return false;
if (!insideDragHandle) {
return false;
}

const anchorComponent = this.std.view.viewFromPath(
'block',
Expand All @@ -44,32 +51,37 @@ export class NoteBlockComponent extends BlockElement<NoteBlockModel> {
if (
!anchorComponent ||
!matchFlavours(anchorComponent.model, [NoteBlockSchema.model.flavour])
)
) {
return false;

}
const noteComponent = anchorComponent as NoteBlockComponent;

const notePortal = noteComponent.closest('.edgeless-block-portal-note');
assertExists(notePortal);

const dragPreviewEl = notePortal.cloneNode() as HTMLElement;
dragPreviewEl.style.transform = '';
dragPreviewEl.style.left = '0';
dragPreviewEl.style.top = '0';

const noteBackground = notePortal.querySelector('.note-background');
assertExists(noteBackground);

const noteBackgroundClone = noteBackground.cloneNode();
dragPreviewEl.appendChild(noteBackgroundClone);

const container = document.createElement('div');
container.style.width = '100%';
container.style.height = '100%';
container.style.overflow = 'hidden';
dragPreviewEl.appendChild(container);

render(noteComponent.host.renderModel(noteComponent.model), container);

startDragging([noteComponent], state, dragPreviewEl);
return true;
},
onDragEnd: ({ draggingElements, dropBlockId, dropType }) => {
onDragEnd: ({ draggingElements, dropBlockId, dropType, state }) => {
if (
draggingElements.length !== 1 ||
!matchFlavours(draggingElements[0].model, [
Expand All @@ -78,20 +90,37 @@ export class NoteBlockComponent extends BlockElement<NoteBlockModel> {
) {
return false;
}
if (dropType === 'in') return true;

if (dropType === 'in') {
return true;
}

const noteBlock = draggingElements[0].model as NoteBlockModel;
const targetBlock = this.page.getBlockById(dropBlockId);
const parentBlock = this.page.getParent(dropBlockId);
if (!targetBlock || !parentBlock) return true;
if (!targetBlock || !parentBlock) {
return true;
}

this.page.moveBlocks(
noteBlock.children,
parentBlock,
targetBlock,
dropType === 'before'
);
this.page.deleteBlock(noteBlock);
const altKey = state.raw.altKey;
if (altKey) {
const duplicateBlocks = getDuplicateBlocks(noteBlock.children);

const parentIndex =
parentBlock.children.indexOf(targetBlock) +
(dropType === 'after' ? 1 : 0);

this.page.addBlocks(duplicateBlocks, parentBlock, parentIndex);
} else {
this.page.moveBlocks(
noteBlock.children,
parentBlock,
targetBlock,
dropType === 'before'
);

this.page.deleteBlock(noteBlock);
}

return true;
},
Expand Down
96 changes: 80 additions & 16 deletions packages/blocks/src/page-block/widgets/drag-handle/drag-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
getClosestNoteBlock,
getDragHandleContainerHeight,
getDragHandleLeftPadding,
getDuplicateBlocks,
getNoteId,
includeTextSelection,
insideDatabaseTable,
Expand Down Expand Up @@ -366,12 +367,15 @@ export class AffineDragHandleWidget extends WidgetElement<
const offset = this._calculatePreviewOffset(blockElements, state);
const posX = state.raw.x - offset.x;
const posY = state.raw.y - offset.y;
const altKey = state.raw.altKey;

dragPreview = new DragPreview(offset);
dragPreview.style.width = `${width / this.scale}px`;
dragPreview.style.transform = `translate(${posX}px, ${posY}px) scale(${
this.scale * this.noteScale
})`;

dragPreview.style.opacity = altKey ? '1' : '0.5';
dragPreview.appendChild(fragment);
}
this.pageBlockElement.appendChild(dragPreview);
Expand Down Expand Up @@ -405,6 +409,9 @@ export class AffineDragHandleWidget extends WidgetElement<
this.dragPreview.style.transform = `translate(${posX}px, ${posY}px) scale(${
this.scale * this.noteScale
})`;

const altKey = state.raw.altKey;
this.dragPreview.style.opacity = altKey ? '1' : '0.5';
};

private _updateDragPreviewOnViewportUpdate = () => {
Expand Down Expand Up @@ -434,11 +441,16 @@ export class AffineDragHandleWidget extends WidgetElement<
dragPreviewEl?: HTMLElement,
dragPreviewOffset?: Point
) => {
if (!blockElements.length) return;
if (!blockElements.length) {
return;
}

this.draggingElements = blockElements;

if (this.dragPreview) this._removeDragPreview();
if (this.dragPreview) {
this._removeDragPreview();
}

this.dragPreview = this._createDragPreview(
blockElements,
state,
Expand All @@ -462,10 +474,13 @@ export class AffineDragHandleWidget extends WidgetElement<
this._anchorBlockId = '';
this._anchorBlockPath = null;

if (this._dragHandleContainer)
if (this._dragHandleContainer) {
this._dragHandleContainer.style.display = 'none';
}

if (force) this._reset();
if (force) {
this._reset();
}
};

private _reset = () => {
Expand Down Expand Up @@ -1143,7 +1158,14 @@ export class AffineDragHandleWidget extends WidgetElement<
},
});

this.page.moveBlocks(selectedBlocks, newNoteBlock);
const altKey = state.raw.altKey;
if (altKey) {
const duplicateBlocks = getDuplicateBlocks(selectedBlocks);

this.page.addBlocks(duplicateBlocks, newNoteBlock);
} else {
this.page.moveBlocks(selectedBlocks, newNoteBlock);
}

return true;
}
Expand All @@ -1154,13 +1176,16 @@ export class AffineDragHandleWidget extends WidgetElement<
this.selectedBlocks.map(selection => selection.blockId),
targetBlockId
)
)
) {
return false;
}

const selectedBlocks = getBlockElementsExcludeSubtrees(draggingElements)
.map(element => getModelByBlockComponent(element))
.filter((x): x is BlockModel => !!x);
if (!selectedBlocks.length) return false;
if (!selectedBlocks.length) {
return false;
}

const targetBlock = this.page.getBlockById(targetBlockId);
assertExists(targetBlock);
Expand All @@ -1172,15 +1197,32 @@ export class AffineDragHandleWidget extends WidgetElement<
: this.page.getParent(targetBlockId);
assertExists(parent);

const altKey = state.raw.altKey;

if (shouldInsertIn) {
this.page.moveBlocks(selectedBlocks, targetBlock);
if (altKey) {
const duplicateBlocks = getDuplicateBlocks(selectedBlocks);

this.page.addBlocks(duplicateBlocks, targetBlock);
} else {
this.page.moveBlocks(selectedBlocks, targetBlock);
}
} else {
this.page.moveBlocks(
selectedBlocks,
parent,
targetBlock,
dropType === 'before'
);
if (altKey) {
const duplicateBlocks = getDuplicateBlocks(selectedBlocks);

const parentIndex =
parent.children.indexOf(targetBlock) + (dropType === 'after' ? 1 : 0);

this.page.addBlocks(duplicateBlocks, parent, parentIndex);
} else {
this.page.moveBlocks(
selectedBlocks,
parent,
targetBlock,
dropType === 'before'
);
}
}

// TODO: need a better way to update selection
Expand Down Expand Up @@ -1216,7 +1258,9 @@ export class AffineDragHandleWidget extends WidgetElement<
const state = ctx.get('pointerState');
// If not click left button to start dragging, should do nothing
const { button } = state.raw;
if (button !== 0) return false;
if (button !== 0) {
return false;
}

// call default drag start handler if no option return true
for (const option of this.optionRunner.options) {
Expand Down Expand Up @@ -1298,7 +1342,11 @@ export class AffineDragHandleWidget extends WidgetElement<

// call default drag end handler if no option return true
this._onDragEnd(state);
if (isInsideEdgelessEditor(this.host)) this._checkTopLevelBlockSelection();

if (isInsideEdgelessEditor(this.host)) {
this._checkTopLevelBlockSelection();
}

return true;
};

Expand All @@ -1322,6 +1370,20 @@ export class AffineDragHandleWidget extends WidgetElement<
if (outOfPageViewPort && !inDragHandle && !inPage) this._hide();
};

private _keyboardHandler: UIEventHandler = ctx => {
if (!this.dragging || !this.dragPreview) {
return;
}

const state = ctx.get('defaultState');
const event = state.event as KeyboardEvent;
event.preventDefault();
event.stopPropagation();

const altKey = event.key === 'Alt' && event.altKey;
this.dragPreview.style.opacity = altKey ? '1' : '0.5';
};

private _onDragHandlePointerEnter = () => {
const container = this._dragHandleContainer;
const grabber = this._dragHandleGrabber;
Expand Down Expand Up @@ -1529,6 +1591,8 @@ export class AffineDragHandleWidget extends WidgetElement<
this.handleEvent('dragEnd', this._dragEndHandler);
this.handleEvent('pointerOut', this._pointerOutHandler);
this.handleEvent('beforeInput', () => this._hide());
this.handleEvent('keyDown', this._keyboardHandler, { global: true });
this.handleEvent('keyUp', this._keyboardHandler, { global: true });
}

override disconnectedCallback() {
Expand Down
Loading

0 comments on commit 1554e65

Please sign in to comment.