[REF] web: scrolling: better handling of action scroll
This commit refactors the way actions restore their scrolling positions (when coming back). By default, in desktop, we want the o_content div of the action to scroll. In mobile, we want the whole action to scroll (including the ControlPanel). With this commit, the logic is handled at the concerned level: in the action hook for the desktop case, and in the action service for the mobile case. Part-of: odoo/odoo#92475
This commit is contained in:
@@ -1,12 +1,7 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { getScrollPosition, setScrollPosition } from "./scrolling";
|
||||
const { onMounted, useComponent, useEffect, useExternalListener } = owl;
|
||||
|
||||
const { useComponent, useEffect } = owl;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Action hook
|
||||
// -----------------------------------------------------------------------------
|
||||
const scrollSymbol = Symbol("scroll");
|
||||
|
||||
export class CallbackRecorder {
|
||||
@@ -66,32 +61,93 @@ export function useSetupAction(params = {}) {
|
||||
__getContext__,
|
||||
} = component.env;
|
||||
|
||||
if (__beforeLeave__ && params.beforeLeave) {
|
||||
useCallbackRecorder(__beforeLeave__, params.beforeLeave);
|
||||
const { beforeUnload, beforeLeave, getGlobalState, getLocalState, rootRef } = params;
|
||||
|
||||
if (beforeUnload) {
|
||||
useExternalListener(window, "beforeunload", beforeUnload);
|
||||
}
|
||||
if (__getGlobalState__ && params.getGlobalState) {
|
||||
useCallbackRecorder(__getGlobalState__, params.getGlobalState);
|
||||
if (__beforeLeave__ && beforeLeave) {
|
||||
useCallbackRecorder(__beforeLeave__, beforeLeave);
|
||||
}
|
||||
if (__getLocalState__) {
|
||||
useCallbackRecorder(__getLocalState__, () => {
|
||||
if (__getGlobalState__ && (getGlobalState || rootRef)) {
|
||||
useCallbackRecorder(__getGlobalState__, () => {
|
||||
const state = {};
|
||||
state[scrollSymbol] = getScrollPosition(component.env);
|
||||
if (params.getLocalState) {
|
||||
Object.assign(state, params.getLocalState());
|
||||
if (getGlobalState) {
|
||||
Object.assign(state, getGlobalState());
|
||||
}
|
||||
if (rootRef) {
|
||||
const searchPanelEl = rootRef.el.querySelector(".o_content .o_search_panel");
|
||||
if (searchPanelEl) {
|
||||
state[scrollSymbol] = {
|
||||
searchPanel: {
|
||||
left: searchPanelEl.scrollLeft,
|
||||
top: searchPanelEl.scrollTop,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
|
||||
if (rootRef) {
|
||||
onMounted(() => {
|
||||
const { globalState } = component.props;
|
||||
const scrolling = globalState && globalState[scrollSymbol];
|
||||
if (scrolling) {
|
||||
const searchPanelEl = rootRef.el.querySelector(".o_content .o_search_panel");
|
||||
if (searchPanelEl) {
|
||||
searchPanelEl.scrollLeft =
|
||||
(scrolling.searchPanel && scrolling.searchPanel.left) || 0;
|
||||
searchPanelEl.scrollTop =
|
||||
(scrolling.searchPanel && scrolling.searchPanel.top) || 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (__getLocalState__ && (getLocalState || rootRef)) {
|
||||
useCallbackRecorder(__getLocalState__, () => {
|
||||
const state = {};
|
||||
if (getLocalState) {
|
||||
Object.assign(state, getLocalState());
|
||||
}
|
||||
if (rootRef) {
|
||||
if (component.env.isSmall) {
|
||||
state[scrollSymbol] = {
|
||||
root: { left: rootRef.el.scrollLeft, top: rootRef.el.scrollTop },
|
||||
};
|
||||
} else {
|
||||
const contentEl = rootRef.el.querySelector(".o_content");
|
||||
if (contentEl) {
|
||||
state[scrollSymbol] = {
|
||||
content: { left: contentEl.scrollLeft, top: contentEl.scrollTop },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
|
||||
if (rootRef) {
|
||||
onMounted(() => {
|
||||
const { state } = component.props;
|
||||
const scrolling = state && state[scrollSymbol];
|
||||
if (scrolling) {
|
||||
if (component.env.isSmall) {
|
||||
rootRef.el.scrollTop = (scrolling.root && scrolling.root.top) || 0;
|
||||
rootRef.el.scrollLeft = (scrolling.root && scrolling.root.left) || 0;
|
||||
} else if (scrolling.content) {
|
||||
const contentEl = rootRef.el.querySelector(".o_content");
|
||||
if (contentEl) {
|
||||
contentEl.scrollTop = scrolling.content.top || 0;
|
||||
contentEl.scrollLeft = scrolling.content.left || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (__getContext__ && params.getContext) {
|
||||
useCallbackRecorder(__getContext__, params.getContext);
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (component.props.state && component.props.state[scrollSymbol]) {
|
||||
setScrollPosition(component.env, component.props.state[scrollSymbol]);
|
||||
}
|
||||
},
|
||||
() => []
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
/*
|
||||
* Returns the main scrollable element relevant in a context of an action.
|
||||
* In desktop, the relevant element is the .o_content of the action.
|
||||
* In mobile, it is the html node itself, provided no other children is scrollable
|
||||
* Most of the times in mobile, we don't want to scroll individual elements, so every bit
|
||||
* of code should enforce that html is the scrollable element.
|
||||
*
|
||||
* @param {OdooEnv} env
|
||||
* @return {Element}
|
||||
*/
|
||||
function getScrollableElement(env) {
|
||||
if (env.isSmall) {
|
||||
return document.firstElementChild; // aka html node;
|
||||
} else {
|
||||
return document.querySelector(".o_web_client > .o_action_manager > .o_action > .o_content");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current top and left scroll position. By default, the scrolling
|
||||
* area is the '.o_content' main div. In mobile, it is the body.
|
||||
*
|
||||
* @param {OdooEnv} env
|
||||
*/
|
||||
export function getScrollPosition(env) {
|
||||
const scrollingEl = getScrollableElement(env);
|
||||
return {
|
||||
left: scrollingEl ? scrollingEl.scrollLeft : 0,
|
||||
top: scrollingEl ? scrollingEl.scrollTop : 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets top and left scroll positions to the given values. By default, the
|
||||
* scrolling area is the '.o_content' main div. In mobile, it is the body.
|
||||
*
|
||||
* @param {OdooEnv} env
|
||||
* @param {Object} offset
|
||||
* @param {number} [offset.left=0]
|
||||
* @param {number} [offset.top=0]
|
||||
*/
|
||||
export function setScrollPosition(env, offset) {
|
||||
const scrollingEl = getScrollableElement(env);
|
||||
if (scrollingEl) {
|
||||
scrollingEl.scrollLeft = offset.left || 0;
|
||||
scrollingEl.scrollTop = offset.top || 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user