Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(edit-page-v2): Add scroll while dragging content inside editor #28419

Merged
merged 9 commits into from
May 8, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,7 @@ export class EditEmaStore extends ComponentStore<EditEmaState> {

private readonly editor$ = this.select((state) => state.editor);
private readonly isEnterpriseLicense$ = this.select((state) => state.isEnterpriseLicense);
private readonly currentState$ = this.select(
(state) => state.editorState ?? EDITOR_STATE.LOADING
);
readonly currentState$ = this.select((state) => state.editorState ?? EDITOR_STATE.LOADING);
private readonly currentExperiment$ = this.select((state) => state.currentExperiment);
private readonly templateThemeId$ = this.select((state) => state.editor.template.themeId);
private readonly templateIdentifier$ = this.select((state) => state.editor.template.identifier);
Expand Down Expand Up @@ -693,6 +691,20 @@ export class EditEmaStore extends ComponentStore<EditEmaState> {
};
});

/**
* Updates the editor scroll state in the dot-ema store.
* If a drag item is present, we assume that scrolling was done during a drag and drop, and the state will automatically change to dragging.
* if there is no dragItem, we change the state to IDLE
*
* @returns The updated dot-ema store state.
*/
readonly updateEditorScrollState = this.updater((state) => {
return {
...state,
editorState: state.dragItem ? EDITOR_STATE.SCROLLING : EDITOR_STATE.IDLE
};
});

readonly setDevice = this.updater((state, device: DotDevice) => {
return {
...state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@
data-testId="contentlet-tools" />
<dot-ema-page-dropzone
*ngIf="
es.editorData.canEditVariant &&
!!es.bounds.length &&
!es.editorData.device &&
es.state === editorState.DRAGGING
(es.editorData.canEditVariant &&
!!es.bounds.length &&
!es.editorData.device &&
es.state === editorState.DRAGGING) ||
es.state === editorState.SCROLLING
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
"
[containers]="es.bounds"
[containers]="es.state === editorState.SCROLLING ? [] : es.bounds"
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
[dragItem]="es.dragItem"
data-testId="dropzone" />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@ import { ConfirmationService, MessageService } from 'primeng/api';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { ProgressBarModule } from 'primeng/progressbar';

import { takeUntil, catchError, filter, map, switchMap, tap, take } from 'rxjs/operators';
import {
takeUntil,
catchError,
filter,
map,
switchMap,
tap,
take,
startWith,
pairwise
} from 'rxjs/operators';

import { CUSTOMER_ACTIONS } from '@dotcms/client';
import {
Expand Down Expand Up @@ -304,6 +314,15 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
}

handleDragEvents() {
this.store.currentState$.pipe(takeUntil(this.destroy$)).subscribe((state) => {
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
if (state === EDITOR_STATE.DRAGGING) {
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
this.iframe.nativeElement.contentWindow?.postMessage(
NOTIFY_CUSTOMER.EMA_REQUEST_BOUNDS,
this.host
);
}
});
zJaaal marked this conversation as resolved.
Show resolved Hide resolved

fromEvent(this.window, 'dragstart')
.pipe(takeUntil(this.destroy$))
.subscribe((event: DragEvent) => {
Expand Down Expand Up @@ -340,11 +359,6 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
} as ContentletDragPayload
});
}

this.iframe.nativeElement.contentWindow?.postMessage(
NOTIFY_CUSTOMER.EMA_REQUEST_BOUNDS,
this.host
);
});

fromEvent(this.window, 'dragenter')
Expand Down Expand Up @@ -406,6 +420,42 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.destroy$))
.subscribe((event: DragEvent) => {
event.preventDefault(); // Prevent file opening

const iframeRect = this.iframe.nativeElement.getBoundingClientRect();

const isInsideIframe =
event.clientX > iframeRect.left && event.clientX < iframeRect.right;

let direction;

KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
if (
isInsideIframe &&
event.clientY > iframeRect.top &&
event.clientY < iframeRect.top + 100
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
) {
direction = 'up';
}

if (
isInsideIframe &&
event.clientY > iframeRect.bottom - 100 &&
event.clientY <= iframeRect.bottom
) {
direction = 'down';
}

if (!direction) {
this.store.updateEditorState(EDITOR_STATE.DRAGGING);

return;
}

this.store.updateEditorState(EDITOR_STATE.SCROLLING);

this.iframe.nativeElement.contentWindow?.postMessage(
{ name: 'scroll-inside-iframe', direction },
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
this.host
);
});

fromEvent(this.window, 'drop')
Expand Down Expand Up @@ -499,19 +549,31 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
handleReloadContent() {
this.store.contentState$
.pipe(
startWith(null),
takeUntil(this.destroy$),
filter(({ state }) => state === EDITOR_STATE.IDLE)
pairwise(),
filter(([_prev, curr]) => curr?.state === EDITOR_STATE.IDLE)
)
.subscribe(({ code }) => {
.subscribe((res) => {
// If we are idle then we are not dragging
this.resetDragProperties();

let [prev] = res;
prev = prev || { state: EDITOR_STATE.LOADING, code: '' };

if (prev?.state !== EDITOR_STATE.LOADING) {
/** We have some EDITOR_STATE values that we don't want to reload the content
* Only when the state is changed from LOADING to IDLE we need to reload the content
*/
return;
}

if (!this.isVTLPage()) {
// Only reload if is Headless.
// If is VTL, the content is updated by store.code$
this.reloadIframe();
} else {
this.setIframeContent(code);
this.setIframeContent(res[1].code);
}
});
}
Expand Down Expand Up @@ -924,8 +986,10 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
});
},
[CUSTOMER_ACTIONS.IFRAME_SCROLL]: () => {
this.resetDragProperties();
this.store.updateEditorState(EDITOR_STATE.IDLE);
this.store.updateEditorState(EDITOR_STATE.SCROLLING);
},
[CUSTOMER_ACTIONS.IFRAME_SCROLL_END]: () => {
this.store.updateEditorScrollState();
},
[CUSTOMER_ACTIONS.PING_EDITOR]: () => {
this.iframe?.nativeElement?.contentWindow.postMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export enum EDITOR_STATE {
IDLE = 'idle',
DRAGGING = 'dragging',
ERROR = 'error',
OUT_OF_BOUNDS = 'out-of-bounds'
OUT_OF_BOUNDS = 'out-of-bounds',
SCROLLING = 'scrolling'
}

export enum EDITOR_MODE {
Expand Down
18 changes: 18 additions & 0 deletions core-web/libs/sdk/client/src/lib/editor/listeners/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ export function listenEditorMessages() {
break;
}
}

if (event.data.name === 'scroll-inside-iframe') {
const scrollY = event.data.direction === 'up' ? -120 : 120;
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
window.scrollBy({ left: 0, top: scrollY, behavior: 'smooth' });
}
};

window.addEventListener('message', messageCallback);
Expand Down Expand Up @@ -175,7 +180,20 @@ export function scrollHandler() {
window.lastScrollYPosition = window.scrollY;
};

const scrollEndCallback = () => {
postMessageToEditor({
action: CUSTOMER_ACTIONS.IFRAME_SCROLL_END
});
};

window.addEventListener('scroll', scrollCallback);
window.addEventListener('scrollend', scrollEndCallback);

subscriptions.push({
type: 'listener',
event: 'scroll',
callback: scrollEndCallback
});

subscriptions.push({
type: 'listener',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export enum CUSTOMER_ACTIONS {
* Tell the editor that the page is being scrolled
*/
IFRAME_SCROLL = 'scroll',
/**
* Tell the editor that the page has stopped scrolling
*/
IFRAME_SCROLL_END = 'scroll-end',
/**
* Ping the editor to see if the page is inside the editor
*/
Expand Down
17 changes: 17 additions & 0 deletions dotCMS/src/main/webapp/html/js/editor-js/sdk-editor.esm.js
KevinDavilaDotCMS marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
/**
* Tell the editor that the page is being scrolled
*/ CUSTOMER_ACTIONS["IFRAME_SCROLL"] = "scroll";
/**
* Tell the editor that the page has stopped scrolling
*/ CUSTOMER_ACTIONS["IFRAME_SCROLL_END"] = "scroll-end";
/**
* Ping the editor to see if the page is inside the editor
*/ CUSTOMER_ACTIONS["PING_EDITOR"] = "ping-editor";
Expand Down Expand Up @@ -226,6 +229,14 @@ function findVTLData(target) {
break;
}
}
if (event.data.name === "scroll-inside-iframe") {
var scrollY = event.data.direction === "up" ? -120 : 120;
window.scrollBy({
left: 0,
top: scrollY,
behavior: "smooth"
});
}
};
window.addEventListener("message", messageCallback);
}
Expand Down Expand Up @@ -293,7 +304,13 @@ function findVTLData(target) {
});
window.lastScrollYPosition = window.scrollY;
};
var scrollEndCallback = function() {
postMessageToEditor({
action: CUSTOMER_ACTIONS.IFRAME_SCROLL_END
});
};
window.addEventListener("scroll", scrollCallback);
window.addEventListener("scrollend", scrollEndCallback);
}
/**
* Restores the scroll position of the window when an iframe is loaded.
Expand Down
Loading