Enforce max_num on MultipleChooserPanel

Enable / disable the open-modal button on reaching the limit, as we do for InlinePanel's standard add button; and when handling the response from the modal, stop adding new items when max_num is reached
pull/9445/head
Matt Westcott 2023-01-18 12:01:45 +00:00
rodzic 2574204b27
commit 4468b55d2d
3 zmienionych plików z 49 dodań i 19 usunięć

Wyświetl plik

@ -27,6 +27,12 @@ export class InlinePanel extends ExpandingFormset {
this.initChildControls(childPrefix);
}
this.updateControlStates();
}
updateControlStates() {
/* Update states of listing controls in response to a change of state such as
adding, deleting or moving an element */
this.updateChildCount();
this.updateMoveButtonDisabledStates();
this.updateAddButtonState();
@ -43,9 +49,7 @@ export class InlinePanel extends ExpandingFormset {
/* set 'deleted' form field to true */
$('#' + deleteInputId).val('1');
currentChild.addClass('deleted').slideUp(() => {
this.updateChildCount();
this.updateMoveButtonDisabledStates();
this.updateAddButtonState();
this.updateControlStates();
});
});
@ -72,8 +76,7 @@ export class InlinePanel extends ExpandingFormset {
currentChildOrderElem.val(prevChildOrder);
prevChildOrderElem.val(currentChildOrder);
this.updateChildCount();
this.updateMoveButtonDisabledStates();
this.updateControlStates();
});
$down.on('click', () => {
@ -98,8 +101,7 @@ export class InlinePanel extends ExpandingFormset {
currentChildOrderElem.val(nextChildOrder);
nextChildOrderElem.val(currentChildOrder);
this.updateChildCount();
this.updateMoveButtonDisabledStates();
this.updateControlStates();
});
}
@ -110,9 +112,7 @@ export class InlinePanel extends ExpandingFormset {
$('#' + childId)
.addClass('deleted')
.hide(0, () => {
this.updateChildCount();
this.updateMoveButtonDisabledStates();
this.updateAddButtonState();
this.updateControlStates();
});
$('#' + childId)
@ -145,14 +145,18 @@ export class InlinePanel extends ExpandingFormset {
});
}
getChildCount() {
const forms = $('> [data-inline-panel-child]', this.formsElt).not(
'.deleted',
);
return forms.length;
}
updateAddButtonState() {
if (this.opts.maxForms) {
const forms = $('> [data-inline-panel-child]', this.formsElt).not(
'.deleted',
);
const addButton = $('#' + this.opts.formsetPrefix + '-ADD');
if (forms.length >= this.opts.maxForms) {
if (this.getChildCount() >= this.opts.maxForms) {
addButton.prop('disabled', true);
} else {
addButton.prop('disabled', false);
@ -228,9 +232,7 @@ export class InlinePanel extends ExpandingFormset {
$('#id_' + newChildPrefix + '-ORDER').val(formIndex + 1);
}
this.updateChildCount();
this.updateMoveButtonDisabledStates();
this.updateAddButtonState();
this.updateControlStates();
initCollapsiblePanels(
document.querySelectorAll(
`#inline_child_${newChildPrefix} [data-panel-toggle]`,

Wyświetl plik

@ -18,6 +18,7 @@ export class MultipleChooserPanel extends InlinePanel {
this.chooserWidgetFactory.openModal(
(result) => {
result.forEach((item) => {
if (opts.maxForms && this.getChildCount() >= opts.maxForms) return;
this.addForm();
const formIndex = this.formCount - 1;
const formPrefix = `${opts.formsetPrefix}-${formIndex}`;
@ -31,4 +32,27 @@ export class MultipleChooserPanel extends InlinePanel {
);
});
}
updateOpenModalButtonState() {
if (this.opts.maxForms) {
const openModalButton = document.getElementById(
`${this.opts.formsetPrefix}-OPEN_MODAL`,
);
if (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');
} else {
openModalButton.removeAttribute('disabled');
openModalButton.removeAttribute('data-force-disabled');
}
}
}
updateControlStates() {
super.updateControlStates();
this.updateOpenModalButtonState();
}
}

Wyświetl plik

@ -55,9 +55,13 @@ function ModalWorkflow(opts) {
$('body').append(self.container);
self.container.modal('hide');
// add listener - once modal is about to be hidden, re-enable the trigger
// add listener - once modal is about to be hidden, re-enable the trigger unless it's been forcibly
// disabled by adding a `data-force-disabled` attribute; this mechanism is necessary to accommodate
// response handlers that disable the trigger to prevent it from reopening
self.container.on('hide.bs.modal', () => {
self.triggerElement.removeAttribute('disabled');
if (!self.triggerElement.hasAttribute('data-force-disabled')) {
self.triggerElement.removeAttribute('disabled');
}
});
// add listener - once modal is fully hidden (closed & css transitions end) - re-focus on trigger and remove from DOM