2018-11-28 17:40:54 +00:00
|
|
|
(function(Modules) {
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
Modules.TemplateFolderForm = function() {
|
|
|
|
|
|
|
|
|
|
this.start = function(templateFolderForm) {
|
|
|
|
|
this.$form = $(templateFolderForm);
|
|
|
|
|
|
remove the unknown button in js
When you hit enter while an input in a form is in focus, your browser
finds the first button in the form, and carries out that action. So,
for non-js users, we added a hidden submit button with a value of
"unknown" to reflect that we don't know the intention of the user.
However, with JS enabled, this ambiguity doesn't exist - there's only
submit button and forms to fill in at a time, and non-visible fields
aren't even submitted at all. We can remove the unknown button,
supporting enter as submit properly. If the user is on one of the grey
button states, with no submit, it'll press the first button, and go to
the new template / move to existing folder dialog. That's fine enough.
2018-12-05 13:48:07 +00:00
|
|
|
// remove the hidden unknown button - if you've got JS enabled then the action you want to do is implied by
|
|
|
|
|
// which field is visible.
|
|
|
|
|
this.$form.find('button[value=unknown]').remove();
|
|
|
|
|
|
2019-02-13 17:47:24 +00:00
|
|
|
this.$liveRegionCounter = this.$form.find('.selection-counter');
|
2018-11-28 17:40:54 +00:00
|
|
|
|
2019-02-15 11:03:22 +00:00
|
|
|
this.$liveRegionCounter.before(this.nothingSelectedButtons);
|
|
|
|
|
this.$liveRegionCounter.before(this.itemsSelectedButtons);
|
2018-11-28 17:40:54 +00:00
|
|
|
|
|
|
|
|
// all the diff states that we want to show or hide
|
2018-12-04 14:48:39 +00:00
|
|
|
this.states = [
|
2020-09-22 11:45:55 +01:00
|
|
|
{
|
|
|
|
|
key: 'nothing-selected-buttons',
|
|
|
|
|
$el: this.$form.find('#nothing_selected'),
|
|
|
|
|
cancellable: false
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'items-selected-buttons',
|
|
|
|
|
$el: this.$form.find('#items_selected'),
|
|
|
|
|
cancellable: false
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'move-to-existing-folder',
|
|
|
|
|
$el: this.$form.find('#move_to_folder_radios'),
|
|
|
|
|
cancellable: true,
|
|
|
|
|
setFocus: () => $('#move_to_folder_radios').focus(),
|
|
|
|
|
action: 'move to folder',
|
|
|
|
|
description: 'Press move to confirm or cancel to close'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'move-to-new-folder',
|
|
|
|
|
$el: this.$form.find('#move_to_new_folder_form'),
|
|
|
|
|
cancellable: true,
|
|
|
|
|
setFocus: () => $('#move_to_new_folder_form').focus(),
|
|
|
|
|
action: 'move to new folder',
|
|
|
|
|
description: 'Press add to new folder to confirm name or cancel to close'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'add-new-folder',
|
|
|
|
|
$el: this.$form.find('#add_new_folder_form'),
|
|
|
|
|
cancellable: true,
|
|
|
|
|
setFocus: () => $('#add_new_folder_form').focus(),
|
|
|
|
|
action: 'new folder',
|
|
|
|
|
description: 'Press add new folder to confirm name or cancel to close'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'add-new-template',
|
|
|
|
|
$el: this.$form.find('#add_new_template_form'),
|
|
|
|
|
cancellable: true,
|
|
|
|
|
setFocus: () => $('#add_new_template_form').focus(),
|
|
|
|
|
action: 'new template',
|
|
|
|
|
description: 'Press continue to confirm selection or cancel to close'
|
|
|
|
|
}
|
2018-12-04 14:48:39 +00:00
|
|
|
];
|
2018-11-29 17:39:58 +00:00
|
|
|
|
2018-12-10 16:53:12 +00:00
|
|
|
// cancel/clear buttons only relevant if JS enabled, so
|
2018-12-04 14:48:39 +00:00
|
|
|
this.states.filter(state => state.cancellable).forEach((x) => this.addCancelButton(x));
|
2018-12-10 16:53:12 +00:00
|
|
|
this.states.filter(state => state.key === 'items-selected-buttons').forEach(x => this.addClearButton(x));
|
2018-11-30 12:02:51 +00:00
|
|
|
|
2020-09-22 11:45:55 +01:00
|
|
|
// make elements focusabled
|
|
|
|
|
this.states.filter(state => state.setFocus).forEach(x => x.$el.attr('tabindex', '0'));
|
|
|
|
|
|
|
|
|
|
this.addDescriptionsToStates();
|
|
|
|
|
|
2019-01-17 10:52:13 +00:00
|
|
|
// activate stickiness of elements in each state
|
|
|
|
|
this.activateStickyElements();
|
|
|
|
|
|
2018-11-29 16:20:44 +00:00
|
|
|
// first off show the new template / new folder buttons
|
2019-03-20 14:04:06 +00:00
|
|
|
this._lastState = this.$form.data('prev-state');
|
|
|
|
|
if (this._lastState === undefined) {
|
2019-02-13 16:43:38 +00:00
|
|
|
this.selectActionButtons();
|
2018-12-19 15:24:48 +00:00
|
|
|
} else {
|
2019-03-20 14:04:06 +00:00
|
|
|
this.currentState = this._lastState;
|
2018-12-19 15:24:48 +00:00
|
|
|
this.render();
|
2018-12-05 12:01:02 +00:00
|
|
|
}
|
2018-11-29 16:20:44 +00:00
|
|
|
|
2023-06-15 15:15:37 -04:00
|
|
|
this.$form.on('click', 'button.usa-button--event', (event) => this.actionButtonClicked(event));
|
2018-11-29 16:20:44 +00:00
|
|
|
this.$form.on('change', 'input[type=checkbox]', () => this.templateFolderCheckboxChanged());
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-22 11:45:55 +01:00
|
|
|
this.addDescriptionsToStates = function () {
|
|
|
|
|
let id, description;
|
2019-01-29 15:53:59 +00:00
|
|
|
|
2020-09-22 11:45:55 +01:00
|
|
|
$.each(this.states.filter(state => 'description' in state), (idx, state) => {
|
|
|
|
|
id = `${state.key}__description`;
|
2023-06-15 15:15:37 -04:00
|
|
|
description = `<p class="usa-sr-only" id="${id}">${state.description}</p>`;
|
2020-09-22 11:45:55 +01:00
|
|
|
state.$el
|
|
|
|
|
.prepend(description)
|
|
|
|
|
.attr('aria-describedby', id);
|
|
|
|
|
});
|
2019-01-29 15:53:59 +00:00
|
|
|
};
|
|
|
|
|
|
2019-01-17 10:52:13 +00:00
|
|
|
this.activateStickyElements = function() {
|
2019-01-25 16:47:49 +00:00
|
|
|
var oldClass = 'js-will-stick-at-bottom-when-scrolling';
|
2019-01-17 10:52:13 +00:00
|
|
|
var newClass = 'js-stick-at-bottom-when-scrolling';
|
|
|
|
|
|
|
|
|
|
this.states.forEach(state => {
|
|
|
|
|
state.$el
|
2019-01-25 16:47:49 +00:00
|
|
|
.find('.' + oldClass)
|
2019-01-17 10:52:13 +00:00
|
|
|
.removeClass(oldClass)
|
|
|
|
|
.addClass(newClass);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-04 14:48:39 +00:00
|
|
|
this.addCancelButton = function(state) {
|
2019-02-01 16:13:04 +00:00
|
|
|
let selector = `[value=${state.key}]`;
|
2019-02-15 16:10:01 +00:00
|
|
|
let $cancel = this.makeButton('Cancel', {
|
|
|
|
|
'onclick': () => {
|
|
|
|
|
|
|
|
|
|
// clear existing data
|
|
|
|
|
state.$el.find('input:radio').prop('checked', false);
|
|
|
|
|
state.$el.find('input:text').val('');
|
|
|
|
|
|
|
|
|
|
// go back to action buttons
|
|
|
|
|
this.selectActionButtons(selector);
|
|
|
|
|
},
|
|
|
|
|
'cancelSelector': selector,
|
2020-09-21 20:27:47 +01:00
|
|
|
'nonvisualText': state.action
|
2019-02-15 16:10:01 +00:00
|
|
|
});
|
2018-11-30 12:02:51 +00:00
|
|
|
|
2018-12-10 17:07:30 +00:00
|
|
|
state.$el.find('[type=submit]').after($cancel);
|
2018-11-30 12:02:51 +00:00
|
|
|
};
|
|
|
|
|
|
2018-12-10 16:53:12 +00:00
|
|
|
this.addClearButton = function(state) {
|
2019-02-13 16:43:38 +00:00
|
|
|
let selector = 'button[value=add-new-template]';
|
2019-02-15 16:10:01 +00:00
|
|
|
let $clear = this.makeButton('Clear', {
|
|
|
|
|
'onclick': () => {
|
2019-02-13 16:43:38 +00:00
|
|
|
|
2019-02-15 16:10:01 +00:00
|
|
|
// uncheck all templates and folders
|
|
|
|
|
this.$form.find('input:checkbox').prop('checked', false);
|
2018-12-10 16:53:12 +00:00
|
|
|
|
2019-02-15 16:10:01 +00:00
|
|
|
// go back to action buttons
|
|
|
|
|
this.selectActionButtons(selector);
|
|
|
|
|
},
|
|
|
|
|
'nonvisualText': "selection"
|
2018-12-10 16:53:12 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
state.$el.find('.template-list-selected-counter').append($clear);
|
|
|
|
|
};
|
|
|
|
|
|
2019-02-15 16:10:01 +00:00
|
|
|
this.makeButton = (text, opts) => {
|
|
|
|
|
let $btn = $('<a href=""></a>')
|
|
|
|
|
.html(text)
|
2023-06-15 15:15:37 -04:00
|
|
|
.addClass('usa-link js-cancel')
|
2019-02-15 16:10:01 +00:00
|
|
|
// isn't set if cancelSelector is undefined
|
|
|
|
|
.data('target', opts.cancelSelector || undefined)
|
|
|
|
|
.attr('tabindex', '0')
|
|
|
|
|
.on('click keydown', event => {
|
|
|
|
|
// space, enter or no keyCode (must be mouse input)
|
|
|
|
|
if ([13, 32, undefined].indexOf(event.keyCode) > -1) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
if (opts.hasOwnProperty('onclick')) { opts.onclick(); }
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (opts.hasOwnProperty('nonvisualText')) {
|
2023-06-15 15:15:37 -04:00
|
|
|
$btn.append(`<span class="usa-sr-only"> ${opts.nonvisualText}</span>`);
|
2018-12-10 17:32:36 +00:00
|
|
|
}
|
2019-02-15 16:10:01 +00:00
|
|
|
|
|
|
|
|
return $btn;
|
|
|
|
|
};
|
2018-12-10 16:53:12 +00:00
|
|
|
|
2019-02-01 16:13:04 +00:00
|
|
|
this.selectActionButtons = function (targetSelector) {
|
2018-12-05 12:01:02 +00:00
|
|
|
// If we want to show one of the grey choose actions state, we can pretend we're in the choose actions state,
|
|
|
|
|
// and then pretend a checkbox was clicked to work out whether to show zero or non-zero options.
|
|
|
|
|
// This calls a render at the end
|
|
|
|
|
this.currentState = 'nothing-selected-buttons';
|
|
|
|
|
this.templateFolderCheckboxChanged();
|
2019-02-01 16:13:04 +00:00
|
|
|
if (targetSelector) {
|
2020-09-22 11:45:55 +01:00
|
|
|
$(targetSelector).focus();
|
2019-02-01 16:13:04 +00:00
|
|
|
}
|
2018-12-05 12:01:02 +00:00
|
|
|
};
|
|
|
|
|
|
2019-03-20 14:04:06 +00:00
|
|
|
// method that checks the state against the last one, used prior to render() to see if needed
|
|
|
|
|
this.stateChanged = function() {
|
|
|
|
|
let changed = this.currentState !== this._lastState;
|
|
|
|
|
|
|
|
|
|
this._lastState = this.currentState;
|
|
|
|
|
return changed;
|
|
|
|
|
};
|
|
|
|
|
|
2020-08-11 15:44:17 +01:00
|
|
|
this.$singleNotificationChannel = (document.querySelector('div[id=add_new_template_form]')).getAttribute("data-channel");
|
|
|
|
|
this.$singleChannelService = (document.querySelector('div[id=add_new_template_form]')).getAttribute("data-service");
|
2020-08-10 18:14:36 +01:00
|
|
|
|
2018-11-29 17:39:58 +00:00
|
|
|
this.actionButtonClicked = function(event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
this.currentState = $(event.currentTarget).val();
|
|
|
|
|
|
2020-08-11 15:44:17 +01:00
|
|
|
if (event.currentTarget.value === 'add-new-template' && this.$singleNotificationChannel) {
|
|
|
|
|
window.location = "/services/" + this.$singleChannelService + "/templates/add-" + this.$singleNotificationChannel;
|
2020-08-10 18:14:36 +01:00
|
|
|
} else {
|
|
|
|
|
if (this.stateChanged()) {
|
|
|
|
|
this.render();
|
2020-08-11 15:44:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-11-29 17:39:58 +00:00
|
|
|
};
|
|
|
|
|
|
2019-02-13 17:47:24 +00:00
|
|
|
this.selectionStatus = {
|
|
|
|
|
'default': 'Nothing selected',
|
2020-09-18 11:51:56 +01:00
|
|
|
'selected': numSelected => {
|
|
|
|
|
const getString = key => {
|
|
|
|
|
if (numSelected[key] === 0) {
|
|
|
|
|
return '';
|
|
|
|
|
} else if (numSelected[key] === 1) {
|
|
|
|
|
return `1 ${key.substring(0, key.length - 1)}`;
|
|
|
|
|
} else {
|
|
|
|
|
return `${numSelected[key]} ${key}`;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const results = [];
|
|
|
|
|
|
|
|
|
|
if (numSelected.templates > 0) {
|
|
|
|
|
results.push(getString('templates'));
|
|
|
|
|
}
|
|
|
|
|
if (numSelected.folders > 0) {
|
|
|
|
|
results.push(getString('folders'));
|
|
|
|
|
}
|
|
|
|
|
return results.join(', ') + ' selected';
|
|
|
|
|
},
|
2019-02-13 17:47:24 +00:00
|
|
|
'update': numSelected => {
|
2020-09-18 11:51:56 +01:00
|
|
|
let message = (numSelected.total > 0) ? this.selectionStatus.selected(numSelected) : this.selectionStatus.default;
|
2019-02-13 17:47:24 +00:00
|
|
|
|
2019-02-15 13:49:49 +00:00
|
|
|
$('.template-list-selected-counter__count').html(message);
|
|
|
|
|
this.$liveRegionCounter.html(message);
|
2019-02-13 17:47:24 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-29 17:39:58 +00:00
|
|
|
this.templateFolderCheckboxChanged = function() {
|
2018-11-29 16:20:44 +00:00
|
|
|
let numSelected = this.countSelectedCheckboxes();
|
|
|
|
|
|
2020-09-18 11:51:56 +01:00
|
|
|
if (this.currentState === 'nothing-selected-buttons' && numSelected.total !== 0) {
|
2018-11-30 16:29:00 +00:00
|
|
|
// user has just selected first item
|
2018-12-04 14:47:05 +00:00
|
|
|
this.currentState = 'items-selected-buttons';
|
2020-09-18 11:51:56 +01:00
|
|
|
} else if (this.currentState === 'items-selected-buttons' && numSelected.total === 0) {
|
2018-11-30 16:29:00 +00:00
|
|
|
// user has just deselected last item
|
2018-12-04 14:47:05 +00:00
|
|
|
this.currentState = 'nothing-selected-buttons';
|
2018-11-29 16:20:44 +00:00
|
|
|
}
|
2018-11-28 17:40:54 +00:00
|
|
|
|
2019-03-20 14:04:06 +00:00
|
|
|
if (this.stateChanged()) {
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
2018-12-10 16:53:12 +00:00
|
|
|
|
2019-02-13 17:47:24 +00:00
|
|
|
this.selectionStatus.update(numSelected);
|
2018-12-10 16:53:12 +00:00
|
|
|
|
2019-01-03 10:50:05 +00:00
|
|
|
$('.template-list-selected-counter').toggle(this.hasCheckboxes());
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.hasCheckboxes = function() {
|
|
|
|
|
return !!this.$form.find('input:checkbox').length;
|
2018-11-28 17:40:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.countSelectedCheckboxes = function() {
|
2020-09-18 11:51:56 +01:00
|
|
|
const allSelected = this.$form.find('input:checkbox:checked');
|
|
|
|
|
const templates = allSelected.filter((idx, el) => $(el).siblings('.template-list-template').length > 0).length;
|
|
|
|
|
const folders = allSelected.filter((idx, el) => $(el).siblings('.template-list-folder').length > 0).length;
|
|
|
|
|
const results = {
|
|
|
|
|
'templates': templates,
|
|
|
|
|
'folders': folders,
|
|
|
|
|
'total': allSelected.length
|
|
|
|
|
};
|
|
|
|
|
return results;
|
2018-11-28 17:40:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.render = function() {
|
2019-01-29 15:53:59 +00:00
|
|
|
let mode = 'default';
|
|
|
|
|
let currentStateObj = this.states.filter(state => { return (state.key === this.currentState); })[0];
|
2020-09-22 11:45:55 +01:00
|
|
|
let scrollTop;
|
2019-01-30 19:12:14 +00:00
|
|
|
|
2018-11-29 17:39:58 +00:00
|
|
|
// detach everything, unless they are the currentState
|
2018-12-04 14:48:39 +00:00
|
|
|
this.states.forEach(
|
2019-02-15 11:03:22 +00:00
|
|
|
state => (state.key === this.currentState ? this.$liveRegionCounter.before(state.$el) : state.$el.detach())
|
2018-11-29 17:39:58 +00:00
|
|
|
);
|
2018-12-13 14:25:22 +00:00
|
|
|
|
2019-01-08 17:30:46 +00:00
|
|
|
// use dialog mode for states which contain more than one form control
|
2019-02-04 15:17:27 +00:00
|
|
|
if (['move-to-existing-folder', 'add-new-template'].indexOf(this.currentState) !== -1) {
|
2019-01-30 19:12:14 +00:00
|
|
|
mode = 'dialog';
|
2019-01-30 11:45:24 +00:00
|
|
|
}
|
2019-02-08 15:22:13 +00:00
|
|
|
|
2020-09-22 11:45:55 +01:00
|
|
|
if (currentStateObj && ('setFocus' in currentStateObj)) {
|
|
|
|
|
scrollTop = $(window).scrollTop();
|
|
|
|
|
currentStateObj.setFocus();
|
|
|
|
|
$(window).scrollTop(scrollTop);
|
|
|
|
|
}
|
2018-11-28 17:40:54 +00:00
|
|
|
};
|
|
|
|
|
|
2019-01-08 15:57:55 +00:00
|
|
|
this.nothingSelectedButtons = $(`
|
2019-01-25 16:47:49 +00:00
|
|
|
<div id="nothing_selected">
|
|
|
|
|
<div class="js-stick-at-bottom-when-scrolling">
|
2023-07-12 15:43:21 -04:00
|
|
|
<div class="usa-button-group">
|
|
|
|
|
<button class="usa-button usa-button--event" value="add-new-template" aria-expanded="false">
|
|
|
|
|
New template
|
|
|
|
|
</button>
|
|
|
|
|
<button class="usa-button usa-button--event" value="add-new-folder" aria-expanded="false">New folder</button>
|
|
|
|
|
</div>
|
2019-02-15 13:49:49 +00:00
|
|
|
<div class="template-list-selected-counter">
|
|
|
|
|
<span class="template-list-selected-counter__count" aria-hidden="true">
|
|
|
|
|
${this.selectionStatus.default}
|
|
|
|
|
</span>
|
2019-01-25 16:47:49 +00:00
|
|
|
</div>
|
2018-12-10 16:53:12 +00:00
|
|
|
</div>
|
2018-11-30 16:29:00 +00:00
|
|
|
</div>
|
2019-01-08 15:57:55 +00:00
|
|
|
`).get(0);
|
2018-11-30 16:29:00 +00:00
|
|
|
|
2019-01-08 15:57:55 +00:00
|
|
|
this.itemsSelectedButtons = $(`
|
2019-01-25 16:47:49 +00:00
|
|
|
<div id="items_selected">
|
|
|
|
|
<div class="js-stick-at-bottom-when-scrolling">
|
2023-07-12 15:43:21 -04:00
|
|
|
<div class="usa-button-group">
|
|
|
|
|
<button class="usa-button usa-button--event" value="move-to-existing-folder" aria-expanded="false">
|
|
|
|
|
Move<span class="usa-sr-only"> selection to folder</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="usa-button usa-button--event" value="move-to-new-folder" aria-expanded="false">Add to new folder</button>
|
|
|
|
|
</div>
|
2019-02-15 11:03:22 +00:00
|
|
|
<div class="template-list-selected-counter" aria-hidden="true">
|
2019-02-15 13:49:49 +00:00
|
|
|
<span class="template-list-selected-counter__count" aria-hidden="true">
|
|
|
|
|
${this.selectionStatus.selected(1)}
|
|
|
|
|
</span>
|
2019-01-25 16:47:49 +00:00
|
|
|
</div>
|
2018-12-10 16:53:12 +00:00
|
|
|
</div>
|
2018-11-30 16:29:00 +00:00
|
|
|
</div>
|
2019-01-08 15:57:55 +00:00
|
|
|
`).get(0);
|
2018-11-28 17:40:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
})(window.GOVUK.Modules);
|