diff --git a/Build/Sources/TypeScript/backend/form-engine-review.ts b/Build/Sources/TypeScript/backend/form-engine-review.ts index ea3ee64c6abf6bbb0230c9bdc07d28a1211cc760..063a9eabf18cf14499b24c4adf47cda81e5b0947 100644 --- a/Build/Sources/TypeScript/backend/form-engine-review.ts +++ b/Build/Sources/TypeScript/backend/form-engine-review.ts @@ -15,6 +15,8 @@ import 'bootstrap'; import $ from 'jquery'; import FormEngine from '@typo3/backend/form-engine'; import '@typo3/backend/element/icon-element'; +import Popover from './popover'; +import {Popover as BootstrapPopover} from 'bootstrap'; /** * Module: @typo3/backend/form-engine-review @@ -26,17 +28,12 @@ class FormEngineReview { /** * Class for the toggle button */ - private toggleButtonClass: string; - - /** - * Class for field list items - */ - private fieldListItemClass: string; + private readonly toggleButtonClass: string = 't3js-toggle-review-panel'; /** * Class of FormEngine labels */ - private labelSelector: string; + private readonly labelSelector: string = '.t3js-formengine-label'; /** * Fetches all fields that have a failed validation @@ -62,12 +59,7 @@ class FormEngineReview { $('<typo3-backend-icon/>', {identifier: 'actions-info', size: 'small'}), ); - $button.popover({ - container: 'body', - html: true, - placement: 'bottom', - }); - + Popover.popover($button); $leastButtonBar.prepend($button); } @@ -75,10 +67,6 @@ class FormEngineReview { * The constructor, set the class properties default values */ constructor() { - this.toggleButtonClass = 't3js-toggle-review-panel'; - this.fieldListItemClass = 't3js-field-item'; - this.labelSelector = '.t3js-formengine-label'; - this.initialize(); } @@ -92,7 +80,6 @@ class FormEngineReview { $((): void => { FormEngineReview.attachButtonToModuleHeader(me); }); - $document.on('click', '.' + this.fieldListItemClass, this.switchToField); $document.on('t3-formengine-postfieldvalidation', this.checkForReviewableField); } @@ -109,34 +96,25 @@ class FormEngineReview { $invalidFields.each(function(this: Element): void { const $field: any = $(this); - const $input: any = $field.find('[data-formengine-validation-rules]'); - let inputId: any = $input.attr('id'); - - if (typeof inputId === 'undefined') { - inputId = $input.parent().children('[id]').first().attr('id'); - } - - $list.append( - $('<a />', { - 'class': 'list-group-item ' + me.fieldListItemClass, - 'data-field-id': inputId, - 'href': '#', - }).text($field.find(me.labelSelector).text()), - ); + const $input: JQuery = $field.find('[data-formengine-validation-rules]'); + + const link = document.createElement('a'); + link.classList.add('list-group-item'); + link.href = '#'; + link.textContent = $field.find(me.labelSelector).text(); + link.addEventListener('click', (e: Event) => me.switchToField(e, $input)); + + $list.append(link); }); $toggleButton.removeClass('hidden'); - - // bootstrap has no official API to update the content of a popover w/o destroying it - const $popover: any = $toggleButton.data('bs.popover'); - if ($popover) { - $popover.options.html = true; - $popover.options.content = $list.wrapAll('<div>').parent().html(); - $popover.setContent($popover.$tip); - $popover.$tip.addClass($popover.options.placement); - } + Popover.setOptions($toggleButton, <BootstrapPopover.Options>{ + html: true, + content: $list[0] + }); } else { - $toggleButton.addClass('hidden').popover('hide'); + $toggleButton.addClass('hidden'); + Popover.hide($toggleButton); } } @@ -145,12 +123,10 @@ class FormEngineReview { * * @param {Event} e */ - public switchToField = (e: Event): void => { + public switchToField = (e: Event, $referenceField: JQuery): void => { e.preventDefault(); - const $listItem: any = $(e.currentTarget); - const referenceFieldId: string = $listItem.data('fieldId'); - const $referenceField: any = $('#' + referenceFieldId); + const listItem: HTMLElement = e.currentTarget as HTMLElement; // iterate possibly nested tab panels $referenceField.parents('[id][role="tabpanel"]').each(function(this: Element): void { diff --git a/Build/Sources/TypeScript/backend/popover.ts b/Build/Sources/TypeScript/backend/popover.ts index 36d14aaec101273c1e7c0981c3e6d6142a4aa0f9..0144cc5b8dc4a9e34f041f54b937e933fdbf522d 100644 --- a/Build/Sources/TypeScript/backend/popover.ts +++ b/Build/Sources/TypeScript/backend/popover.ts @@ -66,16 +66,24 @@ class Popover { public setOptions($element: JQuery, options?: BootstrapPopover.Options): void { options = options || <BootstrapPopover.Options>{}; options.html = true; - const title: string|(() => void) = options.title || $element.data('title') || ''; - const content: string|(() => void) = options.content || $element.data('bs-content') || ''; + const title: string = options.title || $element.data('title') || ''; + const content: string = options.content || $element.data('bs-content') || ''; $element .attr('data-bs-original-title', (title as string)) .attr('data-bs-content', (content as string)) .attr('data-bs-placement', 'auto') + delete options.title; + delete options.content; $.each(options, (key, value) => { this.setOption($element, key, value); }); + + const popover = $element.data('typo3.bs.popover'); + popover.setContent({ + '.popover-header': title, + '.popover-body': content + }); } // noinspection JSMethodCanBeStatic @@ -87,18 +95,12 @@ class Popover { * @param {String} value */ public setOption($element: JQuery, key: string, value: string): void { - if (key === 'content') { - const popover = $element.data('typo3.bs.popover'); - popover._config.content = value; - popover.setContent(popover.tip); - } else { - $element.each((i, el) => { - const popover = $(el).data('typo3.bs.popover'); - if (popover) { - popover._config[key] = value; - } - }); - } + $element.each((i, el) => { + const popover = $(el).data('typo3.bs.popover'); + if (popover) { + popover._config[key] = value; + } + }); } // noinspection JSMethodCanBeStatic diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js index 97a314affdeadd947f33121236b608a9da05cd5b..8e7ce4854bc9936e08874922fcc0c875f8b3411b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import FormEngine from"@typo3/backend/form-engine.js";import"@typo3/backend/element/icon-element.js";class FormEngineReview{constructor(){this.checkForReviewableField=()=>{const t=this,e=FormEngineReview.findInvalidField(),i=$("."+this.toggleButtonClass);if(e.length>0){const n=$("<div />",{class:"list-group"});e.each((function(){const e=$(this),i=e.find("[data-formengine-validation-rules]");let o=i.attr("id");void 0===o&&(o=i.parent().children("[id]").first().attr("id")),n.append($("<a />",{class:"list-group-item "+t.fieldListItemClass,"data-field-id":o,href:"#"}).text(e.find(t.labelSelector).text()))})),i.removeClass("hidden");const o=i.data("bs.popover");o&&(o.options.html=!0,o.options.content=n.wrapAll("<div>").parent().html(),o.setContent(o.$tip),o.$tip.addClass(o.options.placement))}else i.addClass("hidden").popover("hide")},this.switchToField=t=>{t.preventDefault();const e=$(t.currentTarget).data("fieldId"),i=$("#"+e);i.parents('[id][role="tabpanel"]').each((function(){$('[aria-controls="'+$(this).attr("id")+'"]').tab("show")})),i.focus()},this.toggleButtonClass="t3js-toggle-review-panel",this.fieldListItemClass="t3js-field-item",this.labelSelector=".t3js-formengine-label",this.initialize()}static findInvalidField(){return $(document).find(".tab-content ."+FormEngine.Validation.errorClass)}static attachButtonToModuleHeader(t){const e=$(".t3js-module-docheader-bar-buttons").children().last().find('[role="toolbar"]'),i=$("<a />",{class:"btn btn-danger btn-sm hidden "+t.toggleButtonClass,href:"#",title:TYPO3.lang["buttons.reviewFailedValidationFields"]}).append($("<typo3-backend-icon/>",{identifier:"actions-info",size:"small"}));i.popover({container:"body",html:!0,placement:"bottom"}),e.prepend(i)}initialize(){const t=this,e=$(document);$((()=>{FormEngineReview.attachButtonToModuleHeader(t)})),e.on("click","."+this.fieldListItemClass,this.switchToField),e.on("t3-formengine-postfieldvalidation",this.checkForReviewableField)}}export default new FormEngineReview; \ No newline at end of file +import"bootstrap";import $ from"jquery";import FormEngine from"@typo3/backend/form-engine.js";import"@typo3/backend/element/icon-element.js";import Popover from"@typo3/backend/popover.js";class FormEngineReview{constructor(){this.toggleButtonClass="t3js-toggle-review-panel",this.labelSelector=".t3js-formengine-label",this.checkForReviewableField=()=>{const e=this,t=FormEngineReview.findInvalidField(),i=$("."+this.toggleButtonClass);if(t.length>0){const o=$("<div />",{class:"list-group"});t.each((function(){const t=$(this),i=t.find("[data-formengine-validation-rules]"),n=document.createElement("a");n.classList.add("list-group-item"),n.href="#",n.textContent=t.find(e.labelSelector).text(),n.addEventListener("click",(t=>e.switchToField(t,i))),o.append(n)})),i.removeClass("hidden"),Popover.setOptions(i,{html:!0,content:o[0]})}else i.addClass("hidden"),Popover.hide(i)},this.switchToField=(e,t)=>{e.preventDefault();e.currentTarget;t.parents('[id][role="tabpanel"]').each((function(){$('[aria-controls="'+$(this).attr("id")+'"]').tab("show")})),t.focus()},this.initialize()}static findInvalidField(){return $(document).find(".tab-content ."+FormEngine.Validation.errorClass)}static attachButtonToModuleHeader(e){const t=$(".t3js-module-docheader-bar-buttons").children().last().find('[role="toolbar"]'),i=$("<a />",{class:"btn btn-danger btn-sm hidden "+e.toggleButtonClass,href:"#",title:TYPO3.lang["buttons.reviewFailedValidationFields"]}).append($("<typo3-backend-icon/>",{identifier:"actions-info",size:"small"}));Popover.popover(i),t.prepend(i)}initialize(){const e=this,t=$(document);$((()=>{FormEngineReview.attachButtonToModuleHeader(e)})),t.on("t3-formengine-postfieldvalidation",this.checkForReviewableField)}}export default new FormEngineReview; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/popover.js b/typo3/sysext/backend/Resources/Public/JavaScript/popover.js index b8a9dc2e62c2b68b5690cc1b2052774c3fdadebe..84e98a95d8e52e1e45dc38ca6277a5be8c9c2764 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/popover.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/popover.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{Popover as BootstrapPopover}from"bootstrap";class Popover{constructor(){this.DEFAULT_SELECTOR='[data-bs-toggle="popover"]',this.initialize()}initialize(t){t=t||this.DEFAULT_SELECTOR,$(t).each(((t,o)=>{const e=new BootstrapPopover(o);$(o).data("typo3.bs.popover",e)}))}popover(t){t.each(((t,o)=>{const e=new BootstrapPopover(o);$(o).data("typo3.bs.popover",e)}))}setOptions(t,o){(o=o||{}).html=!0;const e=o.title||t.data("title")||"",a=o.content||t.data("bs-content")||"";t.attr("data-bs-original-title",e).attr("data-bs-content",a).attr("data-bs-placement","auto"),$.each(o,((o,e)=>{this.setOption(t,o,e)}))}setOption(t,o,e){if("content"===o){const o=t.data("typo3.bs.popover");o._config.content=e,o.setContent(o.tip)}else t.each(((t,a)=>{const p=$(a).data("typo3.bs.popover");p&&(p._config[o]=e)}))}show(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.show()}))}hide(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.hide()}))}destroy(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.dispose()}))}toggle(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.toggle()}))}update(t){t.data("typo3.bs.popover")._popper.update()}}export default new Popover; \ No newline at end of file +import $ from"jquery";import{Popover as BootstrapPopover}from"bootstrap";class Popover{constructor(){this.DEFAULT_SELECTOR='[data-bs-toggle="popover"]',this.initialize()}initialize(t){t=t||this.DEFAULT_SELECTOR,$(t).each(((t,o)=>{const e=new BootstrapPopover(o);$(o).data("typo3.bs.popover",e)}))}popover(t){t.each(((t,o)=>{const e=new BootstrapPopover(o);$(o).data("typo3.bs.popover",e)}))}setOptions(t,o){(o=o||{}).html=!0;const e=o.title||t.data("title")||"",p=o.content||t.data("bs-content")||"";t.attr("data-bs-original-title",e).attr("data-bs-content",p).attr("data-bs-placement","auto"),delete o.title,delete o.content,$.each(o,((o,e)=>{this.setOption(t,o,e)}));t.data("typo3.bs.popover").setContent({".popover-header":e,".popover-body":p})}setOption(t,o,e){t.each(((t,p)=>{const a=$(p).data("typo3.bs.popover");a&&(a._config[o]=e)}))}show(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.show()}))}hide(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.hide()}))}destroy(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.dispose()}))}toggle(t){t.each(((t,o)=>{const e=$(o).data("typo3.bs.popover");e&&e.toggle()}))}update(t){t.data("typo3.bs.popover")._popper.update()}}export default new Popover; \ No newline at end of file