From a93c35a3befb4e6bb6f8a61e84d55f17681f7c77 Mon Sep 17 00:00:00 2001 From: Ivan Kljun Date: Tue, 17 Oct 2023 16:49:19 +0200 Subject: [PATCH] Add a listener for when TinyMCE is opened and adjust headers [SCI-9449] --- app/javascript/packs/tiny_mce.js | 19 ++-- .../vue/mixins/stackableHeadersMixin.js | 89 +++++++++++++++++-- 2 files changed, 95 insertions(+), 13 deletions(-) diff --git a/app/javascript/packs/tiny_mce.js b/app/javascript/packs/tiny_mce.js index d93fce4b5..3c73ede79 100644 --- a/app/javascript/packs/tiny_mce.js +++ b/app/javascript/packs/tiny_mce.js @@ -178,10 +178,12 @@ window.TinyMCE = (() => { if (typeof tinyMCE !== 'undefined') { // Hide element containing HTML view of RTE field const tinyMceContainer = $(selector).closest('form').find('.tinymce-view'); + const editorForm = $(selector).closest('form'); const tinyMceInitSize = tinyMceContainer.height(); - $(selector).closest('.form-group, .tinymce-editor-container') - .before(`
`); + + editorForm.parent().height(tinyMceInitSize); tinyMceContainer.addClass('hidden'); + const plugins = ` image table autosave autoresize link advlist codesample code autolink lists charmap anchor searchreplace wordcount visualblocks visualchars @@ -301,11 +303,14 @@ window.TinyMCE = (() => { const editorForm = editorContainer.closest('form'); const menuBar = editorForm.find('.tox-menubar'); - $('.tinymce-placeholder').css('height', `${$(editor.editorContainer).height()}px`); - setTimeout(() => { - editorContainer.addClass('tox-tinymce--loaded'); - $('.tinymce-placeholder').remove(); - }, 400); + editorContainer.addClass('tox-tinymce--loaded'); + const event = new CustomEvent('tinyMCEOpened', { + detail: { + target: editorForm.parent(), + } + }); + window.dispatchEvent(event); + editorForm.parent().css('height', ''); // Init saved status label if (editor.getContent() !== '') { diff --git a/app/javascript/vue/mixins/stackableHeadersMixin.js b/app/javascript/vue/mixins/stackableHeadersMixin.js index b5e14b3b0..4477b2ce3 100644 --- a/app/javascript/vue/mixins/stackableHeadersMixin.js +++ b/app/javascript/vue/mixins/stackableHeadersMixin.js @@ -22,20 +22,97 @@ export default { mounted() { this.secondaryNavigation = document.querySelector('#taskSecondaryMenu'); - this.resizeObserver = new ResizeObserver((entries) => { - entries.forEach((entry) => { - this.taskSecondaryMenuHeight = entry.target.offsetHeight; + if (this.secondaryNavigation) { + this.resizeObserver = new ResizeObserver((entries) => { + entries.forEach((entry) => { + this.taskSecondaryMenuHeight = entry.target.offsetHeight; + }); }); - }); - this.resizeObserver.observe(this.secondaryNavigation); + this.resizeObserver.observe(this.secondaryNavigation); + } + window.addEventListener('tinyMCEOpened', (e) => { + this.handleTinyMCEOpened(e.detail.target); + }); }, beforeDestroy() { if (this.resizeObserver) { this.resizeObserver.disconnect(); } + window.removeEventListener('tinyMCEOpened', this.handleTinyMCEOpened); }, methods: { + handleTinyMCEOpened(target) { + const getVisibleHeight = (elemTop, elemHeight) => { + let visibleHeight = 0; + if (elemTop >= 0) { + visibleHeight = Math.min(elemHeight, window.innerHeight - elemTop); + } else if (elemTop + elemHeight > 0) { + visibleHeight = elemTop + elemHeight; + } + return visibleHeight; + }; + + let headerHeight = 0; + let headerTop = 0; + let secondaryNavigationHeight = 0; + let secondaryNavigationTop = 0; + + if (this.headerRef) { + headerHeight = this.headerRef.offsetHeight; + headerTop = this.headerRef.getBoundingClientRect().top; + } + + if (this.secondaryNavigation) { + secondaryNavigationHeight = this.secondaryNavigation.offsetHeight; + secondaryNavigationTop = this.secondaryNavigation.getBoundingClientRect().top; + } + + const editorHeaderTop = target.offset().top; + let totalHeight = 0; + + const visibleHeaderHeight = getVisibleHeight(headerTop, headerHeight); + if (headerTop + visibleHeaderHeight < editorHeaderTop) { + totalHeight += visibleHeaderHeight; + } + + const visibleSecondaryNavHeight = getVisibleHeight(secondaryNavigationTop, secondaryNavigationHeight); + if (secondaryNavigationTop + visibleSecondaryNavHeight < editorHeaderTop) { + totalHeight += visibleSecondaryNavHeight; + } + + const editorHeader = $('.tox-editor-header'); + + // For Protocol Templates only reset the left value + if (!this.headerRef && !this.secondaryNavigation) { + editorHeader.css('left', ''); + return; + } + + // Handle opening TinyMCE toolbars when only a small bottom area of editor is visible + const targetBottom = target[0].getBoundingClientRect().bottom; + if (targetBottom < 3 * headerHeight) { + $('html, body').animate({ + scrollTop: target.offset().top, + }, 800); + return; + } + + // Handle showing TinyMCE toolbar for fixed/static position of toolbar + if (editorHeader.css('position') === 'fixed') { + editorHeader.css({ + top: totalHeight - 1, + left: '', + }); + } else if (headerTop < (visibleHeaderHeight + visibleSecondaryNavHeight) + && target[0].getBoundingClientRect().top <= headerTop) { + $('html, body').animate({ + scrollTop: editorHeader.offset().top + }, 800); + } + + target.focus(); + }, initStackableHeaders() { const header = this.headerRef; const headerHeight = header.offsetHeight; @@ -93,7 +170,7 @@ export default { // Apply TinyMCE offset $('.tox-editor-header').css( 'top', - stickyNavigationHeight + parseInt($(this.secondaryNavigation).css('top'), 10) + stickyNavigationHeight + parseInt($(this.secondaryNavigation).css('top'), 10) - 1, ); this.lastScrollTop = window.scrollY; // Save last scroll position to when user scroll up/down },