Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions packages/amis-editor-core/src/dnd/autoScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @file 拖拽过程中自动滚动容器
*/
import {getScrollParent} from 'amis-core';

export interface AutoScrollOptions {
/** 滚动容器,默认为 document.documentElement */
container?: HTMLElement;
}

export function getScrollableParent(
element: HTMLElement,
iframe?: HTMLElement
): HTMLElement {
if (iframe) {
// 如果是移动端编辑,它的滚动是在 iframe 外面的。
return getScrollParent(iframe) as HTMLElement;
}

const scrollableParent = getScrollParent(element);
return scrollableParent as HTMLElement;
}

export class AutoScroll {
options: AutoScrollOptions;

time: number;

constructor(options: AutoScrollOptions = {}) {
this.options = {
container: document.documentElement,
...options
};
this.time = Date.now();
}

checkScroll(e: MouseEvent | DragEvent) {
const {container} = this.options;
if (!container) return;

const rect = container.getBoundingClientRect();
const thresholdX = Math.max(30, rect.width * 0.1);
const thresholdY = Math.max(30, rect.height * 0.1);
let mouseX = e.pageX;
let mouseY = e.pageY;

// 如果拖拽发生在 iframe 中,则需要计算 iframe 的偏移
if ((e.target as HTMLElement).ownerDocument !== container.ownerDocument) {
const iframe = (e.target as HTMLElement).ownerDocument.defaultView
?.frameElement;
const iframeRect = iframe?.getBoundingClientRect();
mouseX += iframeRect?.left ?? 0;
mouseY += iframeRect?.top ?? 0;
}

if (mouseX < rect.left + thresholdX && container.scrollLeft > 0) {
// 计算距离边界的比例,越近比例越大
const distanceRatio = 1 - (mouseX - rect.left) / thresholdX;
// 根据比例计算滚动距离,最小5,最大30
const scrollAmount = Math.max(5, Math.round(30 * distanceRatio));
container.scrollLeft -= scrollAmount;
} else if (
mouseX > rect.right - thresholdX &&
container.scrollLeft < container.scrollWidth - container.clientWidth
) {
// 计算距离边界的比例,越近比例越大
const distanceRatio = 1 - (rect.right - mouseX) / thresholdX;
// 根据比例计算滚动距离,最小5,最大30
const scrollAmount = Math.max(5, Math.round(30 * distanceRatio));
container.scrollLeft += scrollAmount;
}

if (mouseY < rect.top + thresholdY && container.scrollTop > 0) {
// 计算距离边界的比例,越近比例越大
const distanceRatio = 1 - (mouseY - rect.top) / thresholdY;
// 根据比例计算滚动距离,最小5,最大30
const scrollAmount = Math.max(5, Math.round(30 * distanceRatio));
container.scrollTop -= scrollAmount;
} else if (
mouseY > rect.bottom - thresholdY &&
container.scrollTop < container.scrollHeight - container.clientHeight
) {
// 计算距离边界的比例,越近比例越大
const distanceRatio = 1 - (rect.bottom - mouseY) / thresholdY;
// 根据比例计算滚动距离,最小5,最大30
const scrollAmount = Math.max(5, Math.round(30 * distanceRatio));
container.scrollTop += scrollAmount;
}
}
}
41 changes: 31 additions & 10 deletions packages/amis-editor-core/src/dnd/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,29 @@ import {renderThumbToGhost} from '../component/factory';
import {EditorNodeType} from '../store/node';
import {translateSchema} from '../util';
import {DNDModeInterface} from './interface';
import {AutoScroll, getScrollableParent} from './autoScroll';
function getRelativeParent(element: HTMLElement): HTMLElement | null {
let parent = element;
const win = parent.ownerDocument.defaultView || window;

while (parent) {
const style = win.getComputedStyle(parent);
if (style.position === 'relative') {
return parent;
}
parent = parent.parentElement as HTMLElement;
}
return null;
}

export class DefaultDNDMode implements DNDModeInterface {
readonly dndContainer: HTMLElement; // 记录当前拖拽区域
readonly relativeContainer: HTMLElement;
dropOn?: string;
dropPosition?: 'top' | 'bottom' | 'left' | 'right' | 'center' | 'middle';

autoScroll?: AutoScroll;

constructor(readonly dnd: EditorDNDManager, readonly region: EditorNodeType) {
// 初始化时,默认将元素所在区域设置为当前拖拽区域
this.dndContainer = this.dnd.store
Expand All @@ -23,16 +40,17 @@ export class DefaultDNDMode implements DNDModeInterface {
) as HTMLElement;

// 获取相对定位的父级元素
let parent = this.dndContainer;
const win = parent.ownerDocument.defaultView || window;

while (parent) {
const style = win.getComputedStyle(parent);
if (style.position === 'relative') {
this.relativeContainer = parent;
break;
}
parent = parent.parentElement as HTMLElement;
this.relativeContainer =
getRelativeParent(this.dndContainer) || this.dndContainer;

const scrollableParent = getScrollableParent(
this.dndContainer,
this.dnd.store.getIframe()
);
if (scrollableParent) {
this.autoScroll = new AutoScroll({
container: scrollableParent
});
}
}

Expand Down Expand Up @@ -78,6 +96,8 @@ export class DefaultDNDMode implements DNDModeInterface {
const target = this.getTarget(e);
const wrapper = this.dndContainer;

this.autoScroll?.checkScroll(e);

if (target) {
const dropPosition = this.detectDropPosition(e, target);
this.updateIndicator(
Expand Down Expand Up @@ -263,6 +283,7 @@ export class DefaultDNDMode implements DNDModeInterface {
* 销毁
*/
dispose() {
delete this.autoScroll;
delete this.dropOn;
delete this.dropPosition;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/amis-editor-core/src/dnd/flex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {translateSchema} from '../util';
import {DNDModeInterface} from './interface';
import findLastIndex from 'lodash/findLastIndex';
import find from 'lodash/find';
import {AutoScroll, getScrollableParent} from './autoScroll';

const className = 'PushHighlight';

Expand All @@ -21,6 +22,7 @@ export class FlexDNDMode implements DNDModeInterface {
dragNode?: any;
dragId: string;
store: any;
autoScroll?: AutoScroll;
constructor(
readonly dnd: EditorDNDManager,
readonly region: EditorNodeType,
Expand All @@ -33,6 +35,15 @@ export class FlexDNDMode implements DNDModeInterface {
`[data-region="${region.region}"][data-region-host="${region.id}"]`
) as HTMLElement;
this.maxRolLength = config.regionNode.maxRolLength || 4;
const scrollableParent = getScrollableParent(
this.dndContainer,
this.dnd.store.getIframe()
);
if (scrollableParent) {
this.autoScroll = new AutoScroll({
container: scrollableParent
});
}
}

/**
Expand Down Expand Up @@ -85,6 +96,8 @@ export class FlexDNDMode implements DNDModeInterface {
}

over(e: DragEvent, ghost: HTMLElement) {
this.autoScroll?.checkScroll(e);

const {isMobile} = this.store;
const colTarget = (e.target as HTMLElement).closest('[role="flex-col"]');
const wrapper = this.dndContainer;
Expand Down Expand Up @@ -266,6 +279,7 @@ export class FlexDNDMode implements DNDModeInterface {
* 销毁
*/
dispose() {
delete this.autoScroll;
delete this.dropBeforeId;
delete this.position;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/amis-editor-core/src/dnd/position-h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import findIndex from 'lodash/findIndex';
import {DNDModeInterface} from './interface';
import {EditorNodeType} from '../store/node';
import {EditorDNDManager} from './index';
import {AutoScroll, getScrollableParent} from './autoScroll';

export class PositionHDNDMode implements DNDModeInterface {
readonly dndContainer: HTMLElement; // 记录当前拖拽区域
dropBeforeId?: string;
autoScroll?: AutoScroll;

constructor(readonly dnd: EditorDNDManager, readonly region: EditorNodeType) {
// 初始化时,默认将元素所在区域设置为当前拖拽区域
Expand All @@ -18,6 +20,15 @@ export class PositionHDNDMode implements DNDModeInterface {
.querySelector(
`[data-region="${region.region}"][data-region-host="${region.id}"]`
) as HTMLElement;
const scrollableParent = getScrollableParent(
this.dndContainer,
this.dnd.store.getIframe()
);
if (scrollableParent) {
this.autoScroll = new AutoScroll({
container: scrollableParent
});
}
}

enter(e: DragEvent, ghost: HTMLElement) {
Expand Down Expand Up @@ -60,6 +71,8 @@ export class PositionHDNDMode implements DNDModeInterface {
}

over(e: DragEvent, ghost: HTMLElement) {
this.autoScroll?.checkScroll(e);

let target = this.getTarget(e);
if (!target) {
return;
Expand Down Expand Up @@ -135,5 +148,6 @@ export class PositionHDNDMode implements DNDModeInterface {
*/
dispose() {
delete this.dropBeforeId;
delete this.autoScroll;
}
}
Loading