@ministryofjustice/hmpps-digital-prison-reporting-frontend 9.2.0 → 9.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all.bundle.js +878 -824
- package/all.bundle.js.map +1 -1
- package/all.js +878 -824
- package/all.js.map +1 -1
- package/package.json +3 -3
- package/src/dpr/DprSyncLoading.d.ts +1 -0
- package/src/dpr/components/_charts/chart/bar/clientClass.d.ts +9 -6
- package/src/dpr/components/_charts/chart/clientClass.d.ts +24 -14
- package/src/dpr/components/_charts/chart/doughnut/clientClass.d.ts +14 -11
- package/src/dpr/components/_charts/chart/heatmap/clientClass.d.ts +8 -12
- package/src/dpr/components/_charts/chart/line/clientClass.d.ts +11 -7
- package/src/dpr/components/_dashboards/scorecard/clientClass.d.ts +3 -0
- package/src/dpr/components/_inputs/autocomplete-text-input/clientClass.d.ts +15 -13
- package/src/dpr/components/_inputs/date-input/clientClass.d.ts +8 -0
- package/src/dpr/components/_inputs/granular-date-range/clientClass.d.ts +6 -0
- package/src/dpr/components/_reports/report-page/report-template/report-section/report-data-table/clientClass.d.ts +5 -2
package/all.bundle.js
CHANGED
|
@@ -54,31 +54,6 @@ class DprClientClass {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
class CardGroup extends DprClientClass {
|
|
58
|
-
static getModuleName() {
|
|
59
|
-
return 'card-group';
|
|
60
|
-
}
|
|
61
|
-
initialise() {
|
|
62
|
-
const cards = this.getElement().querySelectorAll('[data-click-navigate-to]');
|
|
63
|
-
const wrapperClass = 'card-loading';
|
|
64
|
-
cards.forEach(card => {
|
|
65
|
-
card.addEventListener('click', () => {
|
|
66
|
-
card.classList.add(wrapperClass);
|
|
67
|
-
cards.forEach(c => {
|
|
68
|
-
if (!c.classList.contains('card-loading')) {
|
|
69
|
-
const disabledClass = 'card-disabled';
|
|
70
|
-
c.classList.add(disabledClass);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
const url = card.dataset['clickNavigateTo'];
|
|
74
|
-
if (url) {
|
|
75
|
-
window.location.href = url;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
57
|
var BookmarkAction;
|
|
83
58
|
(function (BookmarkAction) {
|
|
84
59
|
BookmarkAction["ADD"] = "add";
|
|
@@ -182,6 +157,31 @@ class BookmarkButton extends DprClientClass {
|
|
|
182
157
|
}
|
|
183
158
|
}
|
|
184
159
|
|
|
160
|
+
class CardGroup extends DprClientClass {
|
|
161
|
+
static getModuleName() {
|
|
162
|
+
return 'card-group';
|
|
163
|
+
}
|
|
164
|
+
initialise() {
|
|
165
|
+
const cards = this.getElement().querySelectorAll('[data-click-navigate-to]');
|
|
166
|
+
const wrapperClass = 'card-loading';
|
|
167
|
+
cards.forEach(card => {
|
|
168
|
+
card.addEventListener('click', () => {
|
|
169
|
+
card.classList.add(wrapperClass);
|
|
170
|
+
cards.forEach(c => {
|
|
171
|
+
if (!c.classList.contains('card-loading')) {
|
|
172
|
+
const disabledClass = 'card-disabled';
|
|
173
|
+
c.classList.add(disabledClass);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const url = card.dataset['clickNavigateTo'];
|
|
177
|
+
if (url) {
|
|
178
|
+
window.location.href = url;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
185
|
class dprTruncate extends DprClientClass {
|
|
186
186
|
static getModuleName() {
|
|
187
187
|
return 'dpr-truncate';
|
|
@@ -215,27 +215,6 @@ class dprTruncate extends DprClientClass {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
class Pagination {
|
|
219
|
-
pageSizeSelect;
|
|
220
|
-
static getModuleName() {
|
|
221
|
-
return 'pagination';
|
|
222
|
-
}
|
|
223
|
-
initialise() {
|
|
224
|
-
this.pageSizeSelect = document.getElementById('page-size-select');
|
|
225
|
-
this.initPageSizeSelectEvent();
|
|
226
|
-
}
|
|
227
|
-
initPageSizeSelectEvent() {
|
|
228
|
-
this.pageSizeSelect.addEventListener('change', () => {
|
|
229
|
-
const { name, value } = this.pageSizeSelect;
|
|
230
|
-
const queryParams = new URLSearchParams(window.location.search);
|
|
231
|
-
queryParams.set(name, value);
|
|
232
|
-
queryParams.set('selectedPage', '1');
|
|
233
|
-
window.history.replaceState(null, '', `?${queryParams.toString()}`);
|
|
234
|
-
window.location.reload();
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
218
|
class ReportActions extends DprClientClass {
|
|
240
219
|
refreshButton;
|
|
241
220
|
printButton;
|
|
@@ -318,8 +297,44 @@ class ReportActions extends DprClientClass {
|
|
|
318
297
|
}
|
|
319
298
|
}
|
|
320
299
|
|
|
321
|
-
|
|
300
|
+
class DownloadMessage extends DprClientClass {
|
|
301
|
+
downloadMessage;
|
|
302
|
+
static getModuleName() {
|
|
303
|
+
return 'download-message';
|
|
304
|
+
}
|
|
305
|
+
initialise() {
|
|
306
|
+
this.downloadMessage = this.getElement();
|
|
307
|
+
if (window.location.href.includes('download-disabled')) {
|
|
308
|
+
this.downloadMessage.classList.remove('dpr-download-message--hidden');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
class Pagination {
|
|
314
|
+
pageSizeSelect;
|
|
315
|
+
static getModuleName() {
|
|
316
|
+
return 'pagination';
|
|
317
|
+
}
|
|
318
|
+
initialise() {
|
|
319
|
+
this.pageSizeSelect = document.getElementById('page-size-select');
|
|
320
|
+
this.initPageSizeSelectEvent();
|
|
321
|
+
}
|
|
322
|
+
initPageSizeSelectEvent() {
|
|
323
|
+
this.pageSizeSelect.addEventListener('change', () => {
|
|
324
|
+
const { name, value } = this.pageSizeSelect;
|
|
325
|
+
const queryParams = new URLSearchParams(window.location.search);
|
|
326
|
+
queryParams.set(name, value);
|
|
327
|
+
queryParams.set('selectedPage', '1');
|
|
328
|
+
window.history.replaceState(null, '', `?${queryParams.toString()}`);
|
|
329
|
+
window.location.reload();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
322
334
|
class DataTable extends DprClientClass {
|
|
335
|
+
tableContainer = null;
|
|
336
|
+
table = null;
|
|
337
|
+
gradient = null;
|
|
323
338
|
static getModuleName() {
|
|
324
339
|
return 'data-table';
|
|
325
340
|
}
|
|
@@ -336,9 +351,9 @@ class DataTable extends DprClientClass {
|
|
|
336
351
|
window.addEventListener('resize', () => {
|
|
337
352
|
this.checkOffsetWidths();
|
|
338
353
|
});
|
|
339
|
-
this.tableContainer
|
|
340
|
-
const endOfScroll = this.table
|
|
341
|
-
const currentScroll = event.target
|
|
354
|
+
this.tableContainer?.addEventListener('scroll', event => {
|
|
355
|
+
const endOfScroll = this.table?.offsetWidth;
|
|
356
|
+
const currentScroll = event.target?.offsetWidth + event.target.scrollLeft;
|
|
342
357
|
if (endOfScroll === currentScroll) {
|
|
343
358
|
this.removeGradient();
|
|
344
359
|
}
|
|
@@ -347,14 +362,21 @@ class DataTable extends DprClientClass {
|
|
|
347
362
|
}
|
|
348
363
|
});
|
|
349
364
|
}
|
|
350
|
-
removeGradient(
|
|
351
|
-
this.gradient
|
|
365
|
+
removeGradient() {
|
|
366
|
+
if (this.gradient) {
|
|
367
|
+
this.gradient.style.display = 'none';
|
|
368
|
+
}
|
|
352
369
|
}
|
|
353
|
-
addGradient(
|
|
354
|
-
this.gradient
|
|
370
|
+
addGradient() {
|
|
371
|
+
if (this.gradient) {
|
|
372
|
+
this.gradient.style.display = 'block';
|
|
373
|
+
}
|
|
355
374
|
}
|
|
356
375
|
checkOffsetWidths() {
|
|
357
|
-
if (this.tableContainer
|
|
376
|
+
if (!this.tableContainer || !this.table) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (this.tableContainer?.offsetWidth >= this.table?.offsetWidth) {
|
|
358
380
|
this.removeGradient();
|
|
359
381
|
}
|
|
360
382
|
else {
|
|
@@ -382,142 +404,6 @@ class DataTable extends DprClientClass {
|
|
|
382
404
|
}
|
|
383
405
|
}
|
|
384
406
|
|
|
385
|
-
class DownloadMessage extends DprClientClass {
|
|
386
|
-
downloadMessage;
|
|
387
|
-
static getModuleName() {
|
|
388
|
-
return 'download-message';
|
|
389
|
-
}
|
|
390
|
-
initialise() {
|
|
391
|
-
this.downloadMessage = this.getElement();
|
|
392
|
-
if (window.location.href.includes('download-disabled')) {
|
|
393
|
-
this.downloadMessage.classList.remove('dpr-download-message--hidden');
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
class DprFiltersFormClass extends DprClientClass {
|
|
399
|
-
form;
|
|
400
|
-
static getModuleName() {
|
|
401
|
-
return 'filters-form';
|
|
402
|
-
}
|
|
403
|
-
initialise() {
|
|
404
|
-
if (!(this.element instanceof HTMLFormElement)) {
|
|
405
|
-
throw new Error('DprFormQuerySync must be initialised on a form element');
|
|
406
|
-
}
|
|
407
|
-
this.form = this.element;
|
|
408
|
-
this.bindInputEvents();
|
|
409
|
-
}
|
|
410
|
-
// ----------------------------------
|
|
411
|
-
// Events
|
|
412
|
-
// ----------------------------------
|
|
413
|
-
bindInputEvents() {
|
|
414
|
-
this.getAllInputs().forEach(input => {
|
|
415
|
-
input.addEventListener('change', () => {
|
|
416
|
-
const params = new URLSearchParams(window.location.search);
|
|
417
|
-
this.updateQueryFromInput(input, params);
|
|
418
|
-
this.markPreventDefault(params, input.name); // TODO: Check if this is needed ?
|
|
419
|
-
this.resetPagination(params); // TODO: Check if this is needed ?
|
|
420
|
-
this.replaceUrl(params);
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
// ----------------------------------
|
|
425
|
-
// Query updates
|
|
426
|
-
// ----------------------------------
|
|
427
|
-
updateQueryFromInput(input, params) {
|
|
428
|
-
const { name } = input;
|
|
429
|
-
if (!name)
|
|
430
|
-
return;
|
|
431
|
-
if (input instanceof HTMLInputElement) {
|
|
432
|
-
if (input.type === 'checkbox') {
|
|
433
|
-
this.updateCheckbox(params, input);
|
|
434
|
-
}
|
|
435
|
-
else if (input.type === 'radio') {
|
|
436
|
-
this.updateRadio(params, input);
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
this.updateSingle(params, input);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
this.updateSingle(params, input);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
updateCheckbox(params, input) {
|
|
447
|
-
const values = params.getAll(input.name);
|
|
448
|
-
// Remove all existing values for this key
|
|
449
|
-
params.delete(input.name);
|
|
450
|
-
// Re‑add all values except the unchecked one
|
|
451
|
-
values
|
|
452
|
-
.filter(value => value !== input.value || input.checked)
|
|
453
|
-
.forEach(value => {
|
|
454
|
-
params.append(input.name, value);
|
|
455
|
-
});
|
|
456
|
-
// Add the newly checked value
|
|
457
|
-
if (input.checked && !values.includes(input.value)) {
|
|
458
|
-
params.append(input.name, input.value);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
updateRadio(params, input) {
|
|
462
|
-
if (!input.checked)
|
|
463
|
-
return;
|
|
464
|
-
const name = this.normaliseFilterName(input.name);
|
|
465
|
-
params.set(name, input.value);
|
|
466
|
-
}
|
|
467
|
-
updateSingle(params, input) {
|
|
468
|
-
const name = this.normaliseFilterName(input.name);
|
|
469
|
-
let value = input.value;
|
|
470
|
-
if (input instanceof HTMLInputElement && input.dataset['staticOptionNameValue']) {
|
|
471
|
-
value = input.dataset['staticOptionNameValue'];
|
|
472
|
-
}
|
|
473
|
-
const isDateInput = input instanceof HTMLInputElement && input.classList.contains('moj-js-datepicker-input');
|
|
474
|
-
if (isDateInput) {
|
|
475
|
-
const formatted = dayjs(value, 'D/M/YYYY').format('YYYY-MM-DD');
|
|
476
|
-
value = formatted !== 'Invalid Date' ? formatted : '';
|
|
477
|
-
}
|
|
478
|
-
const trimmed = value.trim();
|
|
479
|
-
if (trimmed) {
|
|
480
|
-
params.set(name, trimmed);
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
params.delete(name);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
// ----------------------------------
|
|
487
|
-
// Behaviour flags
|
|
488
|
-
// ----------------------------------
|
|
489
|
-
/**
|
|
490
|
-
* Signals to the server that defaults
|
|
491
|
-
*/
|
|
492
|
-
markPreventDefault(params, changedName) {
|
|
493
|
-
if (changedName !== 'columns') {
|
|
494
|
-
params.set('preventDefault', 'true');
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Reset pagination when filters change
|
|
499
|
-
*/
|
|
500
|
-
resetPagination(params) {
|
|
501
|
-
if (params.has('selectedPage')) {
|
|
502
|
-
params.set('selectedPage', '1');
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
replaceUrl(params) {
|
|
506
|
-
const query = params.toString();
|
|
507
|
-
const url = query ? `?${query}` : window.location.pathname;
|
|
508
|
-
window.history.replaceState(null, '', url);
|
|
509
|
-
}
|
|
510
|
-
// ----------------------------------
|
|
511
|
-
// DOM helpers
|
|
512
|
-
// ----------------------------------
|
|
513
|
-
getAllInputs() {
|
|
514
|
-
return Array.from(this.form.querySelectorAll('input, select, textarea')).filter(el => el.name);
|
|
515
|
-
}
|
|
516
|
-
normaliseFilterName(name) {
|
|
517
|
-
return name.startsWith('label.') ? name.replace(/^label\./, '') : name;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
407
|
dayjs.extend(customParse);
|
|
522
408
|
/**
|
|
523
409
|
* --------------------------------------------
|
|
@@ -822,12 +708,135 @@ class DprSelectedAsyncFilters extends DprClientClass {
|
|
|
822
708
|
}
|
|
823
709
|
}
|
|
824
710
|
|
|
825
|
-
class
|
|
711
|
+
class DprFiltersFormClass extends DprClientClass {
|
|
712
|
+
form;
|
|
826
713
|
static getModuleName() {
|
|
827
|
-
return '
|
|
714
|
+
return 'filters-form';
|
|
828
715
|
}
|
|
829
716
|
initialise() {
|
|
830
|
-
this.
|
|
717
|
+
if (!(this.element instanceof HTMLFormElement)) {
|
|
718
|
+
throw new Error('DprFormQuerySync must be initialised on a form element');
|
|
719
|
+
}
|
|
720
|
+
this.form = this.element;
|
|
721
|
+
this.bindInputEvents();
|
|
722
|
+
}
|
|
723
|
+
// ----------------------------------
|
|
724
|
+
// Events
|
|
725
|
+
// ----------------------------------
|
|
726
|
+
bindInputEvents() {
|
|
727
|
+
this.getAllInputs().forEach(input => {
|
|
728
|
+
input.addEventListener('change', () => {
|
|
729
|
+
const params = new URLSearchParams(window.location.search);
|
|
730
|
+
this.updateQueryFromInput(input, params);
|
|
731
|
+
this.markPreventDefault(params, input.name); // TODO: Check if this is needed ?
|
|
732
|
+
this.resetPagination(params); // TODO: Check if this is needed ?
|
|
733
|
+
this.replaceUrl(params);
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
// ----------------------------------
|
|
738
|
+
// Query updates
|
|
739
|
+
// ----------------------------------
|
|
740
|
+
updateQueryFromInput(input, params) {
|
|
741
|
+
const { name } = input;
|
|
742
|
+
if (!name)
|
|
743
|
+
return;
|
|
744
|
+
if (input instanceof HTMLInputElement) {
|
|
745
|
+
if (input.type === 'checkbox') {
|
|
746
|
+
this.updateCheckbox(params, input);
|
|
747
|
+
}
|
|
748
|
+
else if (input.type === 'radio') {
|
|
749
|
+
this.updateRadio(params, input);
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
this.updateSingle(params, input);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
this.updateSingle(params, input);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
updateCheckbox(params, input) {
|
|
760
|
+
const values = params.getAll(input.name);
|
|
761
|
+
// Remove all existing values for this key
|
|
762
|
+
params.delete(input.name);
|
|
763
|
+
// Re‑add all values except the unchecked one
|
|
764
|
+
values
|
|
765
|
+
.filter(value => value !== input.value || input.checked)
|
|
766
|
+
.forEach(value => {
|
|
767
|
+
params.append(input.name, value);
|
|
768
|
+
});
|
|
769
|
+
// Add the newly checked value
|
|
770
|
+
if (input.checked && !values.includes(input.value)) {
|
|
771
|
+
params.append(input.name, input.value);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
updateRadio(params, input) {
|
|
775
|
+
if (!input.checked)
|
|
776
|
+
return;
|
|
777
|
+
const name = this.normaliseFilterName(input.name);
|
|
778
|
+
params.set(name, input.value);
|
|
779
|
+
}
|
|
780
|
+
updateSingle(params, input) {
|
|
781
|
+
const name = this.normaliseFilterName(input.name);
|
|
782
|
+
let value = input.value;
|
|
783
|
+
if (input instanceof HTMLInputElement && input.dataset['staticOptionNameValue']) {
|
|
784
|
+
value = input.dataset['staticOptionNameValue'];
|
|
785
|
+
}
|
|
786
|
+
const isDateInput = input instanceof HTMLInputElement && input.classList.contains('moj-js-datepicker-input');
|
|
787
|
+
if (isDateInput) {
|
|
788
|
+
const formatted = dayjs(value, 'D/M/YYYY').format('YYYY-MM-DD');
|
|
789
|
+
value = formatted !== 'Invalid Date' ? formatted : '';
|
|
790
|
+
}
|
|
791
|
+
const trimmed = value.trim();
|
|
792
|
+
if (trimmed) {
|
|
793
|
+
params.set(name, trimmed);
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
params.delete(name);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
// ----------------------------------
|
|
800
|
+
// Behaviour flags
|
|
801
|
+
// ----------------------------------
|
|
802
|
+
/**
|
|
803
|
+
* Signals to the server that defaults
|
|
804
|
+
*/
|
|
805
|
+
markPreventDefault(params, changedName) {
|
|
806
|
+
if (changedName !== 'columns') {
|
|
807
|
+
params.set('preventDefault', 'true');
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Reset pagination when filters change
|
|
812
|
+
*/
|
|
813
|
+
resetPagination(params) {
|
|
814
|
+
if (params.has('selectedPage')) {
|
|
815
|
+
params.set('selectedPage', '1');
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
replaceUrl(params) {
|
|
819
|
+
const query = params.toString();
|
|
820
|
+
const url = query ? `?${query}` : window.location.pathname;
|
|
821
|
+
window.history.replaceState(null, '', url);
|
|
822
|
+
}
|
|
823
|
+
// ----------------------------------
|
|
824
|
+
// DOM helpers
|
|
825
|
+
// ----------------------------------
|
|
826
|
+
getAllInputs() {
|
|
827
|
+
return Array.from(this.form.querySelectorAll('input, select, textarea')).filter(el => el.name);
|
|
828
|
+
}
|
|
829
|
+
normaliseFilterName(name) {
|
|
830
|
+
return name.startsWith('label.') ? name.replace(/^label\./, '') : name;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
class DprAppliedFilters extends DprClientClass {
|
|
835
|
+
static getModuleName() {
|
|
836
|
+
return 'dpr-applied-filters';
|
|
837
|
+
}
|
|
838
|
+
initialise() {
|
|
839
|
+
this.bindRemoveEvents();
|
|
831
840
|
}
|
|
832
841
|
// ----------------------------------
|
|
833
842
|
// Event binding
|
|
@@ -1033,9 +1042,9 @@ class DprReportStatus extends PollingClientClass {
|
|
|
1033
1042
|
}
|
|
1034
1043
|
}
|
|
1035
1044
|
|
|
1036
|
-
// @ts-nocheck
|
|
1037
1045
|
/* eslint-disable class-methods-use-this */
|
|
1038
1046
|
class DprSyncLoading extends DprClientClass {
|
|
1047
|
+
form = null;
|
|
1039
1048
|
static getModuleName() {
|
|
1040
1049
|
return 'sync-loading';
|
|
1041
1050
|
}
|
|
@@ -1045,96 +1054,345 @@ class DprSyncLoading extends DprClientClass {
|
|
|
1045
1054
|
this.load();
|
|
1046
1055
|
}
|
|
1047
1056
|
async load() {
|
|
1048
|
-
this.form
|
|
1057
|
+
this.form?.submit();
|
|
1049
1058
|
}
|
|
1050
1059
|
}
|
|
1051
1060
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1061
|
+
class AutoCompleteMulti extends DprClientClass {
|
|
1062
|
+
filterId;
|
|
1063
|
+
searchInput;
|
|
1064
|
+
searchInputValue;
|
|
1065
|
+
multiselectOptions;
|
|
1054
1066
|
static getModuleName() {
|
|
1055
|
-
return '
|
|
1067
|
+
return 'autocomplete-multiselect-input';
|
|
1056
1068
|
}
|
|
1057
1069
|
initialise() {
|
|
1058
|
-
|
|
1059
|
-
this.
|
|
1060
|
-
|
|
1061
|
-
this.
|
|
1062
|
-
this.
|
|
1063
|
-
this.
|
|
1064
|
-
|
|
1065
|
-
this.
|
|
1066
|
-
this.
|
|
1067
|
-
this.setValidationOnInputEl();
|
|
1068
|
-
this.setMinMaxEventListener();
|
|
1069
|
-
this.setToMinMax();
|
|
1070
|
-
this.setToValue();
|
|
1070
|
+
this.element = this.getElement();
|
|
1071
|
+
this.filterId = this.element.getAttribute('data-filter-id');
|
|
1072
|
+
// Search input
|
|
1073
|
+
this.searchInput = document.getElementById(`search.${this.filterId}`);
|
|
1074
|
+
this.multiselectOptions = Array.from(this.element.querySelectorAll('.govuk-checkboxes__input'));
|
|
1075
|
+
if (!this.searchInput || !this.multiselectOptions)
|
|
1076
|
+
return;
|
|
1077
|
+
this.initialiseCheckboxes();
|
|
1078
|
+
this.initSearchInputAction();
|
|
1071
1079
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
this.dateInput.setAttribute('max', this.max);
|
|
1080
|
-
this.dateInput.setAttribute('display-name', this.displayName);
|
|
1081
|
-
this.dateInput.setAttribute('pattern', this.pattern);
|
|
1082
|
-
this.dateInput.setAttribute('pattern-hint', this.patternHint);
|
|
1080
|
+
initialiseCheckboxes() {
|
|
1081
|
+
this.multiselectOptions.forEach(input => {
|
|
1082
|
+
const wrapper = input.closest('.govuk-checkboxes__item');
|
|
1083
|
+
if (!wrapper)
|
|
1084
|
+
return;
|
|
1085
|
+
wrapper.classList.toggle('dpr-form--hidden', !input.checked);
|
|
1086
|
+
});
|
|
1083
1087
|
}
|
|
1084
|
-
|
|
1085
|
-
this.
|
|
1086
|
-
|
|
1088
|
+
initSearchInputAction() {
|
|
1089
|
+
if (!this.searchInput)
|
|
1090
|
+
return;
|
|
1091
|
+
const input = this.searchInput;
|
|
1092
|
+
this.searchInput.addEventListener('keyup', _event => {
|
|
1093
|
+
this.searchInputValue = input.value;
|
|
1094
|
+
this.updateCheckboxes();
|
|
1087
1095
|
});
|
|
1088
1096
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
+
updateCheckboxes() {
|
|
1098
|
+
const query = this.searchInputValue.toLowerCase().trim();
|
|
1099
|
+
const minLength = 3;
|
|
1100
|
+
this.multiselectOptions.forEach(input => {
|
|
1101
|
+
const wrapper = input.closest('.govuk-checkboxes__item');
|
|
1102
|
+
if (!wrapper)
|
|
1103
|
+
return;
|
|
1104
|
+
// Always show selected items
|
|
1105
|
+
if (input.checked) {
|
|
1106
|
+
wrapper.classList.remove('dpr-form--hidden');
|
|
1107
|
+
return;
|
|
1097
1108
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1109
|
+
// Below threshold - hide unselected
|
|
1110
|
+
if (query.length < minLength) {
|
|
1111
|
+
wrapper.classList.add('dpr-form--hidden');
|
|
1112
|
+
return;
|
|
1103
1113
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1114
|
+
const labelText = input.labels?.[0]?.innerText.toLowerCase() ?? '';
|
|
1115
|
+
const matches = labelText.includes(query);
|
|
1116
|
+
wrapper.classList.toggle('dpr-form--hidden', !matches);
|
|
1117
|
+
});
|
|
1107
1118
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/* eslint-disable class-methods-use-this */
|
|
1122
|
+
class Autocomplete extends DprClientClass {
|
|
1123
|
+
listItemsSelector;
|
|
1124
|
+
listParentSelector;
|
|
1125
|
+
static getModuleName() {
|
|
1126
|
+
return 'autocomplete-text-input';
|
|
1127
|
+
}
|
|
1128
|
+
constructor(element) {
|
|
1129
|
+
super(element);
|
|
1130
|
+
const listId = this.getTextInput()?.getAttribute('aria-owns');
|
|
1131
|
+
this.listItemsSelector = `#${listId} li`;
|
|
1132
|
+
this.listParentSelector = `#${listId} ul`;
|
|
1133
|
+
}
|
|
1134
|
+
initialise() {
|
|
1135
|
+
const textInput = this.getTextInput();
|
|
1136
|
+
textInput?.addEventListener('keyup', event => {
|
|
1137
|
+
this.onTextInput(event, textInput);
|
|
1138
|
+
});
|
|
1139
|
+
textInput?.addEventListener('keypress', e => {
|
|
1140
|
+
if (e.key === 'Enter') {
|
|
1141
|
+
e.stopPropagation();
|
|
1111
1142
|
e.preventDefault();
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
textInput?.addEventListener('input', () => {
|
|
1146
|
+
if (textInput.value !== '') {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
const hiddenInput = this.getHiddenInput();
|
|
1150
|
+
if (hiddenInput) {
|
|
1151
|
+
hiddenInput.value = '';
|
|
1152
|
+
hiddenInput.disabled = true;
|
|
1153
|
+
}
|
|
1154
|
+
delete textInput.dataset['staticOptionNameValue'];
|
|
1155
|
+
textInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1156
|
+
});
|
|
1157
|
+
this.getElement()
|
|
1158
|
+
.querySelectorAll('.autocomplete-text-input-list-button')
|
|
1159
|
+
.forEach(button => {
|
|
1160
|
+
button.addEventListener('mousedown', event => {
|
|
1161
|
+
this.onOptionClick(event, textInput, this.getElement());
|
|
1118
1162
|
});
|
|
1119
1163
|
});
|
|
1164
|
+
this.initialiseDefaultValue(textInput);
|
|
1165
|
+
}
|
|
1166
|
+
initialiseDefaultValue(textInput) {
|
|
1167
|
+
const hiddenInput = this.getHiddenInput();
|
|
1168
|
+
if (hiddenInput?.value) {
|
|
1169
|
+
hiddenInput.disabled = false;
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
if (textInput) {
|
|
1173
|
+
textInput.value = '';
|
|
1174
|
+
}
|
|
1175
|
+
delete textInput?.dataset['staticOptionNameValue'];
|
|
1176
|
+
if (hiddenInput) {
|
|
1177
|
+
hiddenInput.value = '';
|
|
1178
|
+
hiddenInput.disabled = true;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
getTextInput() {
|
|
1182
|
+
return this.getElement().querySelector('.autocomplete-text-input-box');
|
|
1183
|
+
}
|
|
1184
|
+
onTextInput(event, textInput) {
|
|
1185
|
+
const minLength = Number(textInput.dataset['minimumLength']);
|
|
1186
|
+
const { resourceEndpoint } = textInput.dataset;
|
|
1187
|
+
const searchValue = event.target.value.toLowerCase();
|
|
1188
|
+
if (resourceEndpoint) {
|
|
1189
|
+
if (searchValue.length >= minLength) {
|
|
1190
|
+
this.addItem(this.clearListAndRecreateTemplate(), '<i>Searching...</i>');
|
|
1191
|
+
this.populateOptionsDynamically(resourceEndpoint, searchValue, textInput, () => this.clearListAndRecreateTemplate());
|
|
1192
|
+
}
|
|
1193
|
+
else {
|
|
1194
|
+
this.clearListAndRecreateTemplate();
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
this.getElement()
|
|
1199
|
+
.querySelectorAll(this.listItemsSelector)
|
|
1200
|
+
.forEach(item => {
|
|
1201
|
+
if (searchValue.length >= minLength &&
|
|
1202
|
+
this.isMatchingStaticOptionNameOrDisplayPrefix(this.getInputListButton(item), searchValue, item)) {
|
|
1203
|
+
item.classList.remove('autocomplete-text-input-item-hide');
|
|
1204
|
+
}
|
|
1205
|
+
else {
|
|
1206
|
+
item.classList.add('autocomplete-text-input-item-hide');
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
if (searchValue.length === 0) {
|
|
1211
|
+
const changeEvent = new Event('change');
|
|
1212
|
+
textInput.dispatchEvent(changeEvent);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
getInputListButton(item) {
|
|
1216
|
+
return item.querySelector('.autocomplete-text-input-list-button');
|
|
1217
|
+
}
|
|
1218
|
+
isMatchingStaticOptionNameOrDisplayPrefix(inputListButton, searchValue, item) {
|
|
1219
|
+
return (this.isStaticOptionsNamePrefix(inputListButton?.dataset['staticOptionNameValue'], searchValue) ||
|
|
1220
|
+
item.innerText.trim().toLowerCase().startsWith(searchValue));
|
|
1221
|
+
}
|
|
1222
|
+
isStaticOptionsNamePrefix(staticOptionNameValue, searchValue) {
|
|
1223
|
+
return staticOptionNameValue && staticOptionNameValue.trim().toLowerCase().startsWith(searchValue);
|
|
1224
|
+
}
|
|
1225
|
+
async populateOptionsDynamically(resourceEndpoint, searchValue, textInput, templateProvider) {
|
|
1226
|
+
try {
|
|
1227
|
+
const response = await fetch(resourceEndpoint.replace('{prefix}', encodeURI(searchValue)));
|
|
1228
|
+
const results = await response.json();
|
|
1229
|
+
if (searchValue === textInput?.value.toLowerCase()) {
|
|
1230
|
+
const template = templateProvider();
|
|
1231
|
+
results.forEach((result) => {
|
|
1232
|
+
this.addItem(template, result, event => {
|
|
1233
|
+
this.onOptionClick(event, textInput, this.getElement());
|
|
1234
|
+
});
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
catch (error) {
|
|
1239
|
+
this.addItem(templateProvider(), `Failed to retrieve results: ${error}`);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
onOptionClick(event, textInput, topLevelElement) {
|
|
1243
|
+
event.preventDefault();
|
|
1244
|
+
const button = event.currentTarget?.closest('button');
|
|
1245
|
+
const hiddenInput = this.getHiddenInput();
|
|
1246
|
+
const displayValue = button?.innerText.trim();
|
|
1247
|
+
const actualValue = button?.dataset['staticOptionNameValue'] || '';
|
|
1248
|
+
// UI Display Value
|
|
1249
|
+
if (textInput) {
|
|
1250
|
+
this.setValue(textInput, displayValue);
|
|
1251
|
+
textInput.dataset['staticOptionNameValue'] = actualValue;
|
|
1252
|
+
}
|
|
1253
|
+
// submission value
|
|
1254
|
+
if (hiddenInput) {
|
|
1255
|
+
hiddenInput.value = actualValue;
|
|
1256
|
+
hiddenInput.disabled = false;
|
|
1257
|
+
}
|
|
1258
|
+
topLevelElement.querySelectorAll('li').forEach(item => {
|
|
1259
|
+
item.classList.add('autocomplete-text-input-item-hide');
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
setValue(textInput, displayValue) {
|
|
1263
|
+
if (displayValue) {
|
|
1264
|
+
textInput.value = displayValue;
|
|
1265
|
+
}
|
|
1266
|
+
textInput.focus();
|
|
1267
|
+
textInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1268
|
+
}
|
|
1269
|
+
addItem(template, content, clickEvent) {
|
|
1270
|
+
const item = template?.cloneNode(true);
|
|
1271
|
+
const button = item?.querySelector('button');
|
|
1272
|
+
if (button) {
|
|
1273
|
+
button.innerHTML = content;
|
|
1274
|
+
}
|
|
1275
|
+
item.classList.remove('autocomplete-text-input-item-hide');
|
|
1276
|
+
this.getElement().querySelector(this.listParentSelector)?.appendChild(item);
|
|
1277
|
+
if (clickEvent) {
|
|
1278
|
+
item.addEventListener('mousedown', (event) => {
|
|
1279
|
+
clickEvent(event);
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
clearListAndRecreateTemplate() {
|
|
1284
|
+
const template = this.getElement()
|
|
1285
|
+
.querySelector(this.listItemsSelector)
|
|
1286
|
+
?.cloneNode(true);
|
|
1287
|
+
template?.classList.add('autocomplete-text-input-item-hide');
|
|
1288
|
+
this.getElement()
|
|
1289
|
+
.querySelectorAll(this.listItemsSelector)
|
|
1290
|
+
.forEach(e => e.remove());
|
|
1291
|
+
if (template) {
|
|
1292
|
+
this.getElement().querySelector(this.listParentSelector)?.append(template);
|
|
1293
|
+
}
|
|
1294
|
+
return template;
|
|
1295
|
+
}
|
|
1296
|
+
getHiddenInput() {
|
|
1297
|
+
return this.getElement().querySelector('[data-autocomplete-hidden]');
|
|
1120
1298
|
}
|
|
1121
1299
|
}
|
|
1122
1300
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1301
|
+
class DateInput extends DprClientClass {
|
|
1302
|
+
dateInput = null;
|
|
1303
|
+
required = null;
|
|
1304
|
+
displayName = null;
|
|
1305
|
+
pattern = null;
|
|
1306
|
+
patternHint = null;
|
|
1307
|
+
min = null;
|
|
1308
|
+
max = null;
|
|
1309
|
+
setToValueTriggers = null;
|
|
1310
|
+
static getModuleName() {
|
|
1311
|
+
return 'date-input';
|
|
1312
|
+
}
|
|
1313
|
+
initialise() {
|
|
1314
|
+
const element = this.getElement();
|
|
1315
|
+
this.dateInput = element.querySelector(`input.moj-js-datepicker-input`);
|
|
1316
|
+
this.setToValueTriggers = document.querySelectorAll(`[data-set-min-max-trigger='true']`);
|
|
1317
|
+
this.required = this.getElement().getAttribute('data-required');
|
|
1318
|
+
this.displayName = this.getElement().getAttribute('data-display-name');
|
|
1319
|
+
this.pattern = this.getElement().getAttribute('data-pattern');
|
|
1320
|
+
this.patternHint = this.getElement().getAttribute('data-pattern-hint');
|
|
1321
|
+
this.min = this.getElement().getAttribute('data-min');
|
|
1322
|
+
this.max = this.getElement().getAttribute('data-max');
|
|
1323
|
+
this.setValidationOnInputEl();
|
|
1324
|
+
this.setMinMaxEventListener();
|
|
1325
|
+
this.setToMinMax();
|
|
1326
|
+
this.setToValue();
|
|
1327
|
+
}
|
|
1328
|
+
setValidationOnInputEl() {
|
|
1329
|
+
if (this.required && this.required === 'true') {
|
|
1330
|
+
this.dateInput?.setAttribute('required', 'true');
|
|
1331
|
+
}
|
|
1332
|
+
if (this.min)
|
|
1333
|
+
this.dateInput?.setAttribute('min', this.min);
|
|
1334
|
+
if (this.max)
|
|
1335
|
+
this.dateInput?.setAttribute('max', this.max);
|
|
1336
|
+
this.displayName && this.dateInput?.setAttribute('display-name', this.displayName);
|
|
1337
|
+
this.pattern && this.dateInput?.setAttribute('pattern', this.pattern);
|
|
1338
|
+
this.patternHint && this.dateInput?.setAttribute('pattern-hint', this.patternHint);
|
|
1339
|
+
}
|
|
1340
|
+
setMinMaxEventListener() {
|
|
1341
|
+
this.dateInput?.addEventListener('blur', () => {
|
|
1342
|
+
this.setToMinMax();
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
setToMinMax() {
|
|
1346
|
+
if (this.dateInput?.value) {
|
|
1347
|
+
const dateValue = new Date(this.dateInput.value);
|
|
1348
|
+
if (this.min) {
|
|
1349
|
+
const minDate = new Date(this.min);
|
|
1350
|
+
if (dateValue < minDate) {
|
|
1351
|
+
this.dateInput.value = dayjs(this.min).format('DD/MM/YYYY');
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
if (this.max) {
|
|
1355
|
+
const maxDate = new Date(this.max);
|
|
1356
|
+
if (dateValue > maxDate) {
|
|
1357
|
+
this.dateInput.value = dayjs(this.max).format('DD/MM/YYYY');
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
const changeEvent = new Event('change');
|
|
1362
|
+
this.dateInput?.dispatchEvent(changeEvent);
|
|
1363
|
+
}
|
|
1364
|
+
setToValue() {
|
|
1365
|
+
this.setToValueTriggers?.forEach(set => {
|
|
1366
|
+
set.addEventListener('click', e => {
|
|
1367
|
+
e.preventDefault();
|
|
1368
|
+
const value = e.target?.getAttribute('data-set-min-max-value');
|
|
1369
|
+
const inputId = e.target?.getAttribute('data-set-to-input') || '';
|
|
1370
|
+
const input = document.getElementById(inputId);
|
|
1371
|
+
if (input && value) {
|
|
1372
|
+
input.value = value;
|
|
1373
|
+
const changeEvent = new Event('change');
|
|
1374
|
+
input?.dispatchEvent(changeEvent);
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
var isBetween$2 = {exports: {}};
|
|
1382
|
+
|
|
1383
|
+
var isBetween$1 = isBetween$2.exports;
|
|
1384
|
+
|
|
1385
|
+
var hasRequiredIsBetween;
|
|
1386
|
+
|
|
1387
|
+
function requireIsBetween () {
|
|
1388
|
+
if (hasRequiredIsBetween) return isBetween$2.exports;
|
|
1389
|
+
hasRequiredIsBetween = 1;
|
|
1390
|
+
(function (module, exports) {
|
|
1391
|
+
!function(e,i){module.exports=i();}(isBetween$1,(function(){return function(e,i,t){i.prototype.isBetween=function(e,i,s,f){var n=t(e),o=t(i),r="("===(f=f||"()")[0],u=")"===f[1];return (r?this.isAfter(n,s):!this.isBefore(n,s))&&(u?this.isBefore(o,s):!this.isAfter(o,s))||(r?this.isBefore(n,s):!this.isAfter(n,s))&&(u?this.isAfter(o,s):!this.isBefore(o,s))};}}));
|
|
1392
|
+
} (isBetween$2));
|
|
1393
|
+
return isBetween$2.exports;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1138
1396
|
var isBetweenExports = requireIsBetween();
|
|
1139
1397
|
var isBetween = /*@__PURE__*/getDefaultExportFromCjs(isBetweenExports);
|
|
1140
1398
|
|
|
@@ -1289,181 +1547,18 @@ class DateRangeInput extends DprClientClass {
|
|
|
1289
1547
|
}
|
|
1290
1548
|
}
|
|
1291
1549
|
|
|
1292
|
-
// @ts-nocheck
|
|
1293
|
-
/* eslint-disable class-methods-use-this */
|
|
1294
|
-
class Autocomplete extends DprClientClass {
|
|
1295
|
-
static getModuleName() {
|
|
1296
|
-
return 'autocomplete-text-input';
|
|
1297
|
-
}
|
|
1298
|
-
constructor(element) {
|
|
1299
|
-
super(element);
|
|
1300
|
-
const listId = this.getTextInput().getAttribute('aria-owns');
|
|
1301
|
-
this.listItemsSelector = `#${listId} li`;
|
|
1302
|
-
this.listParentSelector = `#${listId} ul`;
|
|
1303
|
-
}
|
|
1304
|
-
initialise() {
|
|
1305
|
-
const textInput = this.getTextInput();
|
|
1306
|
-
textInput.addEventListener('keyup', event => {
|
|
1307
|
-
this.onTextInput(event, textInput);
|
|
1308
|
-
});
|
|
1309
|
-
textInput.addEventListener('keypress', e => {
|
|
1310
|
-
if (e.key === 'Enter') {
|
|
1311
|
-
e.stopPropagation();
|
|
1312
|
-
e.preventDefault();
|
|
1313
|
-
}
|
|
1314
|
-
});
|
|
1315
|
-
textInput.addEventListener('input', () => {
|
|
1316
|
-
if (textInput.value !== '') {
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
const hiddenInput = this.getHiddenInput();
|
|
1320
|
-
if (hiddenInput) {
|
|
1321
|
-
hiddenInput.value = '';
|
|
1322
|
-
hiddenInput.disabled = true;
|
|
1323
|
-
}
|
|
1324
|
-
delete textInput.dataset.staticOptionNameValue;
|
|
1325
|
-
textInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1326
|
-
});
|
|
1327
|
-
this.getElement()
|
|
1328
|
-
.querySelectorAll('.autocomplete-text-input-list-button')
|
|
1329
|
-
.forEach(button => {
|
|
1330
|
-
button.addEventListener('mousedown', event => {
|
|
1331
|
-
this.onOptionClick(event, textInput, this.getElement());
|
|
1332
|
-
});
|
|
1333
|
-
});
|
|
1334
|
-
this.initialiseDefaultValue(textInput);
|
|
1335
|
-
}
|
|
1336
|
-
initialiseDefaultValue(textInput) {
|
|
1337
|
-
const hiddenInput = this.getHiddenInput();
|
|
1338
|
-
if (hiddenInput?.value) {
|
|
1339
|
-
hiddenInput.disabled = false;
|
|
1340
|
-
return;
|
|
1341
|
-
}
|
|
1342
|
-
textInput.value = '';
|
|
1343
|
-
delete textInput.dataset.staticOptionNameValue;
|
|
1344
|
-
if (hiddenInput) {
|
|
1345
|
-
hiddenInput.value = '';
|
|
1346
|
-
hiddenInput.disabled = true;
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
getTextInput() {
|
|
1350
|
-
return this.getElement().querySelector('.autocomplete-text-input-box');
|
|
1351
|
-
}
|
|
1352
|
-
onTextInput(event, textInput) {
|
|
1353
|
-
const minLength = Number(textInput.dataset.minimumLength);
|
|
1354
|
-
const { resourceEndpoint } = textInput.dataset;
|
|
1355
|
-
const searchValue = event.target.value.toLowerCase();
|
|
1356
|
-
if (resourceEndpoint) {
|
|
1357
|
-
if (searchValue.length >= minLength) {
|
|
1358
|
-
this.addItem(this.clearListAndRecreateTemplate(), '<i>Searching...</i>');
|
|
1359
|
-
this.populateOptionsDynamically(resourceEndpoint, searchValue, textInput, () => this.clearListAndRecreateTemplate());
|
|
1360
|
-
}
|
|
1361
|
-
else {
|
|
1362
|
-
this.clearListAndRecreateTemplate();
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
else {
|
|
1366
|
-
this.getElement()
|
|
1367
|
-
.querySelectorAll(this.listItemsSelector)
|
|
1368
|
-
.forEach(item => {
|
|
1369
|
-
if (searchValue.length >= minLength &&
|
|
1370
|
-
this.isMatchingStaticOptionNameOrDisplayPrefix(this.getInputListButton(item), searchValue, item)) {
|
|
1371
|
-
item.classList.remove('autocomplete-text-input-item-hide');
|
|
1372
|
-
}
|
|
1373
|
-
else {
|
|
1374
|
-
item.classList.add('autocomplete-text-input-item-hide');
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
if (searchValue.length === 0) {
|
|
1379
|
-
const changeEvent = new Event('change');
|
|
1380
|
-
textInput.dispatchEvent(changeEvent);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
getInputListButton(item) {
|
|
1384
|
-
return item.querySelector('.autocomplete-text-input-list-button');
|
|
1385
|
-
}
|
|
1386
|
-
isMatchingStaticOptionNameOrDisplayPrefix(inputListButton, searchValue, item) {
|
|
1387
|
-
return (this.isStaticOptionsNamePrefix(inputListButton.dataset.staticOptionNameValue, searchValue) ||
|
|
1388
|
-
item.innerText.trim().toLowerCase().startsWith(searchValue));
|
|
1389
|
-
}
|
|
1390
|
-
isStaticOptionsNamePrefix(staticOptionNameValue, searchValue) {
|
|
1391
|
-
return staticOptionNameValue && staticOptionNameValue.trim().toLowerCase().startsWith(searchValue);
|
|
1392
|
-
}
|
|
1393
|
-
async populateOptionsDynamically(resourceEndpoint, searchValue, textInput, templateProvider) {
|
|
1394
|
-
try {
|
|
1395
|
-
const response = await fetch(resourceEndpoint.replace('{prefix}', encodeURI(searchValue)));
|
|
1396
|
-
const results = await response.json();
|
|
1397
|
-
if (searchValue === textInput.value.toLowerCase()) {
|
|
1398
|
-
const template = templateProvider();
|
|
1399
|
-
results.forEach(r => {
|
|
1400
|
-
this.addItem(template, r, event => {
|
|
1401
|
-
this.onOptionClick(event, textInput, this.getElement());
|
|
1402
|
-
});
|
|
1403
|
-
});
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
catch (error) {
|
|
1407
|
-
this.addItem(templateProvider(), `Failed to retrieve results: ${error}`);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
onOptionClick(event, textInput, topLevelElement) {
|
|
1411
|
-
event.preventDefault();
|
|
1412
|
-
const button = event.currentTarget.closest('button');
|
|
1413
|
-
const hiddenInput = this.getHiddenInput();
|
|
1414
|
-
const displayValue = button.innerText.trim();
|
|
1415
|
-
const actualValue = button.dataset.staticOptionNameValue || '';
|
|
1416
|
-
// UI Display Value
|
|
1417
|
-
textInput.value = displayValue;
|
|
1418
|
-
// submission value
|
|
1419
|
-
if (hiddenInput) {
|
|
1420
|
-
hiddenInput.value = actualValue;
|
|
1421
|
-
hiddenInput.disabled = false;
|
|
1422
|
-
}
|
|
1423
|
-
textInput.dataset.staticOptionNameValue = actualValue;
|
|
1424
|
-
topLevelElement.querySelectorAll('li').forEach(item => {
|
|
1425
|
-
item.classList.add('autocomplete-text-input-item-hide');
|
|
1426
|
-
});
|
|
1427
|
-
textInput.focus();
|
|
1428
|
-
textInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1429
|
-
}
|
|
1430
|
-
setValue(textInput, displayValue) {
|
|
1431
|
-
textInput.value = displayValue;
|
|
1432
|
-
textInput.focus();
|
|
1433
|
-
textInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1434
|
-
}
|
|
1435
|
-
addItem(template, content, clickEvent) {
|
|
1436
|
-
const item = template.cloneNode(true);
|
|
1437
|
-
item.querySelector('button').innerHTML = content;
|
|
1438
|
-
item.classList.remove('autocomplete-text-input-item-hide');
|
|
1439
|
-
this.getElement().querySelector(this.listParentSelector).appendChild(item);
|
|
1440
|
-
if (clickEvent) {
|
|
1441
|
-
item.addEventListener('mousedown', event => {
|
|
1442
|
-
clickEvent(event);
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
clearListAndRecreateTemplate() {
|
|
1447
|
-
const template = this.getElement().querySelector(this.listItemsSelector).cloneNode(true);
|
|
1448
|
-
template.classList.add('autocomplete-text-input-item-hide');
|
|
1449
|
-
this.getElement()
|
|
1450
|
-
.querySelectorAll(this.listItemsSelector)
|
|
1451
|
-
.forEach(e => e.remove());
|
|
1452
|
-
this.getElement().querySelector(this.listParentSelector).append(template);
|
|
1453
|
-
return template;
|
|
1454
|
-
}
|
|
1455
|
-
getHiddenInput() {
|
|
1456
|
-
return this.getElement().querySelector('[data-autocomplete-hidden]');
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
// @ts-nocheck
|
|
1461
1550
|
class GranularDateRange extends DprClientClass {
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1551
|
+
filter = null;
|
|
1552
|
+
quickFiltersInput = null;
|
|
1553
|
+
granularityInput = null;
|
|
1554
|
+
startInput = null;
|
|
1555
|
+
endInput = null;
|
|
1556
|
+
currentQuickFilterValue = '';
|
|
1557
|
+
fieldName = null;
|
|
1558
|
+
idPrefix = '';
|
|
1559
|
+
currentStartInputValue = '';
|
|
1560
|
+
currentEndInputValue = '';
|
|
1561
|
+
currentGranularityValue = '';
|
|
1467
1562
|
static getModuleName() {
|
|
1468
1563
|
return 'granular-date-range-input';
|
|
1469
1564
|
}
|
|
@@ -1475,15 +1570,15 @@ class GranularDateRange extends DprClientClass {
|
|
|
1475
1570
|
this.granularityInput = this.filter.querySelector(`select[name='${this.idPrefix}.granularity']`);
|
|
1476
1571
|
this.startInput = this.filter.querySelector(`input[name='${this.idPrefix}.start']`);
|
|
1477
1572
|
this.endInput = this.filter.querySelector(`input[name='${this.idPrefix}.end']`);
|
|
1478
|
-
this.currentStartInputValue = this.startInput
|
|
1479
|
-
this.currentEndInputValue = this.endInput
|
|
1480
|
-
this.currentQuickFilterValue = this.quickFiltersInput
|
|
1481
|
-
this.currentGranularityValue = this.granularityInput
|
|
1573
|
+
this.currentStartInputValue = this.startInput?.value || '';
|
|
1574
|
+
this.currentEndInputValue = this.endInput?.value || '';
|
|
1575
|
+
this.currentQuickFilterValue = this.quickFiltersInput?.value || '';
|
|
1576
|
+
this.currentGranularityValue = this.granularityInput?.value || '';
|
|
1482
1577
|
this.initChangeEvents();
|
|
1483
1578
|
}
|
|
1484
1579
|
initChangeEvents() {
|
|
1485
1580
|
[this.granularityInput, this.quickFiltersInput, this.startInput, this.endInput].forEach(el => {
|
|
1486
|
-
el
|
|
1581
|
+
el?.addEventListener('change', event => {
|
|
1487
1582
|
this.resolveStateChange(event);
|
|
1488
1583
|
});
|
|
1489
1584
|
});
|
|
@@ -1492,22 +1587,22 @@ class GranularDateRange extends DprClientClass {
|
|
|
1492
1587
|
const target = event.target;
|
|
1493
1588
|
const value = target.value;
|
|
1494
1589
|
switch (target.id) {
|
|
1495
|
-
case this.quickFiltersInput
|
|
1590
|
+
case this.quickFiltersInput?.id: {
|
|
1496
1591
|
this.currentQuickFilterValue = value;
|
|
1497
1592
|
const { granularity, startDate, endDate } = this.calculateStartEndGranularity(value);
|
|
1498
|
-
this.granularityInput.value = granularity;
|
|
1499
|
-
this.startInput.value = startDate.format('DD/MM/YYYY').toString();
|
|
1500
|
-
this.endInput.value = endDate.format('DD/MM/YYYY').toString();
|
|
1593
|
+
this.granularityInput && (this.granularityInput.value = granularity);
|
|
1594
|
+
this.startInput && (this.startInput.value = startDate.format('DD/MM/YYYY').toString());
|
|
1595
|
+
this.endInput && (this.endInput.value = endDate.format('DD/MM/YYYY').toString());
|
|
1501
1596
|
break;
|
|
1502
1597
|
}
|
|
1503
|
-
case this.granularityInput
|
|
1598
|
+
case this.granularityInput?.id: {
|
|
1504
1599
|
if (this.shouldResetQuickFilters(event)) {
|
|
1505
1600
|
this.resetQuickFiltersToNone();
|
|
1506
1601
|
}
|
|
1507
1602
|
break;
|
|
1508
1603
|
}
|
|
1509
|
-
case this.startInput
|
|
1510
|
-
case this.endInput
|
|
1604
|
+
case this.startInput?.id:
|
|
1605
|
+
case this.endInput?.id: {
|
|
1511
1606
|
this.resetQuickFiltersToNone();
|
|
1512
1607
|
break;
|
|
1513
1608
|
}
|
|
@@ -1519,14 +1614,14 @@ class GranularDateRange extends DprClientClass {
|
|
|
1519
1614
|
this.updateQueryParams();
|
|
1520
1615
|
}
|
|
1521
1616
|
resetQuickFiltersToNone() {
|
|
1522
|
-
this.quickFiltersInput.value = 'none';
|
|
1617
|
+
this.quickFiltersInput && (this.quickFiltersInput.value = 'none');
|
|
1523
1618
|
}
|
|
1524
1619
|
updateQueryParams() {
|
|
1525
1620
|
const queryParams = new URLSearchParams(window.location.search);
|
|
1526
|
-
queryParams.set(this.granularityInput.id, this.granularityInput.value);
|
|
1527
|
-
queryParams.set(this.quickFiltersInput.id, this.quickFiltersInput.value);
|
|
1528
|
-
queryParams.set(this.startInput.id, this.startInput.value);
|
|
1529
|
-
queryParams.set(this.endInput.id, this.endInput.value);
|
|
1621
|
+
this.granularityInput && queryParams.set(this.granularityInput.id, this.granularityInput.value);
|
|
1622
|
+
this.quickFiltersInput && queryParams.set(this.quickFiltersInput.id, this.quickFiltersInput.value);
|
|
1623
|
+
this.startInput && queryParams.set(this.startInput.id, this.startInput.value);
|
|
1624
|
+
this.endInput && queryParams.set(this.endInput.id, this.endInput.value);
|
|
1530
1625
|
window.history.replaceState(null, '', `?${queryParams.toString()}`);
|
|
1531
1626
|
}
|
|
1532
1627
|
shouldResetQuickFilters(e) {
|
|
@@ -1540,15 +1635,15 @@ class GranularDateRange extends DprClientClass {
|
|
|
1540
1635
|
return false;
|
|
1541
1636
|
}
|
|
1542
1637
|
calculateStartEndGranularity(quickFilterValue) {
|
|
1543
|
-
let startDate = dayjs(this.startInput
|
|
1544
|
-
let endDate = dayjs(this.endInput
|
|
1545
|
-
let granularity = this.granularityInput
|
|
1638
|
+
let startDate = dayjs(this.startInput?.value);
|
|
1639
|
+
let endDate = dayjs(this.endInput?.value);
|
|
1640
|
+
let granularity = this.granularityInput?.value;
|
|
1546
1641
|
switch (quickFilterValue) {
|
|
1547
1642
|
// This case only happens if quick filter is _already_ none and someone changes granularity
|
|
1548
1643
|
case 'none':
|
|
1549
1644
|
endDate = dayjs();
|
|
1550
1645
|
startDate = dayjs();
|
|
1551
|
-
granularity = this.granularityInput
|
|
1646
|
+
granularity = this.granularityInput?.value;
|
|
1552
1647
|
break;
|
|
1553
1648
|
case 'today':
|
|
1554
1649
|
endDate = dayjs();
|
|
@@ -1669,7 +1764,7 @@ class GranularDateRange extends DprClientClass {
|
|
|
1669
1764
|
return {
|
|
1670
1765
|
startDate,
|
|
1671
1766
|
endDate,
|
|
1672
|
-
granularity,
|
|
1767
|
+
granularity: String(granularity),
|
|
1673
1768
|
};
|
|
1674
1769
|
}
|
|
1675
1770
|
}
|
|
@@ -1743,76 +1838,16 @@ class MultiselectInput extends DprClientClass {
|
|
|
1743
1838
|
/**
|
|
1744
1839
|
* Removes classes to relevant element to hide all select items
|
|
1745
1840
|
*
|
|
1746
|
-
* @param {Event} e
|
|
1747
|
-
* @memberof MultiselectInput
|
|
1748
|
-
*/
|
|
1749
|
-
removeFullWidthClasses(e) {
|
|
1750
|
-
e.preventDefault();
|
|
1751
|
-
this.element.classList.remove('multiselect-container__full-width');
|
|
1752
|
-
this.filtersContainer?.classList.remove('dpr-filter-item__span-3');
|
|
1753
|
-
// Update button visability
|
|
1754
|
-
this.fullListLink?.classList.remove('dpr-multiselect-action--hide');
|
|
1755
|
-
this.hideListLink?.classList.add('dpr-multiselect-action--hide');
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
class AutoCompleteMulti extends DprClientClass {
|
|
1760
|
-
filterId;
|
|
1761
|
-
searchInput;
|
|
1762
|
-
searchInputValue;
|
|
1763
|
-
multiselectOptions;
|
|
1764
|
-
static getModuleName() {
|
|
1765
|
-
return 'autocomplete-multiselect-input';
|
|
1766
|
-
}
|
|
1767
|
-
initialise() {
|
|
1768
|
-
this.element = this.getElement();
|
|
1769
|
-
this.filterId = this.element.getAttribute('data-filter-id');
|
|
1770
|
-
// Search input
|
|
1771
|
-
this.searchInput = document.getElementById(`search.${this.filterId}`);
|
|
1772
|
-
this.multiselectOptions = Array.from(this.element.querySelectorAll('.govuk-checkboxes__input'));
|
|
1773
|
-
if (!this.searchInput || !this.multiselectOptions)
|
|
1774
|
-
return;
|
|
1775
|
-
this.initialiseCheckboxes();
|
|
1776
|
-
this.initSearchInputAction();
|
|
1777
|
-
}
|
|
1778
|
-
initialiseCheckboxes() {
|
|
1779
|
-
this.multiselectOptions.forEach(input => {
|
|
1780
|
-
const wrapper = input.closest('.govuk-checkboxes__item');
|
|
1781
|
-
if (!wrapper)
|
|
1782
|
-
return;
|
|
1783
|
-
wrapper.classList.toggle('dpr-form--hidden', !input.checked);
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
initSearchInputAction() {
|
|
1787
|
-
if (!this.searchInput)
|
|
1788
|
-
return;
|
|
1789
|
-
const input = this.searchInput;
|
|
1790
|
-
this.searchInput.addEventListener('keyup', _event => {
|
|
1791
|
-
this.searchInputValue = input.value;
|
|
1792
|
-
this.updateCheckboxes();
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
updateCheckboxes() {
|
|
1796
|
-
const query = this.searchInputValue.toLowerCase().trim();
|
|
1797
|
-
const minLength = 3;
|
|
1798
|
-
this.multiselectOptions.forEach(input => {
|
|
1799
|
-
const wrapper = input.closest('.govuk-checkboxes__item');
|
|
1800
|
-
if (!wrapper)
|
|
1801
|
-
return;
|
|
1802
|
-
// Always show selected items
|
|
1803
|
-
if (input.checked) {
|
|
1804
|
-
wrapper.classList.remove('dpr-form--hidden');
|
|
1805
|
-
return;
|
|
1806
|
-
}
|
|
1807
|
-
// Below threshold - hide unselected
|
|
1808
|
-
if (query.length < minLength) {
|
|
1809
|
-
wrapper.classList.add('dpr-form--hidden');
|
|
1810
|
-
return;
|
|
1811
|
-
}
|
|
1812
|
-
const labelText = input.labels?.[0]?.innerText.toLowerCase() ?? '';
|
|
1813
|
-
const matches = labelText.includes(query);
|
|
1814
|
-
wrapper.classList.toggle('dpr-form--hidden', !matches);
|
|
1815
|
-
});
|
|
1841
|
+
* @param {Event} e
|
|
1842
|
+
* @memberof MultiselectInput
|
|
1843
|
+
*/
|
|
1844
|
+
removeFullWidthClasses(e) {
|
|
1845
|
+
e.preventDefault();
|
|
1846
|
+
this.element.classList.remove('multiselect-container__full-width');
|
|
1847
|
+
this.filtersContainer?.classList.remove('dpr-filter-item__span-3');
|
|
1848
|
+
// Update button visability
|
|
1849
|
+
this.fullListLink?.classList.remove('dpr-multiselect-action--hide');
|
|
1850
|
+
this.hideListLink?.classList.add('dpr-multiselect-action--hide');
|
|
1816
1851
|
}
|
|
1817
1852
|
}
|
|
1818
1853
|
|
|
@@ -18554,14 +18589,32 @@ var plugin = {
|
|
|
18554
18589
|
}
|
|
18555
18590
|
};
|
|
18556
18591
|
|
|
18557
|
-
// @ts-nocheck
|
|
18558
18592
|
/* eslint-disable class-methods-use-this */
|
|
18559
18593
|
class ChartVisualisation extends DprClientClass {
|
|
18594
|
+
chartContext = null;
|
|
18595
|
+
chart = null;
|
|
18596
|
+
chartParams = {};
|
|
18597
|
+
type = null;
|
|
18598
|
+
id = '';
|
|
18599
|
+
unit = '';
|
|
18600
|
+
suffix = '';
|
|
18601
|
+
legend = null;
|
|
18602
|
+
tooltipDetailsEl = null;
|
|
18603
|
+
headlineValuesEl = null;
|
|
18604
|
+
labelElement = null;
|
|
18605
|
+
valueElement = null;
|
|
18606
|
+
legendElement = null;
|
|
18607
|
+
partialStart = false;
|
|
18608
|
+
partialEnd = false;
|
|
18609
|
+
singleDataset = false;
|
|
18610
|
+
static getModuleName() {
|
|
18611
|
+
return 'chart';
|
|
18612
|
+
}
|
|
18560
18613
|
setupCanvas() {
|
|
18561
18614
|
this.chartContext = this.getElement().querySelector('canvas');
|
|
18562
18615
|
// data
|
|
18563
|
-
this.id = this.chartContext
|
|
18564
|
-
this.chartParams = JSON.parse(this.getElement().getAttribute('data-dpr-chart-data'));
|
|
18616
|
+
this.id = this.chartContext?.getAttribute('id') || '';
|
|
18617
|
+
this.chartParams = JSON.parse(this.getElement().getAttribute('data-dpr-chart-data') || '{}');
|
|
18565
18618
|
this.type = this.getElement().getAttribute('data-dpr-chart-type');
|
|
18566
18619
|
this.setValueSuffix();
|
|
18567
18620
|
// elements
|
|
@@ -18573,14 +18626,14 @@ class ChartVisualisation extends DprClientClass {
|
|
|
18573
18626
|
this.valueElement = document.getElementById(`dpr-${this.id}-value`);
|
|
18574
18627
|
this.legendElement = document.getElementById(`dpr-${this.id}-legend`);
|
|
18575
18628
|
this.legendElement = document.getElementById(`dpr-${this.id}-legend`);
|
|
18576
|
-
if (this.chartParams
|
|
18577
|
-
this.partialStart = this.chartParams
|
|
18578
|
-
this.partialEnd = this.chartParams
|
|
18629
|
+
if (this.chartParams['partialDate']) {
|
|
18630
|
+
this.partialStart = this.chartParams['partialDate'].start || false;
|
|
18631
|
+
this.partialEnd = this.chartParams['partialDate'].end || false;
|
|
18579
18632
|
}
|
|
18580
18633
|
// flags
|
|
18581
|
-
this.singleDataset = this.chartParams
|
|
18634
|
+
this.singleDataset = this.chartParams['datasets'].length === 1;
|
|
18582
18635
|
}
|
|
18583
|
-
initChart() {
|
|
18636
|
+
initChart(chartData) {
|
|
18584
18637
|
// Prevent font loading issue
|
|
18585
18638
|
window.addEventListener('load', () => {
|
|
18586
18639
|
// An example of creating a chart, replace with your code:
|
|
@@ -18589,7 +18642,7 @@ class ChartVisualisation extends DprClientClass {
|
|
|
18589
18642
|
Chart.register(plugin);
|
|
18590
18643
|
Chart.register(MatrixController, MatrixElement);
|
|
18591
18644
|
Chart.defaults.datasets.bar.categoryPercentage = 0.95;
|
|
18592
|
-
this.chart = new Chart(this.chartContext,
|
|
18645
|
+
this.chart = new Chart(this.chartContext, chartData);
|
|
18593
18646
|
this.initChartEvents();
|
|
18594
18647
|
});
|
|
18595
18648
|
}
|
|
@@ -18642,14 +18695,14 @@ class ChartVisualisation extends DprClientClass {
|
|
|
18642
18695
|
}
|
|
18643
18696
|
}
|
|
18644
18697
|
setValueSuffix() {
|
|
18645
|
-
this.unit = this.getElement().getAttribute('data-dpr-chart-unit');
|
|
18698
|
+
this.unit = this.getElement().getAttribute('data-dpr-chart-unit') || '';
|
|
18646
18699
|
this.suffix = this.unit === 'percentage' ? '%' : '';
|
|
18647
18700
|
}
|
|
18648
18701
|
isPercentage() {
|
|
18649
18702
|
return this.unit === 'percentage';
|
|
18650
18703
|
}
|
|
18651
18704
|
initChartEvents() {
|
|
18652
|
-
this.chart
|
|
18705
|
+
this.chart?.canvas.addEventListener('mouseout', () => {
|
|
18653
18706
|
if (this.tooltipDetailsEl)
|
|
18654
18707
|
this.tooltipDetailsEl.style.display = 'none';
|
|
18655
18708
|
if (this.headlineValuesEl)
|
|
@@ -18658,10 +18711,11 @@ class ChartVisualisation extends DprClientClass {
|
|
|
18658
18711
|
}
|
|
18659
18712
|
}
|
|
18660
18713
|
|
|
18661
|
-
// @ts-nocheck
|
|
18662
18714
|
/* eslint-disable no-underscore-dangle */
|
|
18663
18715
|
/* eslint-disable class-methods-use-this */
|
|
18664
18716
|
class BarChartVisualisation extends ChartVisualisation {
|
|
18717
|
+
settings = {};
|
|
18718
|
+
chartData;
|
|
18665
18719
|
static getModuleName() {
|
|
18666
18720
|
return 'bar-chart';
|
|
18667
18721
|
}
|
|
@@ -18701,9 +18755,9 @@ class BarChartVisualisation extends ChartVisualisation {
|
|
|
18701
18755
|
return {
|
|
18702
18756
|
color: '#FFF',
|
|
18703
18757
|
display: () => {
|
|
18704
|
-
return
|
|
18758
|
+
return true;
|
|
18705
18759
|
},
|
|
18706
|
-
formatter: value => {
|
|
18760
|
+
formatter: (value) => {
|
|
18707
18761
|
return `${value}${this.suffix}`;
|
|
18708
18762
|
},
|
|
18709
18763
|
labels: {
|
|
@@ -18719,9 +18773,9 @@ class BarChartVisualisation extends ChartVisualisation {
|
|
|
18719
18773
|
}
|
|
18720
18774
|
}
|
|
18721
18775
|
|
|
18722
|
-
// @ts-nocheck
|
|
18723
|
-
/* eslint-disable class-methods-use-this */
|
|
18724
18776
|
class DoughnutChartVisualisation extends ChartVisualisation {
|
|
18777
|
+
settings = {};
|
|
18778
|
+
chartData;
|
|
18725
18779
|
static getModuleName() {
|
|
18726
18780
|
return 'doughnut-chart';
|
|
18727
18781
|
}
|
|
@@ -18741,7 +18795,7 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
18741
18795
|
};
|
|
18742
18796
|
}
|
|
18743
18797
|
setOptions() {
|
|
18744
|
-
const cutoutValue = this.chartParams
|
|
18798
|
+
const cutoutValue = this.chartParams['datasets'].length === 1 ? '50%' : '20%';
|
|
18745
18799
|
return {
|
|
18746
18800
|
cutout: cutoutValue,
|
|
18747
18801
|
};
|
|
@@ -18756,7 +18810,7 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
18756
18810
|
}
|
|
18757
18811
|
setPlugins() {
|
|
18758
18812
|
const plugins = [];
|
|
18759
|
-
if (this.chartParams
|
|
18813
|
+
if (this.chartParams['datasets'].length === 1 && !this.isPercentage()) {
|
|
18760
18814
|
plugins.push(this.setCentralText());
|
|
18761
18815
|
}
|
|
18762
18816
|
return plugins;
|
|
@@ -18766,16 +18820,14 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
18766
18820
|
// Put the total in the center of the donut
|
|
18767
18821
|
id: 'text',
|
|
18768
18822
|
beforeDraw(chart) {
|
|
18769
|
-
const { width } = chart;
|
|
18770
|
-
const { height } = chart;
|
|
18771
|
-
const { ctx } = chart;
|
|
18823
|
+
const { width, height, ctx } = chart;
|
|
18772
18824
|
ctx.textBaseline = 'middle';
|
|
18773
18825
|
let fontSize = 2.5;
|
|
18774
18826
|
ctx.font = `100 ${fontSize}em GDS Transport`;
|
|
18775
18827
|
ctx.fillStyle = ' #505a5f';
|
|
18776
18828
|
// Accumulated total
|
|
18777
|
-
const total = chart.data.datasets[0].data.reduce((a, c) => a + c, 0);
|
|
18778
|
-
const text = total;
|
|
18829
|
+
const total = chart.data.datasets[0].data.reduce((a, c) => a + Number(c), 0);
|
|
18830
|
+
const text = total?.toString() || '';
|
|
18779
18831
|
const textX = Math.round((width - ctx.measureText(text).width) / 2);
|
|
18780
18832
|
const textY = height / 2;
|
|
18781
18833
|
ctx.fillText(text, textX, textY);
|
|
@@ -18830,7 +18882,7 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
18830
18882
|
return {
|
|
18831
18883
|
textAlign: 'center',
|
|
18832
18884
|
color: '#FFF',
|
|
18833
|
-
display: context => {
|
|
18885
|
+
display: (context) => {
|
|
18834
18886
|
const { dataset, dataIndex } = context;
|
|
18835
18887
|
const value = dataset.data[dataIndex];
|
|
18836
18888
|
const total = dataset.data.reduce((a, c) => a + c, 0);
|
|
@@ -18858,65 +18910,9 @@ ${dataset.label}`;
|
|
|
18858
18910
|
}
|
|
18859
18911
|
}
|
|
18860
18912
|
|
|
18861
|
-
// @ts-nocheck
|
|
18862
|
-
/* eslint-disable class-methods-use-this */
|
|
18863
|
-
class LineChartVisualisation extends ChartVisualisation {
|
|
18864
|
-
static getModuleName() {
|
|
18865
|
-
return 'line-chart';
|
|
18866
|
-
}
|
|
18867
|
-
initialise() {
|
|
18868
|
-
this.setupCanvas();
|
|
18869
|
-
this.settings = this.initSettings();
|
|
18870
|
-
this.chartData = this.generateChartData(this.settings);
|
|
18871
|
-
this.lastIndex = this.chartData.data.labels.length - 1;
|
|
18872
|
-
this.initChart(this.chartData);
|
|
18873
|
-
}
|
|
18874
|
-
initSettings() {
|
|
18875
|
-
return {
|
|
18876
|
-
toolTipOptions: this.setToolTipOptions(),
|
|
18877
|
-
styling: this.setDatasetStyling(),
|
|
18878
|
-
};
|
|
18879
|
-
}
|
|
18880
|
-
setPartialStyle(ctx) {
|
|
18881
|
-
let style;
|
|
18882
|
-
if ((this.partialEnd && ctx.p1DataIndex === this.lastIndex) || (this.partialStart && ctx.p1DataIndex === 1)) {
|
|
18883
|
-
style = [6, 6];
|
|
18884
|
-
}
|
|
18885
|
-
return style;
|
|
18886
|
-
}
|
|
18887
|
-
setDatasetStyling() {
|
|
18888
|
-
return {
|
|
18889
|
-
segment: {
|
|
18890
|
-
borderDash: ctx => this.setPartialStyle(ctx),
|
|
18891
|
-
},
|
|
18892
|
-
};
|
|
18893
|
-
}
|
|
18894
|
-
setToolTipOptions() {
|
|
18895
|
-
const ctx = this;
|
|
18896
|
-
return {
|
|
18897
|
-
callbacks: {
|
|
18898
|
-
title(context) {
|
|
18899
|
-
const { label, dataset } = context[0];
|
|
18900
|
-
const { label: establishmentId } = dataset;
|
|
18901
|
-
const title = ctx.singleDataset ? `${label}` : `${establishmentId}: ${label}`;
|
|
18902
|
-
return title;
|
|
18903
|
-
},
|
|
18904
|
-
label(context) {
|
|
18905
|
-
const { label } = context;
|
|
18906
|
-
const { data, label: legend } = context.dataset;
|
|
18907
|
-
const value = data[context.dataIndex];
|
|
18908
|
-
ctx.setHoverValue({ label, value, legend, ctx });
|
|
18909
|
-
return value;
|
|
18910
|
-
},
|
|
18911
|
-
},
|
|
18912
|
-
};
|
|
18913
|
-
}
|
|
18914
|
-
}
|
|
18915
|
-
|
|
18916
|
-
// @ts-nocheck
|
|
18917
|
-
/* eslint-disable prefer-destructuring */
|
|
18918
|
-
/* eslint-disable class-methods-use-this */
|
|
18919
18913
|
class MatrixChartVisualisation extends ChartVisualisation {
|
|
18914
|
+
settings = {};
|
|
18915
|
+
chartData;
|
|
18920
18916
|
static getModuleName() {
|
|
18921
18917
|
return 'matrix-chart';
|
|
18922
18918
|
}
|
|
@@ -18936,7 +18932,7 @@ class MatrixChartVisualisation extends ChartVisualisation {
|
|
|
18936
18932
|
return {
|
|
18937
18933
|
callbacks: {
|
|
18938
18934
|
title(context) {
|
|
18939
|
-
const
|
|
18935
|
+
const raw = context[0].raw;
|
|
18940
18936
|
const title = `${raw.y} ${raw.x}`;
|
|
18941
18937
|
return title;
|
|
18942
18938
|
},
|
|
@@ -18985,23 +18981,82 @@ class MatrixChartVisualisation extends ChartVisualisation {
|
|
|
18985
18981
|
}
|
|
18986
18982
|
createDatasets() {
|
|
18987
18983
|
const { datasets } = this.chartParams;
|
|
18988
|
-
return datasets.map(
|
|
18989
|
-
const { label, data } =
|
|
18984
|
+
return datasets.map((dataset) => {
|
|
18985
|
+
const { label, data } = dataset;
|
|
18990
18986
|
return {
|
|
18991
18987
|
label,
|
|
18992
18988
|
data,
|
|
18993
18989
|
backgroundColor(c) {
|
|
18994
|
-
|
|
18990
|
+
const color = c.raw.c;
|
|
18991
|
+
return color;
|
|
18995
18992
|
},
|
|
18996
|
-
width: ({ chart }) => (chart.chartArea || {}).width / chart.scales
|
|
18997
|
-
height: ({ chart }) => (chart.chartArea || {}).height / chart.scales
|
|
18993
|
+
width: ({ chart }) => (chart.chartArea || {}).width / chart.scales['x'].ticks.length - 1,
|
|
18994
|
+
height: ({ chart }) => (chart.chartArea || {}).height / chart.scales['y'].ticks.length - 1,
|
|
18998
18995
|
};
|
|
18999
18996
|
});
|
|
19000
18997
|
}
|
|
19001
18998
|
}
|
|
19002
18999
|
|
|
19003
|
-
|
|
19000
|
+
class LineChartVisualisation extends ChartVisualisation {
|
|
19001
|
+
settings = {};
|
|
19002
|
+
chartData;
|
|
19003
|
+
lastIndex = 0;
|
|
19004
|
+
static getModuleName() {
|
|
19005
|
+
return 'line-chart';
|
|
19006
|
+
}
|
|
19007
|
+
initialise() {
|
|
19008
|
+
this.setupCanvas();
|
|
19009
|
+
this.settings = this.initSettings();
|
|
19010
|
+
this.chartData = this.generateChartData(this.settings);
|
|
19011
|
+
this.lastIndex = this.chartData.data?.labels ? this.chartData.data.labels.length - 1 : 0;
|
|
19012
|
+
this.initChart(this.chartData);
|
|
19013
|
+
}
|
|
19014
|
+
initSettings() {
|
|
19015
|
+
return {
|
|
19016
|
+
toolTipOptions: this.setToolTipOptions(),
|
|
19017
|
+
styling: this.setDatasetStyling(),
|
|
19018
|
+
};
|
|
19019
|
+
}
|
|
19020
|
+
setPartialStyle(ctx) {
|
|
19021
|
+
let style;
|
|
19022
|
+
if ((this.partialEnd && ctx.p1DataIndex === this.lastIndex) || (this.partialStart && ctx.p1DataIndex === 1)) {
|
|
19023
|
+
style = [6, 6];
|
|
19024
|
+
}
|
|
19025
|
+
return style;
|
|
19026
|
+
}
|
|
19027
|
+
setDatasetStyling() {
|
|
19028
|
+
return {
|
|
19029
|
+
segment: {
|
|
19030
|
+
borderDash: (ctx) => this.setPartialStyle(ctx),
|
|
19031
|
+
},
|
|
19032
|
+
};
|
|
19033
|
+
}
|
|
19034
|
+
setToolTipOptions() {
|
|
19035
|
+
const ctx = this;
|
|
19036
|
+
return {
|
|
19037
|
+
callbacks: {
|
|
19038
|
+
title(context) {
|
|
19039
|
+
const { label, dataset } = context[0];
|
|
19040
|
+
const { label: establishmentId } = dataset;
|
|
19041
|
+
const title = ctx.singleDataset ? `${label}` : `${establishmentId}: ${label}`;
|
|
19042
|
+
return title;
|
|
19043
|
+
},
|
|
19044
|
+
label(context) {
|
|
19045
|
+
const { label } = context;
|
|
19046
|
+
const { data, label: legend } = context.dataset;
|
|
19047
|
+
const value = String(data[context.dataIndex]);
|
|
19048
|
+
ctx.setHoverValue({ label, value, legend, ctx });
|
|
19049
|
+
return value;
|
|
19050
|
+
},
|
|
19051
|
+
},
|
|
19052
|
+
};
|
|
19053
|
+
}
|
|
19054
|
+
}
|
|
19055
|
+
|
|
19004
19056
|
class Scorecard extends DprClientClass {
|
|
19057
|
+
scorecard = null;
|
|
19058
|
+
value = null;
|
|
19059
|
+
ragStatus = null;
|
|
19005
19060
|
static getModuleName() {
|
|
19006
19061
|
return 'scorecard';
|
|
19007
19062
|
}
|
|
@@ -19013,15 +19068,39 @@ class Scorecard extends DprClientClass {
|
|
|
19013
19068
|
this.initHover();
|
|
19014
19069
|
}
|
|
19015
19070
|
initHover() {
|
|
19016
|
-
this.value
|
|
19017
|
-
this.ragStatus
|
|
19071
|
+
this.value?.addEventListener('mouseover', async () => {
|
|
19072
|
+
this.ragStatus?.classList.add('dpr-scorecard__value-description--active');
|
|
19018
19073
|
});
|
|
19019
|
-
this.value
|
|
19020
|
-
this.ragStatus
|
|
19074
|
+
this.value?.addEventListener('mouseout', async () => {
|
|
19075
|
+
this.ragStatus?.classList.remove('dpr-scorecard__value-description--active');
|
|
19021
19076
|
});
|
|
19022
19077
|
}
|
|
19023
19078
|
}
|
|
19024
19079
|
|
|
19080
|
+
class DprReportsCatalogueCollections extends DprClientClass {
|
|
19081
|
+
static getModuleName() {
|
|
19082
|
+
return 'dpr-reports-catalogue-collections';
|
|
19083
|
+
}
|
|
19084
|
+
initialise() {
|
|
19085
|
+
this.initProductCollectionSelect();
|
|
19086
|
+
}
|
|
19087
|
+
initProductCollectionSelect() {
|
|
19088
|
+
const element = this.getElement();
|
|
19089
|
+
if (element) {
|
|
19090
|
+
const productCollections = element.querySelector('#productCollection');
|
|
19091
|
+
if (productCollections) {
|
|
19092
|
+
productCollections.addEventListener('change', e => {
|
|
19093
|
+
e.preventDefault();
|
|
19094
|
+
const form = productCollections.closest('form');
|
|
19095
|
+
if (!form)
|
|
19096
|
+
return;
|
|
19097
|
+
form.submit();
|
|
19098
|
+
});
|
|
19099
|
+
}
|
|
19100
|
+
}
|
|
19101
|
+
}
|
|
19102
|
+
}
|
|
19103
|
+
|
|
19025
19104
|
class DprReportsCatalogueFiltersClass extends DprClientClass {
|
|
19026
19105
|
static products;
|
|
19027
19106
|
static variants;
|
|
@@ -19162,163 +19241,59 @@ class DprReportsCatalogueSearch extends DprReportsCatalogueFiltersClass {
|
|
|
19162
19241
|
}).length;
|
|
19163
19242
|
}
|
|
19164
19243
|
variantMatches(product, variant, searchTerm) {
|
|
19165
|
-
if (searchTerm === '') {
|
|
19166
|
-
return true;
|
|
19167
|
-
}
|
|
19168
|
-
const heading = product.querySelector('.dpr-report-catalogue__product-row__name');
|
|
19169
|
-
const productText = heading ? this.getSearchText(heading) : '';
|
|
19170
|
-
const variantText = this.getSearchText(variant);
|
|
19171
|
-
const searchTerms = searchTerm.split(/\s+/).filter(Boolean);
|
|
19172
|
-
return searchTerms.every(term => productText.includes(term) || variantText.includes(term));
|
|
19173
|
-
}
|
|
19174
|
-
/**
|
|
19175
|
-
* Determines whether a product heading matches the search term.
|
|
19176
|
-
*/
|
|
19177
|
-
productMatches(product, searchTerm) {
|
|
19178
|
-
const heading = product.querySelector('.dpr-report-catalogue__product-row__name');
|
|
19179
|
-
return heading ? this.matchesSearch(this.getSearchText(heading), searchTerm) : false;
|
|
19180
|
-
}
|
|
19181
|
-
matchesSearch(text, searchTerm) {
|
|
19182
|
-
if (searchTerm === '') {
|
|
19183
|
-
return true;
|
|
19184
|
-
}
|
|
19185
|
-
const searchTerms = searchTerm.split(/\s+/).filter(Boolean);
|
|
19186
|
-
return searchTerms.every(term => text.includes(term));
|
|
19187
|
-
}
|
|
19188
|
-
/**
|
|
19189
|
-
* Normalises a search term for matching.
|
|
19190
|
-
*
|
|
19191
|
-
* Searches with fewer than three characters are treated as empty.
|
|
19192
|
-
*/
|
|
19193
|
-
normaliseSearch(search) {
|
|
19194
|
-
const value = search.trim().toLowerCase();
|
|
19195
|
-
return value.length >= 3 ? value : '';
|
|
19196
|
-
}
|
|
19197
|
-
/**
|
|
19198
|
-
* Reads the current search term from the URL query string.
|
|
19199
|
-
*/
|
|
19200
|
-
getSearchFromQueryString() {
|
|
19201
|
-
const params = new URLSearchParams(window.location.search);
|
|
19202
|
-
return params.get('search') ?? '';
|
|
19203
|
-
}
|
|
19204
|
-
/**
|
|
19205
|
-
* Updates the URL query string with the current search value.
|
|
19206
|
-
*
|
|
19207
|
-
* The parameter is removed when the search is cleared or contains
|
|
19208
|
-
* fewer than three characters.
|
|
19209
|
-
*/
|
|
19210
|
-
updateQueryString(search) {
|
|
19211
|
-
const params = new URLSearchParams(window.location.search);
|
|
19212
|
-
const value = search.trim();
|
|
19213
|
-
if (value.length >= 3) {
|
|
19214
|
-
params.set('search', value);
|
|
19215
|
-
}
|
|
19216
|
-
else {
|
|
19217
|
-
params.delete('search');
|
|
19218
|
-
}
|
|
19219
|
-
const queryString = params.toString();
|
|
19220
|
-
window.history.replaceState({}, '', queryString ? `${window.location.pathname}?${queryString}` : window.location.pathname);
|
|
19221
|
-
}
|
|
19222
|
-
}
|
|
19223
|
-
|
|
19224
|
-
class DprReportsCatalogueCollections extends DprClientClass {
|
|
19225
|
-
static getModuleName() {
|
|
19226
|
-
return 'dpr-reports-catalogue-collections';
|
|
19227
|
-
}
|
|
19228
|
-
initialise() {
|
|
19229
|
-
this.initProductCollectionSelect();
|
|
19230
|
-
}
|
|
19231
|
-
initProductCollectionSelect() {
|
|
19232
|
-
const element = this.getElement();
|
|
19233
|
-
if (element) {
|
|
19234
|
-
const productCollections = element.querySelector('#productCollection');
|
|
19235
|
-
if (productCollections) {
|
|
19236
|
-
productCollections.addEventListener('change', e => {
|
|
19237
|
-
e.preventDefault();
|
|
19238
|
-
const form = productCollections.closest('form');
|
|
19239
|
-
if (!form)
|
|
19240
|
-
return;
|
|
19241
|
-
form.submit();
|
|
19242
|
-
});
|
|
19243
|
-
}
|
|
19244
|
-
}
|
|
19245
|
-
}
|
|
19246
|
-
}
|
|
19247
|
-
|
|
19248
|
-
/**
|
|
19249
|
-
* Client-side report type filter for the reports catalogue.
|
|
19250
|
-
*
|
|
19251
|
-
* Filters variant/dashboard rows by report type using the
|
|
19252
|
-
* `data-report-type` attribute and applies the `type-hide`
|
|
19253
|
-
* CSS class to rows that should not be visible.
|
|
19254
|
-
*
|
|
19255
|
-
* The selected filter is persisted in the URL query string
|
|
19256
|
-
* so that refreshes and shared links retain the current state.
|
|
19257
|
-
*/
|
|
19258
|
-
class DprReportsCatalogueTypeFilter extends DprReportsCatalogueFiltersClass {
|
|
19259
|
-
reportTypeRadios;
|
|
19260
|
-
showHideClassName = 'dpr-reports-catalogue-type-hide';
|
|
19261
|
-
static getModuleName() {
|
|
19262
|
-
return 'dpr-report-catalogue-type-filter';
|
|
19263
|
-
}
|
|
19264
|
-
initialise() {
|
|
19265
|
-
this.reportTypeRadios = [...this.getElement().querySelectorAll('input[type="radio"]')];
|
|
19266
|
-
if (this.reportTypeRadios.length === 0) {
|
|
19267
|
-
return;
|
|
19268
|
-
}
|
|
19269
|
-
const selectedType = this.getReportTypeFromQueryString();
|
|
19270
|
-
const selectedRadio = this.reportTypeRadios.find(radio => radio.value === selectedType);
|
|
19271
|
-
if (selectedRadio) {
|
|
19272
|
-
selectedRadio.checked = true;
|
|
19273
|
-
}
|
|
19274
|
-
this.applyFilter(selectedType);
|
|
19275
|
-
this.reportTypeRadios.forEach(radio => {
|
|
19276
|
-
radio.addEventListener('change', () => {
|
|
19277
|
-
this.updateQueryString(radio.value);
|
|
19278
|
-
this.applyFilter(radio.value);
|
|
19279
|
-
});
|
|
19280
|
-
});
|
|
19244
|
+
if (searchTerm === '') {
|
|
19245
|
+
return true;
|
|
19246
|
+
}
|
|
19247
|
+
const heading = product.querySelector('.dpr-report-catalogue__product-row__name');
|
|
19248
|
+
const productText = heading ? this.getSearchText(heading) : '';
|
|
19249
|
+
const variantText = this.getSearchText(variant);
|
|
19250
|
+
const searchTerms = searchTerm.split(/\s+/).filter(Boolean);
|
|
19251
|
+
return searchTerms.every(term => productText.includes(term) || variantText.includes(term));
|
|
19281
19252
|
}
|
|
19282
19253
|
/**
|
|
19283
|
-
*
|
|
19254
|
+
* Determines whether a product heading matches the search term.
|
|
19284
19255
|
*/
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19289
|
-
|
|
19256
|
+
productMatches(product, searchTerm) {
|
|
19257
|
+
const heading = product.querySelector('.dpr-report-catalogue__product-row__name');
|
|
19258
|
+
return heading ? this.matchesSearch(this.getSearchText(heading), searchTerm) : false;
|
|
19259
|
+
}
|
|
19260
|
+
matchesSearch(text, searchTerm) {
|
|
19261
|
+
if (searchTerm === '') {
|
|
19262
|
+
return true;
|
|
19263
|
+
}
|
|
19264
|
+
const searchTerms = searchTerm.split(/\s+/).filter(Boolean);
|
|
19265
|
+
return searchTerms.every(term => text.includes(term));
|
|
19290
19266
|
}
|
|
19291
19267
|
/**
|
|
19292
|
-
*
|
|
19293
|
-
*
|
|
19268
|
+
* Normalises a search term for matching.
|
|
19269
|
+
*
|
|
19270
|
+
* Searches with fewer than three characters are treated as empty.
|
|
19294
19271
|
*/
|
|
19295
|
-
|
|
19296
|
-
const
|
|
19297
|
-
|
|
19298
|
-
const showRow = reportType === '' || reportType === 'all' || rowType === reportType;
|
|
19299
|
-
row.classList.toggle(this.showHideClassName, !showRow);
|
|
19300
|
-
return showRow;
|
|
19301
|
-
});
|
|
19302
|
-
product.classList.toggle(this.showHideClassName, visibleRows.length === 0);
|
|
19272
|
+
normaliseSearch(search) {
|
|
19273
|
+
const value = search.trim().toLowerCase();
|
|
19274
|
+
return value.length >= 3 ? value : '';
|
|
19303
19275
|
}
|
|
19304
19276
|
/**
|
|
19305
|
-
* Reads the
|
|
19277
|
+
* Reads the current search term from the URL query string.
|
|
19306
19278
|
*/
|
|
19307
|
-
|
|
19279
|
+
getSearchFromQueryString() {
|
|
19308
19280
|
const params = new URLSearchParams(window.location.search);
|
|
19309
|
-
return params.get('
|
|
19281
|
+
return params.get('search') ?? '';
|
|
19310
19282
|
}
|
|
19311
19283
|
/**
|
|
19312
|
-
* Updates the URL query string with the
|
|
19313
|
-
*
|
|
19284
|
+
* Updates the URL query string with the current search value.
|
|
19285
|
+
*
|
|
19286
|
+
* The parameter is removed when the search is cleared or contains
|
|
19287
|
+
* fewer than three characters.
|
|
19314
19288
|
*/
|
|
19315
|
-
updateQueryString(
|
|
19289
|
+
updateQueryString(search) {
|
|
19316
19290
|
const params = new URLSearchParams(window.location.search);
|
|
19317
|
-
|
|
19318
|
-
|
|
19291
|
+
const value = search.trim();
|
|
19292
|
+
if (value.length >= 3) {
|
|
19293
|
+
params.set('search', value);
|
|
19319
19294
|
}
|
|
19320
19295
|
else {
|
|
19321
|
-
params.delete('
|
|
19296
|
+
params.delete('search');
|
|
19322
19297
|
}
|
|
19323
19298
|
const queryString = params.toString();
|
|
19324
19299
|
window.history.replaceState({}, '', queryString ? `${window.location.pathname}?${queryString}` : window.location.pathname);
|
|
@@ -19449,80 +19424,82 @@ class DprReportsCatalogueShowHide extends DprReportsCatalogueFiltersClass {
|
|
|
19449
19424
|
}
|
|
19450
19425
|
|
|
19451
19426
|
/**
|
|
19452
|
-
*
|
|
19427
|
+
* Client-side report type filter for the reports catalogue.
|
|
19453
19428
|
*
|
|
19454
|
-
*
|
|
19429
|
+
* Filters variant/dashboard rows by report type using the
|
|
19430
|
+
* `data-report-type` attribute and applies the `type-hide`
|
|
19431
|
+
* CSS class to rows that should not be visible.
|
|
19432
|
+
*
|
|
19433
|
+
* The selected filter is persisted in the URL query string
|
|
19434
|
+
* so that refreshes and shared links retain the current state.
|
|
19455
19435
|
*/
|
|
19456
|
-
class
|
|
19457
|
-
|
|
19458
|
-
|
|
19436
|
+
class DprReportsCatalogueTypeFilter extends DprReportsCatalogueFiltersClass {
|
|
19437
|
+
reportTypeRadios;
|
|
19438
|
+
showHideClassName = 'dpr-reports-catalogue-type-hide';
|
|
19459
19439
|
static getModuleName() {
|
|
19460
|
-
return 'dpr-report-catalogue-
|
|
19440
|
+
return 'dpr-report-catalogue-type-filter';
|
|
19461
19441
|
}
|
|
19462
19442
|
initialise() {
|
|
19463
|
-
this.
|
|
19464
|
-
this.
|
|
19465
|
-
this.updateTotals();
|
|
19466
|
-
document.addEventListener('dpr-report-catalogue-filter-changed', () => {
|
|
19467
|
-
this.updateTotals();
|
|
19468
|
-
});
|
|
19469
|
-
}
|
|
19470
|
-
/**
|
|
19471
|
-
* Updates visible product and variant counts.
|
|
19472
|
-
*/
|
|
19473
|
-
updateTotals() {
|
|
19474
|
-
if (!this.productsTotal || !this.variantsTotal) {
|
|
19443
|
+
this.reportTypeRadios = [...this.getElement().querySelectorAll('input[type="radio"]')];
|
|
19444
|
+
if (this.reportTypeRadios.length === 0) {
|
|
19475
19445
|
return;
|
|
19476
19446
|
}
|
|
19477
|
-
const
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19447
|
+
const selectedType = this.getReportTypeFromQueryString();
|
|
19448
|
+
const selectedRadio = this.reportTypeRadios.find(radio => radio.value === selectedType);
|
|
19449
|
+
if (selectedRadio) {
|
|
19450
|
+
selectedRadio.checked = true;
|
|
19451
|
+
}
|
|
19452
|
+
this.applyFilter(selectedType);
|
|
19453
|
+
this.reportTypeRadios.forEach(radio => {
|
|
19454
|
+
radio.addEventListener('change', () => {
|
|
19455
|
+
this.updateQueryString(radio.value);
|
|
19456
|
+
this.applyFilter(radio.value);
|
|
19457
|
+
});
|
|
19458
|
+
});
|
|
19481
19459
|
}
|
|
19482
19460
|
/**
|
|
19483
|
-
*
|
|
19461
|
+
* Applies the selected report type filter to all products.
|
|
19484
19462
|
*/
|
|
19485
|
-
|
|
19486
|
-
|
|
19463
|
+
applyFilter(reportType) {
|
|
19464
|
+
this.getProducts().forEach(product => {
|
|
19465
|
+
this.filterProduct(product, reportType);
|
|
19466
|
+
});
|
|
19467
|
+
document.dispatchEvent(new CustomEvent('dpr-report-catalogue-filter-changed'));
|
|
19487
19468
|
}
|
|
19488
19469
|
/**
|
|
19489
|
-
*
|
|
19470
|
+
* Filters all rows belonging to a product and determines
|
|
19471
|
+
* whether the product itself should remain visible.
|
|
19490
19472
|
*/
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
}
|
|
19498
|
-
|
|
19499
|
-
class DprReportsCatalogueProductCounts extends DprReportsCatalogueFiltersClass {
|
|
19500
|
-
static getModuleName() {
|
|
19501
|
-
return 'dpr-report-catalogue-product-counts';
|
|
19502
|
-
}
|
|
19503
|
-
initialise() {
|
|
19504
|
-
this.updateCounts();
|
|
19505
|
-
document.addEventListener('dpr-report-catalogue-filter-changed', () => {
|
|
19506
|
-
this.updateCounts();
|
|
19507
|
-
});
|
|
19508
|
-
}
|
|
19509
|
-
updateCounts() {
|
|
19510
|
-
this.getProducts().forEach(product => {
|
|
19511
|
-
const count = this.getVisibleVariantCount(product);
|
|
19512
|
-
const totalElement = product.querySelector('[data-product-variants-total]');
|
|
19513
|
-
if (totalElement) {
|
|
19514
|
-
totalElement.textContent = `(${this.getVariantLabel(count)})`;
|
|
19515
|
-
}
|
|
19473
|
+
filterProduct(product, reportType) {
|
|
19474
|
+
const visibleRows = this.getProductVariants(product).filter(row => {
|
|
19475
|
+
const rowType = row.dataset['reportType'];
|
|
19476
|
+
const showRow = reportType === '' || reportType === 'all' || rowType === reportType;
|
|
19477
|
+
row.classList.toggle(this.showHideClassName, !showRow);
|
|
19478
|
+
return showRow;
|
|
19516
19479
|
});
|
|
19480
|
+
product.classList.toggle(this.showHideClassName, visibleRows.length === 0);
|
|
19517
19481
|
}
|
|
19518
|
-
|
|
19519
|
-
|
|
19482
|
+
/**
|
|
19483
|
+
* Reads the selected report type from the URL query string.
|
|
19484
|
+
*/
|
|
19485
|
+
getReportTypeFromQueryString() {
|
|
19486
|
+
const params = new URLSearchParams(window.location.search);
|
|
19487
|
+
return params.get('report-type') ?? 'all';
|
|
19520
19488
|
}
|
|
19521
|
-
|
|
19522
|
-
|
|
19523
|
-
|
|
19489
|
+
/**
|
|
19490
|
+
* Updates the URL query string with the currently
|
|
19491
|
+
* selected report type.
|
|
19492
|
+
*/
|
|
19493
|
+
updateQueryString(reportType) {
|
|
19494
|
+
const params = new URLSearchParams(window.location.search);
|
|
19495
|
+
if (reportType && reportType !== 'all') {
|
|
19496
|
+
params.set('report-type', reportType);
|
|
19524
19497
|
}
|
|
19525
|
-
|
|
19498
|
+
else {
|
|
19499
|
+
params.delete('report-type');
|
|
19500
|
+
}
|
|
19501
|
+
const queryString = params.toString();
|
|
19502
|
+
window.history.replaceState({}, '', queryString ? `${window.location.pathname}?${queryString}` : window.location.pathname);
|
|
19526
19503
|
}
|
|
19527
19504
|
}
|
|
19528
19505
|
|
|
@@ -19583,7 +19560,84 @@ class DprReportsCatalogueNavigation extends DprReportsCatalogueFiltersClass {
|
|
|
19583
19560
|
}
|
|
19584
19561
|
}
|
|
19585
19562
|
|
|
19586
|
-
|
|
19563
|
+
class DprReportsCatalogueProductCounts extends DprReportsCatalogueFiltersClass {
|
|
19564
|
+
static getModuleName() {
|
|
19565
|
+
return 'dpr-report-catalogue-product-counts';
|
|
19566
|
+
}
|
|
19567
|
+
initialise() {
|
|
19568
|
+
this.updateCounts();
|
|
19569
|
+
document.addEventListener('dpr-report-catalogue-filter-changed', () => {
|
|
19570
|
+
this.updateCounts();
|
|
19571
|
+
});
|
|
19572
|
+
}
|
|
19573
|
+
updateCounts() {
|
|
19574
|
+
this.getProducts().forEach(product => {
|
|
19575
|
+
const count = this.getVisibleVariantCount(product);
|
|
19576
|
+
const totalElement = product.querySelector('[data-product-variants-total]');
|
|
19577
|
+
if (totalElement) {
|
|
19578
|
+
totalElement.textContent = `(${this.getVariantLabel(count)})`;
|
|
19579
|
+
}
|
|
19580
|
+
});
|
|
19581
|
+
}
|
|
19582
|
+
getVariantLabel(count) {
|
|
19583
|
+
return `${count} report${count === 1 ? '' : 's'}`;
|
|
19584
|
+
}
|
|
19585
|
+
getVisibleVariantCount(product) {
|
|
19586
|
+
if (!this.isVisibleProduct(product)) {
|
|
19587
|
+
return 0;
|
|
19588
|
+
}
|
|
19589
|
+
return [...product.querySelectorAll('.dpr-report-catalogue__variant-row')].filter(row => this.isVisibleVariant(row)).length;
|
|
19590
|
+
}
|
|
19591
|
+
}
|
|
19592
|
+
|
|
19593
|
+
/**
|
|
19594
|
+
* Updates catalogue totals whenever filters change.
|
|
19595
|
+
*
|
|
19596
|
+
* Counts only visible products and variants.
|
|
19597
|
+
*/
|
|
19598
|
+
class DprReportsCatalogueTotals extends DprReportsCatalogueFiltersClass {
|
|
19599
|
+
productsTotal;
|
|
19600
|
+
variantsTotal;
|
|
19601
|
+
static getModuleName() {
|
|
19602
|
+
return 'dpr-report-catalogue-totals';
|
|
19603
|
+
}
|
|
19604
|
+
initialise() {
|
|
19605
|
+
this.productsTotal = this.getElement().querySelector('[data-products-total]');
|
|
19606
|
+
this.variantsTotal = this.getElement().querySelector('[data-products-variants]');
|
|
19607
|
+
this.updateTotals();
|
|
19608
|
+
document.addEventListener('dpr-report-catalogue-filter-changed', () => {
|
|
19609
|
+
this.updateTotals();
|
|
19610
|
+
});
|
|
19611
|
+
}
|
|
19612
|
+
/**
|
|
19613
|
+
* Updates visible product and variant counts.
|
|
19614
|
+
*/
|
|
19615
|
+
updateTotals() {
|
|
19616
|
+
if (!this.productsTotal || !this.variantsTotal) {
|
|
19617
|
+
return;
|
|
19618
|
+
}
|
|
19619
|
+
const variantCount = this.getVisibleVariants().length;
|
|
19620
|
+
this.variantsTotal.innerHTML = `<strong>${variantCount}</strong> ${this.pluralise(variantCount, 'report', 'reports')}`;
|
|
19621
|
+
const productCount = this.getVisibleProducts().length;
|
|
19622
|
+
this.productsTotal.innerHTML = `<strong>${productCount}</strong> ${this.pluralise(productCount, 'product', 'products')}`;
|
|
19623
|
+
}
|
|
19624
|
+
/**
|
|
19625
|
+
* Returns all visible products.
|
|
19626
|
+
*/
|
|
19627
|
+
getVisibleProducts() {
|
|
19628
|
+
return this.getProducts().filter(product => this.isVisibleProduct(product));
|
|
19629
|
+
}
|
|
19630
|
+
/**
|
|
19631
|
+
* Returns all visible variants.
|
|
19632
|
+
*/
|
|
19633
|
+
getVisibleVariants() {
|
|
19634
|
+
return this.getVariants().filter(variant => this.isVisibleVariant(variant));
|
|
19635
|
+
}
|
|
19636
|
+
pluralise(count, singular, plural) {
|
|
19637
|
+
return count === 1 ? singular : plural;
|
|
19638
|
+
}
|
|
19639
|
+
}
|
|
19640
|
+
|
|
19587
19641
|
/* eslint-disable no-new */
|
|
19588
19642
|
/* global dayjs */
|
|
19589
19643
|
/**
|