diff --git a/client/src/helpers/record/misc/sticky-bar.js b/client/src/helpers/record/misc/sticky-bar.js
new file mode 100644
index 0000000000..fab6320b1e
--- /dev/null
+++ b/client/src/helpers/record/misc/sticky-bar.js
@@ -0,0 +1,149 @@
+/************************************************************************
+ * This file is part of EspoCRM.
+ *
+ * EspoCRM – Open Source CRM application.
+ * Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
+ * Website: https://www.espocrm.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * The interactive user interfaces in modified source and object code versions
+ * of this program must display Appropriate Legal Notices, as required under
+ * Section 5 of the GNU Affero General Public License version 3.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
+ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
+ ************************************************************************/
+
+import $ from 'jquery';
+
+/**
+ * @internal
+ */
+class StickyBarHelper {
+
+
+ /**
+ * @param {import('views/record/detail').default} view
+ * @param {boolean} stickButtonsFormBottomSelector
+ * @param {boolean} stickButtonsContainerAllTheWay
+ * @param {number} numId
+ */
+ constructor(view, stickButtonsFormBottomSelector, stickButtonsContainerAllTheWay, numId) {
+ this.view = view;
+ this.stickButtonsFormBottomSelector = stickButtonsFormBottomSelector;
+ this.stickButtonsContainerAllTheWay = stickButtonsContainerAllTheWay;
+ this.numId = numId;
+
+ this.themeManager = view.getThemeManager();
+ this.$el = view.$el;
+ }
+
+ init() {
+ const $containers = this.$el.find('.detail-button-container');
+ const $container = this.$el.find('.detail-button-container.record-buttons');
+
+ if (!$container.length) {
+ return;
+ }
+
+ const navbarHeight = this.themeManager.getParam('navbarHeight');
+ const screenWidthXs = this.themeManager.getParam('screenWidthXs');
+
+ const isSmallScreen = $(window.document).width() < screenWidthXs;
+
+ const getOffsetTop = (/** JQuery */$element) => {
+ let element = /** @type {HTMLElement} */$element.get(0);
+
+ let value = 0;
+
+ while (element) {
+ value += !isNaN(element.offsetTop) ? element.offsetTop : 0;
+
+ element = element.offsetParent;
+ }
+
+ if (isSmallScreen) {
+ return value;
+ }
+
+ return value - navbarHeight;
+ };
+
+ let stickTop = getOffsetTop($container);
+ const blockHeight = $container.outerHeight();
+
+ stickTop -= 5; // padding;
+
+ const $block = $('
')
+ .css('height', blockHeight + 'px')
+ .html(' ')
+ .hide()
+ .insertAfter($container);
+
+ let $middle = this.view.getMiddleView().$el;
+ const $window = $(window);
+ const $navbarRight = $('#navbar .navbar-right');
+
+ if (this.stickButtonsFormBottomSelector) {
+ const $bottom = this.$el.find(this.stickButtonsFormBottomSelector);
+
+ if ($bottom.length) {
+ $middle = $bottom;
+ }
+ }
+
+ $window.off('scroll.detail-' + this.numId);
+
+ $window.on('scroll.detail-' + this.numId, () => {
+ const edge = $middle.position().top + $middle.outerHeight(false) - blockHeight;
+ const scrollTop = $window.scrollTop();
+
+ if (scrollTop >= edge && !this.stickButtonsContainerAllTheWay) {
+ $containers.hide();
+ $navbarRight.removeClass('has-sticked-bar');
+ $block.show();
+
+ return;
+ }
+
+ if (isSmallScreen && $('#navbar .navbar-body').hasClass('in')) {
+ return;
+ }
+
+ if (scrollTop > stickTop) {
+ if (!$containers.hasClass('stick-sub')) {
+ $containers.addClass('stick-sub');
+ $block.show();
+ }
+
+ $navbarRight.addClass('has-sticked-bar');
+
+ $containers.show();
+
+ return;
+ }
+
+ if ($containers.hasClass('stick-sub')) {
+ $containers.removeClass('stick-sub');
+ $navbarRight.removeClass('has-sticked-bar');
+ $block.hide();
+ }
+
+ $containers.show();
+ });
+ }
+}
+
+export default StickyBarHelper;
diff --git a/client/src/views/record/detail.js b/client/src/views/record/detail.js
index d124b63f52..58119a81c6 100644
--- a/client/src/views/record/detail.js
+++ b/client/src/views/record/detail.js
@@ -31,6 +31,7 @@
import BaseRecordView from 'views/record/base';
import ViewRecordHelper from 'view-record-helper';
import ActionItemSetup from 'helpers/action-item-setup';
+import StickyBarHelper from 'helpers/record/misc/sticky-bar';
/**
* A detail record view.
@@ -1419,102 +1420,16 @@ class DetailRecordView extends BaseRecordView {
}
}
- /**
- * @private
- */
+ /** @private */
initStickableButtonsContainer() {
- const $containers = this.$el.find('.detail-button-container');
- const $container = this.$el.find('.detail-button-container.record-buttons');
+ const helper = new StickyBarHelper(
+ this,
+ this.stickButtonsFormBottomSelector,
+ this.stickButtonsContainerAllTheWay,
+ this.numId
+ );
- if (!$container.length) {
- return;
- }
-
- const navbarHeight = this.getThemeManager().getParam('navbarHeight');
- const screenWidthXs = this.getThemeManager().getParam('screenWidthXs');
-
- const isSmallScreen = $(window.document).width() < screenWidthXs;
-
- const getOffsetTop = (/** JQuery */$element) => {
- let element = /** @type {HTMLElement} */$element.get(0);
-
- let value = 0;
-
- while (element) {
- value += !isNaN(element.offsetTop) ? element.offsetTop : 0;
-
- element = element.offsetParent;
- }
-
- if (isSmallScreen) {
- return value;
- }
-
- return value - navbarHeight;
- };
-
- let stickTop = getOffsetTop($container);
- const blockHeight = $container.outerHeight();
-
- stickTop -= 5; // padding;
-
- const $block = $('
')
- .css('height', blockHeight + 'px')
- .html(' ')
- .hide()
- .insertAfter($container);
-
- let $middle = this.getMiddleView().$el;
- const $window = $(window);
- const $navbarRight = $('#navbar .navbar-right');
-
- if (this.stickButtonsFormBottomSelector) {
- const $bottom = this.$el.find(this.stickButtonsFormBottomSelector);
-
- if ($bottom.length) {
- $middle = $bottom;
- }
- }
-
- $window.off('scroll.detail-' + this.numId);
-
- $window.on('scroll.detail-' + this.numId, () => {
- const edge = $middle.position().top + $middle.outerHeight(false) - blockHeight;
- const scrollTop = $window.scrollTop();
-
- if (scrollTop >= edge && !this.stickButtonsContainerAllTheWay) {
- $containers.hide();
- $navbarRight.removeClass('has-sticked-bar');
- $block.show();
-
- return;
- }
-
- if (isSmallScreen && $('#navbar .navbar-body').hasClass('in')) {
- return;
- }
-
- if (scrollTop > stickTop) {
- if (!$containers.hasClass('stick-sub')) {
- $containers.addClass('stick-sub');
- $block.show();
- }
-
- $navbarRight.addClass('has-sticked-bar');
-
- $containers.show();
-
- return;
- }
-
- if ($containers.hasClass('stick-sub')) {
- $containers.removeClass('stick-sub');
- $navbarRight.removeClass('has-sticked-bar');
- $block.hide();
- }
-
- $containers.show();
- });
+ helper.init();
}
fetch() {