diff --git a/client/src/components/InlinePanel/index.js b/client/src/components/InlinePanel/index.js index c450929286..dbb974d0bb 100644 --- a/client/src/components/InlinePanel/index.js +++ b/client/src/components/InlinePanel/index.js @@ -181,6 +181,26 @@ export class InlinePanel extends ExpandingFormset { return forms.length; } + getFirstchild() { + const forms = $('> [data-inline-panel-child]', this.formsElt); + return forms.first()[0]; + } + + isFirstChildEmpty() { + let empty = false; + const forms = $('> [data-inline-panel-child]', this.formsElt); + const title = forms + .first()[0] + .querySelector('.chosen') + .querySelector(`[id$="title"]`); + const firsChild = forms.first()[0]; + + if (title.textContent === '' && !firsChild.classList.contains('deleted')) { + empty = true; + } + return empty; + } + updateAddButtonState() { if (this.opts.maxForms) { const addButton = $('#' + this.opts.formsetPrefix + '-ADD'); diff --git a/client/src/components/MultipleChooserPanel/index.js b/client/src/components/MultipleChooserPanel/index.js index 74ca3028fe..6388d5f3c6 100644 --- a/client/src/components/MultipleChooserPanel/index.js +++ b/client/src/components/MultipleChooserPanel/index.js @@ -15,10 +15,32 @@ export class MultipleChooserPanel extends InlinePanel { `${opts.formsetPrefix}-OPEN_MODAL`, ); openModalButton.addEventListener('click', () => { + if (this.isFirstChildEmpty()) { + openModalButton.setAttribute( + 'maxforms-remainder', + opts.maxForms + 1 - this.getChildCount(), + ); + openModalButton.setAttribute('maxforms', opts.maxForms); + } else { + openModalButton.setAttribute( + 'maxforms-remainder', + opts.maxForms - this.getChildCount(), + ); + openModalButton.setAttribute('maxforms', opts.maxForms); + } + + if (Number(openModalButton.getAttribute('maxforms-remainder')) === 0) { + openModalButton.setAttribute('disabled', 'true'); + } + this.chooserWidgetFactory.openModal( (result) => { result.forEach((item) => { - if (opts.maxForms && this.getChildCount() >= opts.maxForms) return; + if (this.isFirstChildEmpty()) { + if (opts.maxForms && this.getChildCount() >= opts.maxForms + 1) + return; + } else if (opts.maxForms && this.getChildCount() >= opts.maxForms) + return; this.addForm(); const formIndex = this.formCount - 1; const formPrefix = `${opts.formsetPrefix}-${formIndex}`; @@ -38,15 +60,30 @@ export class MultipleChooserPanel extends InlinePanel { const openModalButton = document.getElementById( `${this.opts.formsetPrefix}-OPEN_MODAL`, ); - if (this.getChildCount() >= this.opts.maxForms) { + const firstChildButton = this.getFirstchild() + .querySelector('.unchosen') + .querySelector('button'); + if (this.isFirstChildEmpty()) { + firstChildButton.setAttribute( + 'maxforms-remainder', + this.opts.maxForms + 1 - this.getChildCount(), + ); + } + if ( + this.isFirstChildEmpty() + ? this.getChildCount() >= this.opts.maxForms + 1 + : this.getChildCount() >= this.opts.maxForms + ) { // need to set the data-force-disabled attribute to override the standard modal-workflow // behaviour of re-enabling the button after the modal closes (which potentially happens // after this code has run) openModalButton.setAttribute('disabled', 'true'); openModalButton.setAttribute('data-force-disabled', 'true'); + if (firstChildButton) firstChildButton.setAttribute('disabled', 'true'); } else { openModalButton.removeAttribute('disabled'); openModalButton.removeAttribute('data-force-disabled'); + if (firstChildButton) firstChildButton.removeAttribute('disabled'); } } } diff --git a/client/src/includes/chooserModal.js b/client/src/includes/chooserModal.js index e05a339d0d..863584560a 100644 --- a/client/src/includes/chooserModal.js +++ b/client/src/includes/chooserModal.js @@ -218,6 +218,13 @@ class ChooserModalOnloadHandlerFactory { } $(this.chosenLinkSelector, containerElement).on('click', (event) => { + const openModalButton = $('[data-multiple-choice-open-modal]')[0]; + + if ( + Number(modal.triggerElement.getAttribute('maxforms-remainder')) === 1 + ) { + openModalButton.setAttribute('disabled', 'true'); + } modal.loadUrl(event.currentTarget.href); return false; }); @@ -237,9 +244,56 @@ class ChooserModalOnloadHandlerFactory { } updateMultipleChoiceSubmitEnabledState(modal) { + const openModalButton = $('[data-multiple-choice-open-modal]')[0]; // update the enabled state of the multiple choice submit button depending on whether // any items have been selected - if ($('[data-multiple-choice-select]:checked', modal.body).length) { + if ( + $('[data-multiple-choice-select]:checked', modal.body).length || + $('[data-multiple-choice-select][disabled]', modal.body).length || + (Number(openModalButton.getAttribute('maxforms-remainder')) === 0 && + openModalButton.getAttribute('disabled') === 'false') + ) { + const messageCounter = document.createElement('p'); + const selectCount = $( + '[data-multiple-choice-select]:checked', + modal.body, + ).length; + messageCounter.textContent = gettext( + `${openModalButton.getAttribute('maxforms') - openModalButton.getAttribute('maxforms-remainder') + selectCount}/${openModalButton.getAttribute('maxforms')}`, + ); + messageCounter.style.cssText = + 'font-weight: bold; font-size: 1rem; padding-top: 15px;'; + + const multipleChoice = $('[data-multiple-choice-select]', modal.body); + const lastMultipleChoice = multipleChoice.last(); + const message = lastMultipleChoice.next(); + + if ( + openModalButton.hasAttribute('maxforms') && + Number(openModalButton.getAttribute('maxforms')) !== 1000 + ) { + if (message.length <= 0) { + lastMultipleChoice.after(messageCounter); + } else { + message.remove(); + lastMultipleChoice.after(messageCounter); + } + } + if ( + $('[data-multiple-choice-select]:checked', modal.body).length === + Number(openModalButton.getAttribute('maxforms-remainder')) + ) { + $('[data-multiple-choice-select]:not(:checked)').attr( + 'disabled', + 'true', + ); + } else if ( + $('[data-multiple-choice-select]:checked', modal.body).length < + openModalButton.getAttribute('maxforms-remainder') + ) { + $('[data-multiple-choice-select]', modal.body).removeAttr('disabled'); + } + $('[data-multiple-choice-submit]', modal.body).removeAttr('disabled'); } else { $('[data-multiple-choice-submit]', modal.body).attr('disabled', true); diff --git a/wagtail/admin/templates/wagtailadmin/panels/multiple_chooser_panel.html b/wagtail/admin/templates/wagtailadmin/panels/multiple_chooser_panel.html index 3cc6e945b8..2af8fe7549 100644 --- a/wagtail/admin/templates/wagtailadmin/panels/multiple_chooser_panel.html +++ b/wagtail/admin/templates/wagtailadmin/panels/multiple_chooser_panel.html @@ -2,7 +2,7 @@ {% load i18n l10n wagtailadmin_tags %} {% block add_button %} - {% endblock %}