katalyst-govuk-formbuilder 1.12.1 → 1.14.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ebff84adc49430cbde51bc966776bcf31941cd924c683ad08a02e339bdb568b
4
- data.tar.gz: 6074316978d6aec2f8607e9651c1c1e69fff509ce9f4ae46a8c00c402df5299b
3
+ metadata.gz: da880fe6a065ccf8448421e041d1c32f634eaaa1a2fd01487ab4effb71747350
4
+ data.tar.gz: c908724012b845f60d31f8fe4c5390b4ba8b1402dc36c90b3c49b43e19f555e1
5
5
  SHA512:
6
- metadata.gz: b83c4d07ca815bc5c4e961daa13dda61c157fec1442b7c942d3d8941cb81037c553456c97a4ac14036766f11df5e50c3b1b81ea7652b246632f380037a78aa58
7
- data.tar.gz: a4c35916d2aeb5dfa9bcccd33692046f05dc549e83b87ac2225af5d848fd6dd5092ac1100b0f4df82341b20294f543d506400e2c5c7b0d9709358a912a284c2b
6
+ metadata.gz: 5d919c4cd82b24cd149019fd99b4b423b284fcaeac98dd302071051657255f4f4927c1d6f19126b517f50d36aa98b3b6670b971214d3f9c687fec8c1c153db2f
7
+ data.tar.gz: bdfa418fa809c75069efc79296bfce96a3321adf3d481be56303df42aa3785d75af1179d9b6c1cb9603f4a5da609b3496763fb924bfb97fc25096af3e60b89eb
@@ -43,7 +43,7 @@
43
43
 
44
44
 
45
45
  :root {
46
- --govuk-frontend-version: "5.6.0";
46
+ --govuk-frontend-version: "5.8.0";
47
47
  --govuk-frontend-breakpoint-mobile: 20rem;
48
48
  --govuk-frontend-breakpoint-tablet: 40.0625rem;
49
49
  --govuk-frontend-breakpoint-desktop: 48.0625rem;
@@ -1,69 +1,3 @@
1
- function normaliseString(value, property) {
2
- const trimmedValue = value ? value.trim() : '';
3
- let output;
4
- let outputType = property == null ? void 0 : property.type;
5
- if (!outputType) {
6
- if (['true', 'false'].includes(trimmedValue)) {
7
- outputType = 'boolean';
8
- }
9
- if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
10
- outputType = 'number';
11
- }
12
- }
13
- switch (outputType) {
14
- case 'boolean':
15
- output = trimmedValue === 'true';
16
- break;
17
- case 'number':
18
- output = Number(trimmedValue);
19
- break;
20
- default:
21
- output = value;
22
- }
23
- return output;
24
- }
25
-
26
- function mergeConfigs(...configObjects) {
27
- const formattedConfigObject = {};
28
- for (const configObject of configObjects) {
29
- for (const key of Object.keys(configObject)) {
30
- const option = formattedConfigObject[key];
31
- const override = configObject[key];
32
- if (isObject(option) && isObject(override)) {
33
- formattedConfigObject[key] = mergeConfigs(option, override);
34
- } else {
35
- formattedConfigObject[key] = override;
36
- }
37
- }
38
- }
39
- return formattedConfigObject;
40
- }
41
- function extractConfigByNamespace(Component, dataset, namespace) {
42
- const property = Component.schema.properties[namespace];
43
- if ((property == null ? void 0 : property.type) !== 'object') {
44
- return;
45
- }
46
- const newObject = {
47
- [namespace]: ({})
48
- };
49
- for (const [key, value] of Object.entries(dataset)) {
50
- let current = newObject;
51
- const keyParts = key.split('.');
52
- for (const [index, name] of keyParts.entries()) {
53
- if (typeof current === 'object') {
54
- if (index < keyParts.length - 1) {
55
- if (!isObject(current[name])) {
56
- current[name] = {};
57
- }
58
- current = current[name];
59
- } else if (key !== namespace) {
60
- current[name] = normaliseString(value);
61
- }
62
- }
63
- }
64
- }
65
- return newObject[namespace];
66
- }
67
1
  function getFragmentFromUrl(url) {
68
2
  if (!url.includes('#')) {
69
3
  return undefined;
@@ -94,50 +28,33 @@ function setFocus($element, options = {}) {
94
28
  (_options$onBeforeFocu = options.onBeforeFocus) == null || _options$onBeforeFocu.call($element);
95
29
  $element.focus();
96
30
  }
31
+ function isInitialised($root, moduleName) {
32
+ return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
33
+ }
34
+
35
+ /**
36
+ * Checks if GOV.UK Frontend is supported on this page
37
+ *
38
+ * Some browsers will load and run our JavaScript but GOV.UK Frontend
39
+ * won't be supported.
40
+ *
41
+ * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
42
+ * @returns {boolean} Whether GOV.UK Frontend is supported on this page
43
+ */
97
44
  function isSupported($scope = document.body) {
98
45
  if (!$scope) {
99
46
  return false;
100
47
  }
101
48
  return $scope.classList.contains('govuk-frontend-supported');
102
49
  }
103
- function validateConfig(schema, config) {
104
- const validationErrors = [];
105
- for (const [name, conditions] of Object.entries(schema)) {
106
- const errors = [];
107
- if (Array.isArray(conditions)) {
108
- for (const {
109
- required,
110
- errorMessage
111
- } of conditions) {
112
- if (!required.every(key => !!config[key])) {
113
- errors.push(errorMessage);
114
- }
115
- }
116
- if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
117
- validationErrors.push(...errors);
118
- }
119
- }
120
- }
121
- return validationErrors;
122
- }
123
50
  function isArray(option) {
124
51
  return Array.isArray(option);
125
52
  }
126
53
  function isObject(option) {
127
54
  return !!option && typeof option === 'object' && !isArray(option);
128
55
  }
129
-
130
- function normaliseDataset(Component, dataset) {
131
- const out = {};
132
- for (const [field, property] of Object.entries(Component.schema.properties)) {
133
- if (field in dataset) {
134
- out[field] = normaliseString(dataset[field], property);
135
- }
136
- if ((property == null ? void 0 : property.type) === 'object') {
137
- out[field] = extractConfigByNamespace(Component, dataset, field);
138
- }
139
- }
140
- return out;
56
+ function formatErrorMessage(Component, message) {
57
+ return `${Component.moduleName}: ${message}`;
141
58
  }
142
59
 
143
60
  class GOVUKFrontendError extends Error {
@@ -169,35 +86,214 @@ class ElementError extends GOVUKFrontendError {
169
86
  let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
170
87
  if (typeof messageOrOptions === 'object') {
171
88
  const {
172
- componentName,
89
+ component,
173
90
  identifier,
174
91
  element,
175
92
  expectedType
176
93
  } = messageOrOptions;
177
- message = `${componentName}: ${identifier}`;
94
+ message = identifier;
178
95
  message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
96
+ message = formatErrorMessage(component, message);
179
97
  }
180
98
  super(message);
181
99
  this.name = 'ElementError';
182
100
  }
183
101
  }
102
+ class InitError extends GOVUKFrontendError {
103
+ constructor(componentOrMessage) {
104
+ const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
105
+ super(message);
106
+ this.name = 'InitError';
107
+ }
108
+ }
184
109
 
185
110
  class GOVUKFrontendComponent {
186
- constructor() {
187
- this.checkSupport();
111
+ /**
112
+ * Returns the root element of the component
113
+ *
114
+ * @protected
115
+ * @returns {RootElementType} - the root element of component
116
+ */
117
+ get $root() {
118
+ return this._$root;
119
+ }
120
+ constructor($root) {
121
+ this._$root = undefined;
122
+ const childConstructor = this.constructor;
123
+ if (typeof childConstructor.moduleName !== 'string') {
124
+ throw new InitError(`\`moduleName\` not defined in component`);
125
+ }
126
+ if (!($root instanceof childConstructor.elementType)) {
127
+ throw new ElementError({
128
+ element: $root,
129
+ component: childConstructor,
130
+ identifier: 'Root element (`$root`)',
131
+ expectedType: childConstructor.elementType.name
132
+ });
133
+ } else {
134
+ this._$root = $root;
135
+ }
136
+ childConstructor.checkSupport();
137
+ this.checkInitialised();
138
+ const moduleName = childConstructor.moduleName;
139
+ this.$root.setAttribute(`data-${moduleName}-init`, '');
140
+ }
141
+ checkInitialised() {
142
+ const constructor = this.constructor;
143
+ const moduleName = constructor.moduleName;
144
+ if (moduleName && isInitialised(this.$root, moduleName)) {
145
+ throw new InitError(constructor);
146
+ }
188
147
  }
189
- checkSupport() {
148
+ static checkSupport() {
190
149
  if (!isSupported()) {
191
150
  throw new SupportError();
192
151
  }
193
152
  }
194
153
  }
195
154
 
155
+ /**
156
+ * @typedef ChildClass
157
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
158
+ */
159
+
160
+ /**
161
+ * @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor
162
+ */
163
+ GOVUKFrontendComponent.elementType = HTMLElement;
164
+
165
+ const configOverride = Symbol.for('configOverride');
166
+ class ConfigurableComponent extends GOVUKFrontendComponent {
167
+ [configOverride](param) {
168
+ return {};
169
+ }
170
+
171
+ /**
172
+ * Returns the root element of the component
173
+ *
174
+ * @protected
175
+ * @returns {ConfigurationType} - the root element of component
176
+ */
177
+ get config() {
178
+ return this._config;
179
+ }
180
+ constructor($root, config) {
181
+ super($root);
182
+ this._config = undefined;
183
+ const childConstructor = this.constructor;
184
+ if (typeof childConstructor.defaults === 'undefined') {
185
+ throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
186
+ }
187
+ const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
188
+ this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
189
+ }
190
+ }
191
+ function normaliseString(value, property) {
192
+ const trimmedValue = value ? value.trim() : '';
193
+ let output;
194
+ let outputType = property == null ? undefined : property.type;
195
+ if (!outputType) {
196
+ if (['true', 'false'].includes(trimmedValue)) {
197
+ outputType = 'boolean';
198
+ }
199
+ if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
200
+ outputType = 'number';
201
+ }
202
+ }
203
+ switch (outputType) {
204
+ case 'boolean':
205
+ output = trimmedValue === 'true';
206
+ break;
207
+ case 'number':
208
+ output = Number(trimmedValue);
209
+ break;
210
+ default:
211
+ output = value;
212
+ }
213
+ return output;
214
+ }
215
+ function normaliseDataset(Component, dataset) {
216
+ if (typeof Component.schema === 'undefined') {
217
+ throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
218
+ }
219
+ const out = {};
220
+ for (const [field, property] of Object.entries(Component.schema.properties)) {
221
+ if (field in dataset) {
222
+ out[field] = normaliseString(dataset[field], property);
223
+ }
224
+ if ((property == null ? undefined : property.type) === 'object') {
225
+ out[field] = extractConfigByNamespace(Component.schema, dataset, field);
226
+ }
227
+ }
228
+ return out;
229
+ }
230
+ function mergeConfigs(...configObjects) {
231
+ const formattedConfigObject = {};
232
+ for (const configObject of configObjects) {
233
+ for (const key of Object.keys(configObject)) {
234
+ const option = formattedConfigObject[key];
235
+ const override = configObject[key];
236
+ if (isObject(option) && isObject(override)) {
237
+ formattedConfigObject[key] = mergeConfigs(option, override);
238
+ } else {
239
+ formattedConfigObject[key] = override;
240
+ }
241
+ }
242
+ }
243
+ return formattedConfigObject;
244
+ }
245
+ function validateConfig(schema, config) {
246
+ const validationErrors = [];
247
+ for (const [name, conditions] of Object.entries(schema)) {
248
+ const errors = [];
249
+ if (Array.isArray(conditions)) {
250
+ for (const {
251
+ required,
252
+ errorMessage
253
+ } of conditions) {
254
+ if (!required.every(key => !!config[key])) {
255
+ errors.push(errorMessage);
256
+ }
257
+ }
258
+ if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
259
+ validationErrors.push(...errors);
260
+ }
261
+ }
262
+ }
263
+ return validationErrors;
264
+ }
265
+ function extractConfigByNamespace(schema, dataset, namespace) {
266
+ const property = schema.properties[namespace];
267
+ if ((property == null ? undefined : property.type) !== 'object') {
268
+ return;
269
+ }
270
+ const newObject = {
271
+ [namespace]: ({})
272
+ };
273
+ for (const [key, value] of Object.entries(dataset)) {
274
+ let current = newObject;
275
+ const keyParts = key.split('.');
276
+ for (const [index, name] of keyParts.entries()) {
277
+ if (typeof current === 'object') {
278
+ if (index < keyParts.length - 1) {
279
+ if (!isObject(current[name])) {
280
+ current[name] = {};
281
+ }
282
+ current = current[name];
283
+ } else if (key !== namespace) {
284
+ current[name] = normaliseString(value);
285
+ }
286
+ }
287
+ }
288
+ }
289
+ return newObject[namespace];
290
+ }
291
+
196
292
  class I18n {
197
293
  constructor(translations = {}, config = {}) {
198
294
  var _config$locale;
199
- this.translations = void 0;
200
- this.locale = void 0;
295
+ this.translations = undefined;
296
+ this.locale = undefined;
201
297
  this.translations = translations;
202
298
  this.locale = (_config$locale = config.locale) != null ? _config$locale : document.documentElement.lang || 'en';
203
299
  }
@@ -206,7 +302,7 @@ class I18n {
206
302
  throw new Error('i18n: lookup key missing');
207
303
  }
208
304
  let translation = this.translations[lookupKey];
209
- if (typeof (options == null ? void 0 : options.count) === 'number' && typeof translation === 'object') {
305
+ if (typeof (options == null ? undefined : options.count) === 'number' && typeof translation === 'object') {
210
306
  const translationPluralForm = translation[this.getPluralSuffix(lookupKey, options.count)];
211
307
  if (translationPluralForm) {
212
308
  translation = translationPluralForm;
@@ -392,28 +488,18 @@ const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
392
488
  * JavaScript enhancements for the Button component
393
489
  *
394
490
  * @preserve
491
+ * @augments ConfigurableComponent<ButtonConfig>
395
492
  */
396
- class Button extends GOVUKFrontendComponent {
493
+ class Button extends ConfigurableComponent {
397
494
  /**
398
- * @param {Element | null} $module - HTML element to use for button
495
+ * @param {Element | null} $root - HTML element to use for button
399
496
  * @param {ButtonConfig} [config] - Button config
400
497
  */
401
- constructor($module, config = {}) {
402
- super();
403
- this.$module = void 0;
404
- this.config = void 0;
498
+ constructor($root, config = {}) {
499
+ super($root, config);
405
500
  this.debounceFormSubmitTimer = null;
406
- if (!($module instanceof HTMLElement)) {
407
- throw new ElementError({
408
- componentName: 'Button',
409
- element: $module,
410
- identifier: 'Root element (`$module`)'
411
- });
412
- }
413
- this.$module = $module;
414
- this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, $module.dataset));
415
- this.$module.addEventListener('keydown', event => this.handleKeyDown(event));
416
- this.$module.addEventListener('click', event => this.debounce(event));
501
+ this.$root.addEventListener('keydown', event => this.handleKeyDown(event));
502
+ this.$root.addEventListener('click', event => this.debounce(event));
417
503
  }
418
504
  handleKeyDown(event) {
419
505
  const $target = event.target;
@@ -448,7 +534,7 @@ class Button extends GOVUKFrontendComponent {
448
534
  */
449
535
 
450
536
  /**
451
- * @typedef {import('../../common/index.mjs').Schema} Schema
537
+ * @typedef {import('../../common/configuration.mjs').Schema} Schema
452
538
  */
453
539
  Button.moduleName = 'govuk-button';
454
540
  Button.defaults = Object.freeze({
@@ -478,65 +564,58 @@ function closestAttributeValue($element, attributeName) {
478
564
  * of the available characters/words has been entered.
479
565
  *
480
566
  * @preserve
567
+ * @augments ConfigurableComponent<CharacterCountConfig>
481
568
  */
482
- class CharacterCount extends GOVUKFrontendComponent {
569
+ class CharacterCount extends ConfigurableComponent {
570
+ [configOverride](datasetConfig) {
571
+ let configOverrides = {};
572
+ if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
573
+ configOverrides = {
574
+ maxlength: undefined,
575
+ maxwords: undefined
576
+ };
577
+ }
578
+ return configOverrides;
579
+ }
580
+
483
581
  /**
484
- * @param {Element | null} $module - HTML element to use for character count
582
+ * @param {Element | null} $root - HTML element to use for character count
485
583
  * @param {CharacterCountConfig} [config] - Character count config
486
584
  */
487
- constructor($module, config = {}) {
585
+ constructor($root, config = {}) {
488
586
  var _ref, _this$config$maxwords;
489
- super();
490
- this.$module = void 0;
491
- this.$textarea = void 0;
492
- this.$visibleCountMessage = void 0;
493
- this.$screenReaderCountMessage = void 0;
587
+ super($root, config);
588
+ this.$textarea = undefined;
589
+ this.$visibleCountMessage = undefined;
590
+ this.$screenReaderCountMessage = undefined;
494
591
  this.lastInputTimestamp = null;
495
592
  this.lastInputValue = '';
496
593
  this.valueChecker = null;
497
- this.config = void 0;
498
- this.i18n = void 0;
499
- this.maxLength = void 0;
500
- if (!($module instanceof HTMLElement)) {
501
- throw new ElementError({
502
- componentName: 'Character count',
503
- element: $module,
504
- identifier: 'Root element (`$module`)'
505
- });
506
- }
507
- const $textarea = $module.querySelector('.govuk-js-character-count');
594
+ this.i18n = undefined;
595
+ this.maxLength = undefined;
596
+ const $textarea = this.$root.querySelector('.govuk-js-character-count');
508
597
  if (!($textarea instanceof HTMLTextAreaElement || $textarea instanceof HTMLInputElement)) {
509
598
  throw new ElementError({
510
- componentName: 'Character count',
599
+ component: CharacterCount,
511
600
  element: $textarea,
512
601
  expectedType: 'HTMLTextareaElement or HTMLInputElement',
513
602
  identifier: 'Form field (`.govuk-js-character-count`)'
514
603
  });
515
604
  }
516
- const datasetConfig = normaliseDataset(CharacterCount, $module.dataset);
517
- let configOverrides = {};
518
- if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
519
- configOverrides = {
520
- maxlength: undefined,
521
- maxwords: undefined
522
- };
523
- }
524
- this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
525
605
  const errors = validateConfig(CharacterCount.schema, this.config);
526
606
  if (errors[0]) {
527
- throw new ConfigError(`Character count: ${errors[0]}`);
607
+ throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
528
608
  }
529
609
  this.i18n = new I18n(this.config.i18n, {
530
- locale: closestAttributeValue($module, 'lang')
610
+ locale: closestAttributeValue(this.$root, 'lang')
531
611
  });
532
612
  this.maxLength = (_ref = (_this$config$maxwords = this.config.maxwords) != null ? _this$config$maxwords : this.config.maxlength) != null ? _ref : Infinity;
533
- this.$module = $module;
534
613
  this.$textarea = $textarea;
535
614
  const textareaDescriptionId = `${this.$textarea.id}-info`;
536
615
  const $textareaDescription = document.getElementById(textareaDescriptionId);
537
616
  if (!$textareaDescription) {
538
617
  throw new ElementError({
539
- componentName: 'Character count',
618
+ component: CharacterCount,
540
619
  element: $textareaDescription,
541
620
  identifier: `Count message (\`id="${textareaDescriptionId}"\`)`
542
621
  });
@@ -708,7 +787,7 @@ class CharacterCount extends GOVUKFrontendComponent {
708
787
  */
709
788
 
710
789
  /**
711
- * @typedef {import('../../common/index.mjs').Schema} Schema
790
+ * @typedef {import('../../common/configuration.mjs').Schema} Schema
712
791
  * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
713
792
  */
714
793
  CharacterCount.moduleName = 'govuk-character-count';
@@ -780,27 +859,18 @@ class Checkboxes extends GOVUKFrontendComponent {
780
859
  * (for example if the user has navigated back), and set up event handlers to
781
860
  * keep the reveal in sync with the checkbox state.
782
861
  *
783
- * @param {Element | null} $module - HTML element to use for checkboxes
862
+ * @param {Element | null} $root - HTML element to use for checkboxes
784
863
  */
785
- constructor($module) {
786
- super();
787
- this.$module = void 0;
788
- this.$inputs = void 0;
789
- if (!($module instanceof HTMLElement)) {
790
- throw new ElementError({
791
- componentName: 'Checkboxes',
792
- element: $module,
793
- identifier: 'Root element (`$module`)'
794
- });
795
- }
796
- const $inputs = $module.querySelectorAll('input[type="checkbox"]');
864
+ constructor($root) {
865
+ super($root);
866
+ this.$inputs = undefined;
867
+ const $inputs = this.$root.querySelectorAll('input[type="checkbox"]');
797
868
  if (!$inputs.length) {
798
869
  throw new ElementError({
799
- componentName: 'Checkboxes',
870
+ component: Checkboxes,
800
871
  identifier: 'Form inputs (`<input type="checkbox">`)'
801
872
  });
802
873
  }
803
- this.$module = $module;
804
874
  this.$inputs = $inputs;
805
875
  this.$inputs.forEach($input => {
806
876
  const targetId = $input.getAttribute('data-aria-controls');
@@ -809,7 +879,7 @@ class Checkboxes extends GOVUKFrontendComponent {
809
879
  }
810
880
  if (!document.getElementById(targetId)) {
811
881
  throw new ElementError({
812
- componentName: 'Checkboxes',
882
+ component: Checkboxes,
813
883
  identifier: `Conditional reveal (\`id="${targetId}"\`)`
814
884
  });
815
885
  }
@@ -818,7 +888,7 @@ class Checkboxes extends GOVUKFrontendComponent {
818
888
  });
819
889
  window.addEventListener('pageshow', () => this.syncAllConditionalReveals());
820
890
  this.syncAllConditionalReveals();
821
- this.$module.addEventListener('click', event => this.handleClick(event));
891
+ this.$root.addEventListener('click', event => this.handleClick(event));
822
892
  }
823
893
  syncAllConditionalReveals() {
824
894
  this.$inputs.forEach($input => this.syncConditionalRevealWithInputState($input));
@@ -884,29 +954,19 @@ Checkboxes.moduleName = 'govuk-checkboxes';
884
954
  * configuration.
885
955
  *
886
956
  * @preserve
957
+ * @augments ConfigurableComponent<ErrorSummaryConfig>
887
958
  */
888
- class ErrorSummary extends GOVUKFrontendComponent {
959
+ class ErrorSummary extends ConfigurableComponent {
889
960
  /**
890
- * @param {Element | null} $module - HTML element to use for error summary
961
+ * @param {Element | null} $root - HTML element to use for error summary
891
962
  * @param {ErrorSummaryConfig} [config] - Error summary config
892
963
  */
893
- constructor($module, config = {}) {
894
- super();
895
- this.$module = void 0;
896
- this.config = void 0;
897
- if (!($module instanceof HTMLElement)) {
898
- throw new ElementError({
899
- componentName: 'Error summary',
900
- element: $module,
901
- identifier: 'Root element (`$module`)'
902
- });
903
- }
904
- this.$module = $module;
905
- this.config = mergeConfigs(ErrorSummary.defaults, config, normaliseDataset(ErrorSummary, $module.dataset));
964
+ constructor($root, config = {}) {
965
+ super($root, config);
906
966
  if (!this.config.disableAutoFocus) {
907
- setFocus(this.$module);
967
+ setFocus(this.$root);
908
968
  }
909
- this.$module.addEventListener('click', event => this.handleClick(event));
969
+ this.$root.addEventListener('click', event => this.handleClick(event));
910
970
  }
911
971
  handleClick(event) {
912
972
  const $target = event.target;
@@ -969,7 +1029,7 @@ class ErrorSummary extends GOVUKFrontendComponent {
969
1029
  */
970
1030
 
971
1031
  /**
972
- * @typedef {import('../../common/index.mjs').Schema} Schema
1032
+ * @typedef {import('../../common/configuration.mjs').Schema} Schema
973
1033
  */
974
1034
  ErrorSummary.moduleName = 'govuk-error-summary';
975
1035
  ErrorSummary.defaults = Object.freeze({
@@ -987,31 +1047,23 @@ ErrorSummary.schema = Object.freeze({
987
1047
  * Password input component
988
1048
  *
989
1049
  * @preserve
1050
+ * @augments ConfigurableComponent<PasswordInputConfig>
990
1051
  */
991
- class PasswordInput extends GOVUKFrontendComponent {
1052
+ class PasswordInput extends ConfigurableComponent {
992
1053
  /**
993
- * @param {Element | null} $module - HTML element to use for password input
1054
+ * @param {Element | null} $root - HTML element to use for password input
994
1055
  * @param {PasswordInputConfig} [config] - Password input config
995
1056
  */
996
- constructor($module, config = {}) {
997
- super();
998
- this.$module = void 0;
999
- this.config = void 0;
1000
- this.i18n = void 0;
1001
- this.$input = void 0;
1002
- this.$showHideButton = void 0;
1003
- this.$screenReaderStatusMessage = void 0;
1004
- if (!($module instanceof HTMLElement)) {
1005
- throw new ElementError({
1006
- componentName: 'Password input',
1007
- element: $module,
1008
- identifier: 'Root element (`$module`)'
1009
- });
1010
- }
1011
- const $input = $module.querySelector('.govuk-js-password-input-input');
1057
+ constructor($root, config = {}) {
1058
+ super($root, config);
1059
+ this.i18n = undefined;
1060
+ this.$input = undefined;
1061
+ this.$showHideButton = undefined;
1062
+ this.$screenReaderStatusMessage = undefined;
1063
+ const $input = this.$root.querySelector('.govuk-js-password-input-input');
1012
1064
  if (!($input instanceof HTMLInputElement)) {
1013
1065
  throw new ElementError({
1014
- componentName: 'Password input',
1066
+ component: PasswordInput,
1015
1067
  element: $input,
1016
1068
  expectedType: 'HTMLInputElement',
1017
1069
  identifier: 'Form field (`.govuk-js-password-input-input`)'
@@ -1020,10 +1072,10 @@ class PasswordInput extends GOVUKFrontendComponent {
1020
1072
  if ($input.type !== 'password') {
1021
1073
  throw new ElementError('Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.');
1022
1074
  }
1023
- const $showHideButton = $module.querySelector('.govuk-js-password-input-toggle');
1075
+ const $showHideButton = this.$root.querySelector('.govuk-js-password-input-toggle');
1024
1076
  if (!($showHideButton instanceof HTMLButtonElement)) {
1025
1077
  throw new ElementError({
1026
- componentName: 'Password input',
1078
+ component: PasswordInput,
1027
1079
  element: $showHideButton,
1028
1080
  expectedType: 'HTMLButtonElement',
1029
1081
  identifier: 'Button (`.govuk-js-password-input-toggle`)'
@@ -1032,12 +1084,10 @@ class PasswordInput extends GOVUKFrontendComponent {
1032
1084
  if ($showHideButton.type !== 'button') {
1033
1085
  throw new ElementError('Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.');
1034
1086
  }
1035
- this.$module = $module;
1036
1087
  this.$input = $input;
1037
1088
  this.$showHideButton = $showHideButton;
1038
- this.config = mergeConfigs(PasswordInput.defaults, config, normaliseDataset(PasswordInput, $module.dataset));
1039
1089
  this.i18n = new I18n(this.config.i18n, {
1040
- locale: closestAttributeValue($module, 'lang')
1090
+ locale: closestAttributeValue(this.$root, 'lang')
1041
1091
  });
1042
1092
  this.$showHideButton.removeAttribute('hidden');
1043
1093
  const $screenReaderStatusMessage = document.createElement('div');
@@ -1115,7 +1165,7 @@ class PasswordInput extends GOVUKFrontendComponent {
1115
1165
  */
1116
1166
 
1117
1167
  /**
1118
- * @typedef {import('../../common/index.mjs').Schema} Schema
1168
+ * @typedef {import('../../common/configuration.mjs').Schema} Schema
1119
1169
  * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
1120
1170
  */
1121
1171
  PasswordInput.moduleName = 'govuk-password-input';
@@ -1155,27 +1205,18 @@ class Radios extends GOVUKFrontendComponent {
1155
1205
  * (for example if the user has navigated back), and set up event handlers to
1156
1206
  * keep the reveal in sync with the radio state.
1157
1207
  *
1158
- * @param {Element | null} $module - HTML element to use for radios
1208
+ * @param {Element | null} $root - HTML element to use for radios
1159
1209
  */
1160
- constructor($module) {
1161
- super();
1162
- this.$module = void 0;
1163
- this.$inputs = void 0;
1164
- if (!($module instanceof HTMLElement)) {
1165
- throw new ElementError({
1166
- componentName: 'Radios',
1167
- element: $module,
1168
- identifier: 'Root element (`$module`)'
1169
- });
1170
- }
1171
- const $inputs = $module.querySelectorAll('input[type="radio"]');
1210
+ constructor($root) {
1211
+ super($root);
1212
+ this.$inputs = undefined;
1213
+ const $inputs = this.$root.querySelectorAll('input[type="radio"]');
1172
1214
  if (!$inputs.length) {
1173
1215
  throw new ElementError({
1174
- componentName: 'Radios',
1216
+ component: Radios,
1175
1217
  identifier: 'Form inputs (`<input type="radio">`)'
1176
1218
  });
1177
1219
  }
1178
- this.$module = $module;
1179
1220
  this.$inputs = $inputs;
1180
1221
  this.$inputs.forEach($input => {
1181
1222
  const targetId = $input.getAttribute('data-aria-controls');
@@ -1184,7 +1225,7 @@ class Radios extends GOVUKFrontendComponent {
1184
1225
  }
1185
1226
  if (!document.getElementById(targetId)) {
1186
1227
  throw new ElementError({
1187
- componentName: 'Radios',
1228
+ component: Radios,
1188
1229
  identifier: `Conditional reveal (\`id="${targetId}"\`)`
1189
1230
  });
1190
1231
  }
@@ -1193,7 +1234,7 @@ class Radios extends GOVUKFrontendComponent {
1193
1234
  });
1194
1235
  window.addEventListener('pageshow', () => this.syncAllConditionalReveals());
1195
1236
  this.syncAllConditionalReveals();
1196
- this.$module.addEventListener('click', event => this.handleClick(event));
1237
+ this.$root.addEventListener('click', event => this.handleClick(event));
1197
1238
  }
1198
1239
  syncAllConditionalReveals() {
1199
1240
  this.$inputs.forEach($input => this.syncConditionalRevealWithInputState($input));
@@ -1,10 +1,11 @@
1
- function e(e,t){const n=e?e.trim():"";let o,s=null==t?void 0:t.type;switch(s||(["true","false"].includes(n)&&(s="boolean"),n.length>0&&isFinite(Number(n))&&(s="number")),s){case"boolean":o="true"===n;break;case"number":o=Number(n);break;default:o=e}return o}function t(...e){const n={};for(const o of e)for(const e of Object.keys(o)){const i=n[e],r=o[e];s(i)&&s(r)?n[e]=t(i,r):n[e]=r}return n}function n(t,n,o){const i=t.schema.properties[o];if("object"!==(null==i?void 0:i.type))return;const r={[o]:{}};for(const[t,i]of Object.entries(n)){let n=r;const a=t.split(".");for(const[r,l]of a.entries())"object"==typeof n&&(r<a.length-1?(s(n[l])||(n[l]={}),n=n[l]):t!==o&&(n[l]=e(i)))}return r[o]}function o(e=document.body){return!!e&&e.classList.contains("govuk-frontend-supported")}function s(e){return!!e&&"object"==typeof e&&!function(e){return Array.isArray(e)}(e)}function i(t,o){const s={};for(const[i,r]of Object.entries(t.schema.properties))i in o&&(s[i]=e(o[i],r)),"object"===(null==r?void 0:r.type)&&(s[i]=n(t,o,i));return s}class r extends Error{constructor(...e){super(...e),this.name="GOVUKFrontendError"}}class a extends r{constructor(e=document.body){const t="noModule"in HTMLScriptElement.prototype?'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet':"GOV.UK Frontend is not supported in this browser";super(e?t:'GOV.UK Frontend initialised without `<script type="module">`'),this.name="SupportError"}}class l extends r{constructor(...e){super(...e),this.name="ConfigError"}}class u extends r{constructor(e){let t="string"==typeof e?e:"";if("object"==typeof e){const{componentName:n,identifier:o,element:s,expectedType:i}=e;t=`${n}: ${o}`,t+=s?` is not of type ${null!=i?i:"HTMLElement"}`:" not found"}super(t),this.name="ElementError"}}class c{constructor(){this.checkSupport()}checkSupport(){if(!o())throw new a}}class h{constructor(e={},t={}){var n;this.translations=void 0,this.locale=void 0,this.translations=e,this.locale=null!=(n=t.locale)?n:document.documentElement.lang||"en"}t(e,t){if(!e)throw new Error("i18n: lookup key missing");let n=this.translations[e];if("number"==typeof(null==t?void 0:t.count)&&"object"==typeof n){const o=n[this.getPluralSuffix(e,t.count)];o&&(n=o)}if("string"==typeof n){if(n.match(/%{(.\S+)}/)){if(!t)throw new Error("i18n: cannot replace placeholders in string if no option data provided");return this.replacePlaceholders(n,t)}return n}return e}replacePlaceholders(e,t){const n=Intl.NumberFormat.supportedLocalesOf(this.locale).length?new Intl.NumberFormat(this.locale):void 0;return e.replace(/%{(.\S+)}/g,(function(e,o){if(Object.prototype.hasOwnProperty.call(t,o)){const e=t[o];return!1===e||"number"!=typeof e&&"string"!=typeof e?"":"number"==typeof e?n?n.format(e):`${e}`:e}throw new Error(`i18n: no data found to replace ${e} placeholder in string`)}))}hasIntlPluralRulesSupport(){return Boolean("PluralRules"in window.Intl&&Intl.PluralRules.supportedLocalesOf(this.locale).length)}getPluralSuffix(e,t){if(t=Number(t),!isFinite(t))return"other";const n=this.translations[e],o=this.hasIntlPluralRulesSupport()?new Intl.PluralRules(this.locale).select(t):this.selectPluralFormUsingFallbackRules(t);if("object"==typeof n){if(o in n)return o;if("other"in n)return console.warn(`i18n: Missing plural form ".${o}" for "${this.locale}" locale. Falling back to ".other".`),"other"}throw new Error(`i18n: Plural form ".other" is required for "${this.locale}" locale`)}selectPluralFormUsingFallbackRules(e){e=Math.abs(Math.floor(e));const t=this.getPluralRulesForLocale();return t?h.pluralRules[t](e):"other"}getPluralRulesForLocale(){const e=this.locale.split("-")[0];for(const t in h.pluralRulesMap){const n=h.pluralRulesMap[t];if(n.includes(this.locale)||n.includes(e))return t}}}h.pluralRulesMap={arabic:["ar"],chinese:["my","zh","id","ja","jv","ko","ms","th","vi"],french:["hy","bn","fr","gu","hi","fa","pa","zu"],german:["af","sq","az","eu","bg","ca","da","nl","en","et","fi","ka","de","el","hu","lb","no","so","sw","sv","ta","te","tr","ur"],irish:["ga"],russian:["ru","uk"],scottish:["gd"],spanish:["pt-PT","it","es"],welsh:["cy"]},h.pluralRules={arabic:e=>0===e?"zero":1===e?"one":2===e?"two":e%100>=3&&e%100<=10?"few":e%100>=11&&e%100<=99?"many":"other",chinese:()=>"other",french:e=>0===e||1===e?"one":"other",german:e=>1===e?"one":"other",irish:e=>1===e?"one":2===e?"two":e>=3&&e<=6?"few":e>=7&&e<=10?"many":"other",russian(e){const t=e%100,n=t%10;return 1===n&&11!==t?"one":n>=2&&n<=4&&!(t>=12&&t<=14)?"few":0===n||n>=5&&n<=9||t>=11&&t<=14?"many":"other"},scottish:e=>1===e||11===e?"one":2===e||12===e?"two":e>=3&&e<=10||e>=13&&e<=19?"few":"other",spanish:e=>1===e?"one":e%1e6==0&&0!==e?"many":"other",welsh:e=>0===e?"zero":1===e?"one":2===e?"two":3===e?"few":6===e?"many":"other"};
1
+ function e(e=document.body){return!!e&&e.classList.contains("govuk-frontend-supported")}function t(e){return!!e&&"object"==typeof e&&!function(e){return Array.isArray(e)}(e)}function n(e,t){return`${e.moduleName}: ${t}`}class o extends Error{constructor(...e){super(...e),this.name="GOVUKFrontendError"}}class s extends o{constructor(e=document.body){const t="noModule"in HTMLScriptElement.prototype?'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet':"GOV.UK Frontend is not supported in this browser";super(e?t:'GOV.UK Frontend initialised without `<script type="module">`'),this.name="SupportError"}}class i extends o{constructor(...e){super(...e),this.name="ConfigError"}}class r extends o{constructor(e){let t="string"==typeof e?e:"";if("object"==typeof e){const{component:o,identifier:s,element:i,expectedType:r}=e;t=s,t+=i?` is not of type ${null!=r?r:"HTMLElement"}`:" not found",t=n(o,t)}super(t),this.name="ElementError"}}class a extends o{constructor(e){super("string"==typeof e?e:n(e,"Root element (`$root`) already initialised")),this.name="InitError"}}class u{get $root(){return this._$root}constructor(e){this._$root=void 0;const t=this.constructor;if("string"!=typeof t.moduleName)throw new a("`moduleName` not defined in component");if(!(e instanceof t.elementType))throw new r({element:e,component:t,identifier:"Root element (`$root`)",expectedType:t.elementType.name});this._$root=e,t.checkSupport(),this.checkInitialised();const n=t.moduleName;this.$root.setAttribute(`data-${n}-init`,"")}checkInitialised(){const e=this.constructor,t=e.moduleName;if(t&&function(e,t){return e instanceof HTMLElement&&e.hasAttribute(`data-${t}-init`)}(this.$root,t))throw new a(e)}static checkSupport(){if(!e())throw new s}}u.elementType=HTMLElement;const c=Symbol.for("configOverride");class l extends u{[c](e){return{}}get config(){return this._config}constructor(e,t){super(e),this._config=void 0;const o=this.constructor;if(void 0===o.defaults)throw new i(n(o,"Config passed as parameter into constructor but no defaults defined"));const s=function(e,t){if(void 0===e.schema)throw new i(n(e,"Config passed as parameter into constructor but no schema defined"));const o={};for(const[n,s]of Object.entries(e.schema.properties))n in t&&(o[n]=h(t[n],s)),"object"===(null==s?void 0:s.type)&&(o[n]=p(e.schema,t,n));return o}(o,this._$root.dataset);this._config=d(o.defaults,null!=t?t:{},this[c](s),s)}}function h(e,t){const n=e?e.trim():"";let o,s=null==t?void 0:t.type;switch(s||(["true","false"].includes(n)&&(s="boolean"),n.length>0&&isFinite(Number(n))&&(s="number")),s){case"boolean":o="true"===n;break;case"number":o=Number(n);break;default:o=e}return o}function d(...e){const n={};for(const o of e)for(const e of Object.keys(o)){const s=n[e],i=o[e];t(s)&&t(i)?n[e]=d(s,i):n[e]=i}return n}function p(e,n,o){const s=e.properties[o];if("object"!==(null==s?void 0:s.type))return;const i={[o]:{}};for(const[e,s]of Object.entries(n)){let n=i;const r=e.split(".");for(const[i,a]of r.entries())"object"==typeof n&&(i<r.length-1?(t(n[a])||(n[a]={}),n=n[a]):e!==o&&(n[a]=h(s)))}return i[o]}class m{constructor(e={},t={}){var n;this.translations=void 0,this.locale=void 0,this.translations=e,this.locale=null!=(n=t.locale)?n:document.documentElement.lang||"en"}t(e,t){if(!e)throw new Error("i18n: lookup key missing");let n=this.translations[e];if("number"==typeof(null==t?void 0:t.count)&&"object"==typeof n){const o=n[this.getPluralSuffix(e,t.count)];o&&(n=o)}if("string"==typeof n){if(n.match(/%{(.\S+)}/)){if(!t)throw new Error("i18n: cannot replace placeholders in string if no option data provided");return this.replacePlaceholders(n,t)}return n}return e}replacePlaceholders(e,t){const n=Intl.NumberFormat.supportedLocalesOf(this.locale).length?new Intl.NumberFormat(this.locale):void 0;return e.replace(/%{(.\S+)}/g,(function(e,o){if(Object.prototype.hasOwnProperty.call(t,o)){const e=t[o];return!1===e||"number"!=typeof e&&"string"!=typeof e?"":"number"==typeof e?n?n.format(e):`${e}`:e}throw new Error(`i18n: no data found to replace ${e} placeholder in string`)}))}hasIntlPluralRulesSupport(){return Boolean("PluralRules"in window.Intl&&Intl.PluralRules.supportedLocalesOf(this.locale).length)}getPluralSuffix(e,t){if(t=Number(t),!isFinite(t))return"other";const n=this.translations[e],o=this.hasIntlPluralRulesSupport()?new Intl.PluralRules(this.locale).select(t):this.selectPluralFormUsingFallbackRules(t);if("object"==typeof n){if(o in n)return o;if("other"in n)return console.warn(`i18n: Missing plural form ".${o}" for "${this.locale}" locale. Falling back to ".other".`),"other"}throw new Error(`i18n: Plural form ".other" is required for "${this.locale}" locale`)}selectPluralFormUsingFallbackRules(e){e=Math.abs(Math.floor(e));const t=this.getPluralRulesForLocale();return t?m.pluralRules[t](e):"other"}getPluralRulesForLocale(){const e=this.locale.split("-")[0];for(const t in m.pluralRulesMap){const n=m.pluralRulesMap[t];if(n.includes(this.locale)||n.includes(e))return t}}}m.pluralRulesMap={arabic:["ar"],chinese:["my","zh","id","ja","jv","ko","ms","th","vi"],french:["hy","bn","fr","gu","hi","fa","pa","zu"],german:["af","sq","az","eu","bg","ca","da","nl","en","et","fi","ka","de","el","hu","lb","no","so","sw","sv","ta","te","tr","ur"],irish:["ga"],russian:["ru","uk"],scottish:["gd"],spanish:["pt-PT","it","es"],welsh:["cy"]},m.pluralRules={arabic:e=>0===e?"zero":1===e?"one":2===e?"two":e%100>=3&&e%100<=10?"few":e%100>=11&&e%100<=99?"many":"other",chinese:()=>"other",french:e=>0===e||1===e?"one":"other",german:e=>1===e?"one":"other",irish:e=>1===e?"one":2===e?"two":e>=3&&e<=6?"few":e>=7&&e<=10?"many":"other",russian(e){const t=e%100,n=t%10;return 1===n&&11!==t?"one":n>=2&&n<=4&&!(t>=12&&t<=14)?"few":0===n||n>=5&&n<=9||t>=11&&t<=14?"many":"other"},scottish:e=>1===e||11===e?"one":2===e||12===e?"two":e>=3&&e<=10||e>=13&&e<=19?"few":"other",spanish:e=>1===e?"one":e%1e6==0&&0!==e?"many":"other",welsh:e=>0===e?"zero":1===e?"one":2===e?"two":3===e?"few":6===e?"many":"other"};
2
2
  /**
3
3
  * JavaScript enhancements for the Button component
4
4
  *
5
5
  * @preserve
6
+ * @augments ConfigurableComponent<ButtonConfig>
6
7
  */
7
- class d extends c{constructor(e,n={}){if(super(),this.$module=void 0,this.config=void 0,this.debounceFormSubmitTimer=null,!(e instanceof HTMLElement))throw new u({componentName:"Button",element:e,identifier:"Root element (`$module`)"});this.$module=e,this.config=t(d.defaults,n,i(d,e.dataset)),this.$module.addEventListener("keydown",(e=>this.handleKeyDown(e))),this.$module.addEventListener("click",(e=>this.debounce(e)))}handleKeyDown(e){const t=e.target;" "===e.key&&t instanceof HTMLElement&&"button"===t.getAttribute("role")&&(e.preventDefault(),t.click())}debounce(e){if(this.config.preventDoubleClick)return this.debounceFormSubmitTimer?(e.preventDefault(),!1):void(this.debounceFormSubmitTimer=window.setTimeout((()=>{this.debounceFormSubmitTimer=null}),1e3))}}function m(e,t){const n=e.closest(`[${t}]`);return n?n.getAttribute(t):null}
8
+ class f extends l{constructor(e,t={}){super(e,t),this.debounceFormSubmitTimer=null,this.$root.addEventListener("keydown",(e=>this.handleKeyDown(e))),this.$root.addEventListener("click",(e=>this.debounce(e)))}handleKeyDown(e){const t=e.target;" "===e.key&&t instanceof HTMLElement&&"button"===t.getAttribute("role")&&(e.preventDefault(),t.click())}debounce(e){if(this.config.preventDoubleClick)return this.debounceFormSubmitTimer?(e.preventDefault(),!1):void(this.debounceFormSubmitTimer=window.setTimeout((()=>{this.debounceFormSubmitTimer=null}),1e3))}}function g(e,t){const n=e.closest(`[${t}]`);return n?n.getAttribute(t):null}
8
9
  /**
9
10
  * Character count component
10
11
  *
@@ -16,13 +17,14 @@ class d extends c{constructor(e,n={}){if(super(),this.$module=void 0,this.config
16
17
  * of the available characters/words has been entered.
17
18
  *
18
19
  * @preserve
19
- */d.moduleName="govuk-button",d.defaults=Object.freeze({preventDoubleClick:!1}),d.schema=Object.freeze({properties:{preventDoubleClick:{type:"boolean"}}});class p extends c{constructor(e,n={}){var o,s;if(super(),this.$module=void 0,this.$textarea=void 0,this.$visibleCountMessage=void 0,this.$screenReaderCountMessage=void 0,this.lastInputTimestamp=null,this.lastInputValue="",this.valueChecker=null,this.config=void 0,this.i18n=void 0,this.maxLength=void 0,!(e instanceof HTMLElement))throw new u({componentName:"Character count",element:e,identifier:"Root element (`$module`)"});const r=e.querySelector(".govuk-js-character-count");if(!(r instanceof HTMLTextAreaElement||r instanceof HTMLInputElement))throw new u({componentName:"Character count",element:r,expectedType:"HTMLTextareaElement or HTMLInputElement",identifier:"Form field (`.govuk-js-character-count`)"});const a=i(p,e.dataset);let c={};("maxwords"in a||"maxlength"in a)&&(c={maxlength:void 0,maxwords:void 0}),this.config=t(p.defaults,n,c,a);const d=function(e,t){const n=[];for(const[o,s]of Object.entries(e)){const e=[];if(Array.isArray(s)){for(const{required:n,errorMessage:o}of s)n.every((e=>!!t[e]))||e.push(o);"anyOf"!==o||s.length-e.length>=1||n.push(...e)}}return n}(p.schema,this.config);if(d[0])throw new l(`Character count: ${d[0]}`);this.i18n=new h(this.config.i18n,{locale:m(e,"lang")}),this.maxLength=null!=(o=null!=(s=this.config.maxwords)?s:this.config.maxlength)?o:1/0,this.$module=e,this.$textarea=r;const f=`${this.$textarea.id}-info`,g=document.getElementById(f);if(!g)throw new u({componentName:"Character count",element:g,identifier:`Count message (\`id="${f}"\`)`});`${g.textContent}`.match(/^\s*$/)&&(g.textContent=this.i18n.t("textareaDescription",{count:this.maxLength})),this.$textarea.insertAdjacentElement("afterend",g);const v=document.createElement("div");v.className="govuk-character-count__sr-status govuk-visually-hidden",v.setAttribute("aria-live","polite"),this.$screenReaderCountMessage=v,g.insertAdjacentElement("afterend",v);const w=document.createElement("div");w.className=g.className,w.classList.add("govuk-character-count__status"),w.setAttribute("aria-hidden","true"),this.$visibleCountMessage=w,g.insertAdjacentElement("afterend",w),g.classList.add("govuk-visually-hidden"),this.$textarea.removeAttribute("maxlength"),this.bindChangeEvents(),window.addEventListener("pageshow",(()=>this.updateCountMessage())),this.updateCountMessage()}bindChangeEvents(){this.$textarea.addEventListener("keyup",(()=>this.handleKeyUp())),this.$textarea.addEventListener("focus",(()=>this.handleFocus())),this.$textarea.addEventListener("blur",(()=>this.handleBlur()))}handleKeyUp(){this.updateVisibleCountMessage(),this.lastInputTimestamp=Date.now()}handleFocus(){this.valueChecker=window.setInterval((()=>{(!this.lastInputTimestamp||Date.now()-500>=this.lastInputTimestamp)&&this.updateIfValueChanged()}),1e3)}handleBlur(){this.valueChecker&&window.clearInterval(this.valueChecker)}updateIfValueChanged(){this.$textarea.value!==this.lastInputValue&&(this.lastInputValue=this.$textarea.value,this.updateCountMessage())}updateCountMessage(){this.updateVisibleCountMessage(),this.updateScreenReaderCountMessage()}updateVisibleCountMessage(){const e=this.maxLength-this.count(this.$textarea.value)<0;this.$visibleCountMessage.classList.toggle("govuk-character-count__message--disabled",!this.isOverThreshold()),this.$textarea.classList.toggle("govuk-textarea--error",e),this.$visibleCountMessage.classList.toggle("govuk-error-message",e),this.$visibleCountMessage.classList.toggle("govuk-hint",!e),this.$visibleCountMessage.textContent=this.getCountMessage()}updateScreenReaderCountMessage(){this.isOverThreshold()?this.$screenReaderCountMessage.removeAttribute("aria-hidden"):this.$screenReaderCountMessage.setAttribute("aria-hidden","true"),this.$screenReaderCountMessage.textContent=this.getCountMessage()}count(e){if(this.config.maxwords){var t;return(null!=(t=e.match(/\S+/g))?t:[]).length}return e.length}getCountMessage(){const e=this.maxLength-this.count(this.$textarea.value),t=this.config.maxwords?"words":"characters";return this.formatCountMessage(e,t)}formatCountMessage(e,t){if(0===e)return this.i18n.t(`${t}AtLimit`);const n=e<0?"OverLimit":"UnderLimit";return this.i18n.t(`${t}${n}`,{count:Math.abs(e)})}isOverThreshold(){if(!this.config.threshold)return!0;const e=this.count(this.$textarea.value);return this.maxLength*this.config.threshold/100<=e}}p.moduleName="govuk-character-count",p.defaults=Object.freeze({threshold:0,i18n:{charactersUnderLimit:{one:"You have %{count} character remaining",other:"You have %{count} characters remaining"},charactersAtLimit:"You have 0 characters remaining",charactersOverLimit:{one:"You have %{count} character too many",other:"You have %{count} characters too many"},wordsUnderLimit:{one:"You have %{count} word remaining",other:"You have %{count} words remaining"},wordsAtLimit:"You have 0 words remaining",wordsOverLimit:{one:"You have %{count} word too many",other:"You have %{count} words too many"},textareaDescription:{other:""}}}),p.schema=Object.freeze({properties:{i18n:{type:"object"},maxwords:{type:"number"},maxlength:{type:"number"},threshold:{type:"number"}},anyOf:[{required:["maxwords"],errorMessage:'Either "maxlength" or "maxwords" must be provided'},{required:["maxlength"],errorMessage:'Either "maxlength" or "maxwords" must be provided'}]});
20
+ * @augments ConfigurableComponent<CharacterCountConfig>
21
+ */f.moduleName="govuk-button",f.defaults=Object.freeze({preventDoubleClick:!1}),f.schema=Object.freeze({properties:{preventDoubleClick:{type:"boolean"}}});class v extends l{[c](e){let t={};return("maxwords"in e||"maxlength"in e)&&(t={maxlength:void 0,maxwords:void 0}),t}constructor(e,t={}){var o,s;super(e,t),this.$textarea=void 0,this.$visibleCountMessage=void 0,this.$screenReaderCountMessage=void 0,this.lastInputTimestamp=null,this.lastInputValue="",this.valueChecker=null,this.i18n=void 0,this.maxLength=void 0;const a=this.$root.querySelector(".govuk-js-character-count");if(!(a instanceof HTMLTextAreaElement||a instanceof HTMLInputElement))throw new r({component:v,element:a,expectedType:"HTMLTextareaElement or HTMLInputElement",identifier:"Form field (`.govuk-js-character-count`)"});const u=function(e,t){const n=[];for(const[o,s]of Object.entries(e)){const e=[];if(Array.isArray(s)){for(const{required:n,errorMessage:o}of s)n.every((e=>!!t[e]))||e.push(o);"anyOf"!==o||s.length-e.length>=1||n.push(...e)}}return n}(v.schema,this.config);if(u[0])throw new i(n(v,u[0]));this.i18n=new m(this.config.i18n,{locale:g(this.$root,"lang")}),this.maxLength=null!=(o=null!=(s=this.config.maxwords)?s:this.config.maxlength)?o:1/0,this.$textarea=a;const c=`${this.$textarea.id}-info`,l=document.getElementById(c);if(!l)throw new r({component:v,element:l,identifier:`Count message (\`id="${c}"\`)`});`${l.textContent}`.match(/^\s*$/)&&(l.textContent=this.i18n.t("textareaDescription",{count:this.maxLength})),this.$textarea.insertAdjacentElement("afterend",l);const h=document.createElement("div");h.className="govuk-character-count__sr-status govuk-visually-hidden",h.setAttribute("aria-live","polite"),this.$screenReaderCountMessage=h,l.insertAdjacentElement("afterend",h);const d=document.createElement("div");d.className=l.className,d.classList.add("govuk-character-count__status"),d.setAttribute("aria-hidden","true"),this.$visibleCountMessage=d,l.insertAdjacentElement("afterend",d),l.classList.add("govuk-visually-hidden"),this.$textarea.removeAttribute("maxlength"),this.bindChangeEvents(),window.addEventListener("pageshow",(()=>this.updateCountMessage())),this.updateCountMessage()}bindChangeEvents(){this.$textarea.addEventListener("keyup",(()=>this.handleKeyUp())),this.$textarea.addEventListener("focus",(()=>this.handleFocus())),this.$textarea.addEventListener("blur",(()=>this.handleBlur()))}handleKeyUp(){this.updateVisibleCountMessage(),this.lastInputTimestamp=Date.now()}handleFocus(){this.valueChecker=window.setInterval((()=>{(!this.lastInputTimestamp||Date.now()-500>=this.lastInputTimestamp)&&this.updateIfValueChanged()}),1e3)}handleBlur(){this.valueChecker&&window.clearInterval(this.valueChecker)}updateIfValueChanged(){this.$textarea.value!==this.lastInputValue&&(this.lastInputValue=this.$textarea.value,this.updateCountMessage())}updateCountMessage(){this.updateVisibleCountMessage(),this.updateScreenReaderCountMessage()}updateVisibleCountMessage(){const e=this.maxLength-this.count(this.$textarea.value)<0;this.$visibleCountMessage.classList.toggle("govuk-character-count__message--disabled",!this.isOverThreshold()),this.$textarea.classList.toggle("govuk-textarea--error",e),this.$visibleCountMessage.classList.toggle("govuk-error-message",e),this.$visibleCountMessage.classList.toggle("govuk-hint",!e),this.$visibleCountMessage.textContent=this.getCountMessage()}updateScreenReaderCountMessage(){this.isOverThreshold()?this.$screenReaderCountMessage.removeAttribute("aria-hidden"):this.$screenReaderCountMessage.setAttribute("aria-hidden","true"),this.$screenReaderCountMessage.textContent=this.getCountMessage()}count(e){if(this.config.maxwords){var t;return(null!=(t=e.match(/\S+/g))?t:[]).length}return e.length}getCountMessage(){const e=this.maxLength-this.count(this.$textarea.value),t=this.config.maxwords?"words":"characters";return this.formatCountMessage(e,t)}formatCountMessage(e,t){if(0===e)return this.i18n.t(`${t}AtLimit`);const n=e<0?"OverLimit":"UnderLimit";return this.i18n.t(`${t}${n}`,{count:Math.abs(e)})}isOverThreshold(){if(!this.config.threshold)return!0;const e=this.count(this.$textarea.value);return this.maxLength*this.config.threshold/100<=e}}v.moduleName="govuk-character-count",v.defaults=Object.freeze({threshold:0,i18n:{charactersUnderLimit:{one:"You have %{count} character remaining",other:"You have %{count} characters remaining"},charactersAtLimit:"You have 0 characters remaining",charactersOverLimit:{one:"You have %{count} character too many",other:"You have %{count} characters too many"},wordsUnderLimit:{one:"You have %{count} word remaining",other:"You have %{count} words remaining"},wordsAtLimit:"You have 0 words remaining",wordsOverLimit:{one:"You have %{count} word too many",other:"You have %{count} words too many"},textareaDescription:{other:""}}}),v.schema=Object.freeze({properties:{i18n:{type:"object"},maxwords:{type:"number"},maxlength:{type:"number"},threshold:{type:"number"}},anyOf:[{required:["maxwords"],errorMessage:'Either "maxlength" or "maxwords" must be provided'},{required:["maxlength"],errorMessage:'Either "maxlength" or "maxwords" must be provided'}]});
20
22
  /**
21
23
  * Checkboxes component
22
24
  *
23
25
  * @preserve
24
26
  */
25
- class f extends c{constructor(e){if(super(),this.$module=void 0,this.$inputs=void 0,!(e instanceof HTMLElement))throw new u({componentName:"Checkboxes",element:e,identifier:"Root element (`$module`)"});const t=e.querySelectorAll('input[type="checkbox"]');if(!t.length)throw new u({componentName:"Checkboxes",identifier:'Form inputs (`<input type="checkbox">`)'});this.$module=e,this.$inputs=t,this.$inputs.forEach((e=>{const t=e.getAttribute("data-aria-controls");if(t){if(!document.getElementById(t))throw new u({componentName:"Checkboxes",identifier:`Conditional reveal (\`id="${t}"\`)`});e.setAttribute("aria-controls",t),e.removeAttribute("data-aria-controls")}})),window.addEventListener("pageshow",(()=>this.syncAllConditionalReveals())),this.syncAllConditionalReveals(),this.$module.addEventListener("click",(e=>this.handleClick(e)))}syncAllConditionalReveals(){this.$inputs.forEach((e=>this.syncConditionalRevealWithInputState(e)))}syncConditionalRevealWithInputState(e){const t=e.getAttribute("aria-controls");if(!t)return;const n=document.getElementById(t);if(null!=n&&n.classList.contains("govuk-checkboxes__conditional")){const t=e.checked;e.setAttribute("aria-expanded",t.toString()),n.classList.toggle("govuk-checkboxes__conditional--hidden",!t)}}unCheckAllInputsExcept(e){document.querySelectorAll(`input[type="checkbox"][name="${e.name}"]`).forEach((t=>{e.form===t.form&&t!==e&&(t.checked=!1,this.syncConditionalRevealWithInputState(t))}))}unCheckExclusiveInputs(e){document.querySelectorAll(`input[data-behaviour="exclusive"][type="checkbox"][name="${e.name}"]`).forEach((t=>{e.form===t.form&&(t.checked=!1,this.syncConditionalRevealWithInputState(t))}))}handleClick(e){const t=e.target;if(!(t instanceof HTMLInputElement)||"checkbox"!==t.type)return;if(t.getAttribute("aria-controls")&&this.syncConditionalRevealWithInputState(t),!t.checked)return;"exclusive"===t.getAttribute("data-behaviour")?this.unCheckAllInputsExcept(t):this.unCheckExclusiveInputs(t)}}f.moduleName="govuk-checkboxes";
27
+ class w extends u{constructor(e){super(e),this.$inputs=void 0;const t=this.$root.querySelectorAll('input[type="checkbox"]');if(!t.length)throw new r({component:w,identifier:'Form inputs (`<input type="checkbox">`)'});this.$inputs=t,this.$inputs.forEach((e=>{const t=e.getAttribute("data-aria-controls");if(t){if(!document.getElementById(t))throw new r({component:w,identifier:`Conditional reveal (\`id="${t}"\`)`});e.setAttribute("aria-controls",t),e.removeAttribute("data-aria-controls")}})),window.addEventListener("pageshow",(()=>this.syncAllConditionalReveals())),this.syncAllConditionalReveals(),this.$root.addEventListener("click",(e=>this.handleClick(e)))}syncAllConditionalReveals(){this.$inputs.forEach((e=>this.syncConditionalRevealWithInputState(e)))}syncConditionalRevealWithInputState(e){const t=e.getAttribute("aria-controls");if(!t)return;const n=document.getElementById(t);if(null!=n&&n.classList.contains("govuk-checkboxes__conditional")){const t=e.checked;e.setAttribute("aria-expanded",t.toString()),n.classList.toggle("govuk-checkboxes__conditional--hidden",!t)}}unCheckAllInputsExcept(e){document.querySelectorAll(`input[type="checkbox"][name="${e.name}"]`).forEach((t=>{e.form===t.form&&t!==e&&(t.checked=!1,this.syncConditionalRevealWithInputState(t))}))}unCheckExclusiveInputs(e){document.querySelectorAll(`input[data-behaviour="exclusive"][type="checkbox"][name="${e.name}"]`).forEach((t=>{e.form===t.form&&(t.checked=!1,this.syncConditionalRevealWithInputState(t))}))}handleClick(e){const t=e.target;if(!(t instanceof HTMLInputElement)||"checkbox"!==t.type)return;if(t.getAttribute("aria-controls")&&this.syncConditionalRevealWithInputState(t),!t.checked)return;"exclusive"===t.getAttribute("data-behaviour")?this.unCheckAllInputsExcept(t):this.unCheckExclusiveInputs(t)}}w.moduleName="govuk-checkboxes";
26
28
  /**
27
29
  * Error summary component
28
30
  *
@@ -30,17 +32,19 @@ class f extends c{constructor(e){if(super(),this.$module=void 0,this.$inputs=voi
30
32
  * configuration.
31
33
  *
32
34
  * @preserve
35
+ * @augments ConfigurableComponent<ErrorSummaryConfig>
33
36
  */
34
- class g extends c{constructor(e,n={}){if(super(),this.$module=void 0,this.config=void 0,!(e instanceof HTMLElement))throw new u({componentName:"Error summary",element:e,identifier:"Root element (`$module`)"});this.$module=e,this.config=t(g.defaults,n,i(g,e.dataset)),this.config.disableAutoFocus||function(e,t={}){var n;const o=e.getAttribute("tabindex");function s(){var n;null==(n=t.onBlur)||n.call(e),o||e.removeAttribute("tabindex")}o||e.setAttribute("tabindex","-1"),e.addEventListener("focus",(function(){e.addEventListener("blur",s,{once:!0})}),{once:!0}),null==(n=t.onBeforeFocus)||n.call(e),e.focus()}(this.$module),this.$module.addEventListener("click",(e=>this.handleClick(e)))}handleClick(e){const t=e.target;t&&this.focusTarget(t)&&e.preventDefault()}focusTarget(e){if(!(e instanceof HTMLAnchorElement))return!1;const t=function(e){if(e.includes("#"))return e.split("#").pop()}(e.href);if(!t)return!1;const n=document.getElementById(t);if(!n)return!1;const o=this.getAssociatedLegendOrLabel(n);return!!o&&(o.scrollIntoView(),n.focus({preventScroll:!0}),!0)}getAssociatedLegendOrLabel(e){var t;const n=e.closest("fieldset");if(n){const t=n.getElementsByTagName("legend");if(t.length){const n=t[0];if(e instanceof HTMLInputElement&&("checkbox"===e.type||"radio"===e.type))return n;const o=n.getBoundingClientRect().top,s=e.getBoundingClientRect();if(s.height&&window.innerHeight){if(s.top+s.height-o<window.innerHeight/2)return n}}}return null!=(t=document.querySelector(`label[for='${e.getAttribute("id")}']`))?t:e.closest("label")}}g.moduleName="govuk-error-summary",g.defaults=Object.freeze({disableAutoFocus:!1}),g.schema=Object.freeze({properties:{disableAutoFocus:{type:"boolean"}}});
37
+ class b extends l{constructor(e,t={}){super(e,t),this.config.disableAutoFocus||function(e,t={}){var n;const o=e.getAttribute("tabindex");function s(){var n;null==(n=t.onBlur)||n.call(e),o||e.removeAttribute("tabindex")}o||e.setAttribute("tabindex","-1"),e.addEventListener("focus",(function(){e.addEventListener("blur",s,{once:!0})}),{once:!0}),null==(n=t.onBeforeFocus)||n.call(e),e.focus()}(this.$root),this.$root.addEventListener("click",(e=>this.handleClick(e)))}handleClick(e){const t=e.target;t&&this.focusTarget(t)&&e.preventDefault()}focusTarget(e){if(!(e instanceof HTMLAnchorElement))return!1;const t=function(e){if(e.includes("#"))return e.split("#").pop()}(e.href);if(!t)return!1;const n=document.getElementById(t);if(!n)return!1;const o=this.getAssociatedLegendOrLabel(n);return!!o&&(o.scrollIntoView(),n.focus({preventScroll:!0}),!0)}getAssociatedLegendOrLabel(e){var t;const n=e.closest("fieldset");if(n){const t=n.getElementsByTagName("legend");if(t.length){const n=t[0];if(e instanceof HTMLInputElement&&("checkbox"===e.type||"radio"===e.type))return n;const o=n.getBoundingClientRect().top,s=e.getBoundingClientRect();if(s.height&&window.innerHeight){if(s.top+s.height-o<window.innerHeight/2)return n}}}return null!=(t=document.querySelector(`label[for='${e.getAttribute("id")}']`))?t:e.closest("label")}}b.moduleName="govuk-error-summary",b.defaults=Object.freeze({disableAutoFocus:!1}),b.schema=Object.freeze({properties:{disableAutoFocus:{type:"boolean"}}});
35
38
  /**
36
39
  * Password input component
37
40
  *
38
41
  * @preserve
42
+ * @augments ConfigurableComponent<PasswordInputConfig>
39
43
  */
40
- class v extends c{constructor(e,n={}){if(super(),this.$module=void 0,this.config=void 0,this.i18n=void 0,this.$input=void 0,this.$showHideButton=void 0,this.$screenReaderStatusMessage=void 0,!(e instanceof HTMLElement))throw new u({componentName:"Password input",element:e,identifier:"Root element (`$module`)"});const o=e.querySelector(".govuk-js-password-input-input");if(!(o instanceof HTMLInputElement))throw new u({componentName:"Password input",element:o,expectedType:"HTMLInputElement",identifier:"Form field (`.govuk-js-password-input-input`)"});if("password"!==o.type)throw new u("Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.");const s=e.querySelector(".govuk-js-password-input-toggle");if(!(s instanceof HTMLButtonElement))throw new u({componentName:"Password input",element:s,expectedType:"HTMLButtonElement",identifier:"Button (`.govuk-js-password-input-toggle`)"});if("button"!==s.type)throw new u("Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.");this.$module=e,this.$input=o,this.$showHideButton=s,this.config=t(v.defaults,n,i(v,e.dataset)),this.i18n=new h(this.config.i18n,{locale:m(e,"lang")}),this.$showHideButton.removeAttribute("hidden");const r=document.createElement("div");r.className="govuk-password-input__sr-status govuk-visually-hidden",r.setAttribute("aria-live","polite"),this.$screenReaderStatusMessage=r,this.$input.insertAdjacentElement("afterend",r),this.$showHideButton.addEventListener("click",this.toggle.bind(this)),this.$input.form&&this.$input.form.addEventListener("submit",(()=>this.hide())),window.addEventListener("pageshow",(e=>{e.persisted&&"password"!==this.$input.type&&this.hide()})),this.hide()}toggle(e){e.preventDefault(),"password"!==this.$input.type?this.hide():this.show()}show(){this.setType("text")}hide(){this.setType("password")}setType(e){if(e===this.$input.type)return;this.$input.setAttribute("type",e);const t="password"===e,n=t?"show":"hide",o=t?"passwordHidden":"passwordShown";this.$showHideButton.innerText=this.i18n.t(`${n}Password`),this.$showHideButton.setAttribute("aria-label",this.i18n.t(`${n}PasswordAriaLabel`)),this.$screenReaderStatusMessage.innerText=this.i18n.t(`${o}Announcement`)}}v.moduleName="govuk-password-input",v.defaults=Object.freeze({i18n:{showPassword:"Show",hidePassword:"Hide",showPasswordAriaLabel:"Show password",hidePasswordAriaLabel:"Hide password",passwordShownAnnouncement:"Your password is visible",passwordHiddenAnnouncement:"Your password is hidden"}}),v.schema=Object.freeze({properties:{i18n:{type:"object"}}});
44
+ class y extends l{constructor(e,t={}){super(e,t),this.i18n=void 0,this.$input=void 0,this.$showHideButton=void 0,this.$screenReaderStatusMessage=void 0;const n=this.$root.querySelector(".govuk-js-password-input-input");if(!(n instanceof HTMLInputElement))throw new r({component:y,element:n,expectedType:"HTMLInputElement",identifier:"Form field (`.govuk-js-password-input-input`)"});if("password"!==n.type)throw new r("Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.");const o=this.$root.querySelector(".govuk-js-password-input-toggle");if(!(o instanceof HTMLButtonElement))throw new r({component:y,element:o,expectedType:"HTMLButtonElement",identifier:"Button (`.govuk-js-password-input-toggle`)"});if("button"!==o.type)throw new r("Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.");this.$input=n,this.$showHideButton=o,this.i18n=new m(this.config.i18n,{locale:g(this.$root,"lang")}),this.$showHideButton.removeAttribute("hidden");const s=document.createElement("div");s.className="govuk-password-input__sr-status govuk-visually-hidden",s.setAttribute("aria-live","polite"),this.$screenReaderStatusMessage=s,this.$input.insertAdjacentElement("afterend",s),this.$showHideButton.addEventListener("click",this.toggle.bind(this)),this.$input.form&&this.$input.form.addEventListener("submit",(()=>this.hide())),window.addEventListener("pageshow",(e=>{e.persisted&&"password"!==this.$input.type&&this.hide()})),this.hide()}toggle(e){e.preventDefault(),"password"!==this.$input.type?this.hide():this.show()}show(){this.setType("text")}hide(){this.setType("password")}setType(e){if(e===this.$input.type)return;this.$input.setAttribute("type",e);const t="password"===e,n=t?"show":"hide",o=t?"passwordHidden":"passwordShown";this.$showHideButton.innerText=this.i18n.t(`${n}Password`),this.$showHideButton.setAttribute("aria-label",this.i18n.t(`${n}PasswordAriaLabel`)),this.$screenReaderStatusMessage.innerText=this.i18n.t(`${o}Announcement`)}}y.moduleName="govuk-password-input",y.defaults=Object.freeze({i18n:{showPassword:"Show",hidePassword:"Hide",showPasswordAriaLabel:"Show password",hidePasswordAriaLabel:"Hide password",passwordShownAnnouncement:"Your password is visible",passwordHiddenAnnouncement:"Your password is hidden"}}),y.schema=Object.freeze({properties:{i18n:{type:"object"}}});
41
45
  /**
42
46
  * Radios component
43
47
  *
44
48
  * @preserve
45
49
  */
46
- class w extends c{constructor(e){if(super(),this.$module=void 0,this.$inputs=void 0,!(e instanceof HTMLElement))throw new u({componentName:"Radios",element:e,identifier:"Root element (`$module`)"});const t=e.querySelectorAll('input[type="radio"]');if(!t.length)throw new u({componentName:"Radios",identifier:'Form inputs (`<input type="radio">`)'});this.$module=e,this.$inputs=t,this.$inputs.forEach((e=>{const t=e.getAttribute("data-aria-controls");if(t){if(!document.getElementById(t))throw new u({componentName:"Radios",identifier:`Conditional reveal (\`id="${t}"\`)`});e.setAttribute("aria-controls",t),e.removeAttribute("data-aria-controls")}})),window.addEventListener("pageshow",(()=>this.syncAllConditionalReveals())),this.syncAllConditionalReveals(),this.$module.addEventListener("click",(e=>this.handleClick(e)))}syncAllConditionalReveals(){this.$inputs.forEach((e=>this.syncConditionalRevealWithInputState(e)))}syncConditionalRevealWithInputState(e){const t=e.getAttribute("aria-controls");if(!t)return;const n=document.getElementById(t);if(null!=n&&n.classList.contains("govuk-radios__conditional")){const t=e.checked;e.setAttribute("aria-expanded",t.toString()),n.classList.toggle("govuk-radios__conditional--hidden",!t)}}handleClick(e){const t=e.target;if(!(t instanceof HTMLInputElement)||"radio"!==t.type)return;const n=document.querySelectorAll('input[type="radio"][aria-controls]'),o=t.form,s=t.name;n.forEach((e=>{const t=e.form===o;e.name===s&&t&&this.syncConditionalRevealWithInputState(e)}))}}function b(e){let t;if(e=void 0!==e?e:{},!o())return void console.log(new a);const n=[[d,e.button],[p,e.characterCount],[f],[g,e.errorSummary],[w],[v,e.passwordInput]],s=null!=(t=e.scope)?t:document;n.forEach((([e,t])=>{s.querySelectorAll(`[data-module="${e.moduleName}"]`).forEach((n=>{try{"defaults"in e?new e(n,t):new e(n)}catch(e){console.log(e)}}))}))}w.moduleName="govuk-radios";export{d as Button,p as CharacterCount,f as Checkboxes,g as ErrorSummary,v as PasswordInput,w as Radios,b as initAll};
50
+ class $ extends u{constructor(e){super(e),this.$inputs=void 0;const t=this.$root.querySelectorAll('input[type="radio"]');if(!t.length)throw new r({component:$,identifier:'Form inputs (`<input type="radio">`)'});this.$inputs=t,this.$inputs.forEach((e=>{const t=e.getAttribute("data-aria-controls");if(t){if(!document.getElementById(t))throw new r({component:$,identifier:`Conditional reveal (\`id="${t}"\`)`});e.setAttribute("aria-controls",t),e.removeAttribute("data-aria-controls")}})),window.addEventListener("pageshow",(()=>this.syncAllConditionalReveals())),this.syncAllConditionalReveals(),this.$root.addEventListener("click",(e=>this.handleClick(e)))}syncAllConditionalReveals(){this.$inputs.forEach((e=>this.syncConditionalRevealWithInputState(e)))}syncConditionalRevealWithInputState(e){const t=e.getAttribute("aria-controls");if(!t)return;const n=document.getElementById(t);if(null!=n&&n.classList.contains("govuk-radios__conditional")){const t=e.checked;e.setAttribute("aria-expanded",t.toString()),n.classList.toggle("govuk-radios__conditional--hidden",!t)}}handleClick(e){const t=e.target;if(!(t instanceof HTMLInputElement)||"radio"!==t.type)return;const n=document.querySelectorAll('input[type="radio"][aria-controls]'),o=t.form,s=t.name;n.forEach((e=>{const t=e.form===o;e.name===s&&t&&this.syncConditionalRevealWithInputState(e)}))}}function x(t){let n;if(t=void 0!==t?t:{},!e())return void console.log(new s);const o=[[f,t.button],[v,t.characterCount],[w],[b,t.errorSummary],[$],[y,t.passwordInput]],i=null!=(n=t.scope)?n:document;o.forEach((([e,t])=>{i.querySelectorAll(`[data-module="${e.moduleName}"]`).forEach((n=>{try{"defaults"in e?new e(n,t):new e(n)}catch(e){console.log(e)}}))}))}$.moduleName="govuk-radios";export{f as Button,v as CharacterCount,w as Checkboxes,b as ErrorSummary,y as PasswordInput,$ as Radios,x as initAll};
@@ -1,3 +1,9 @@
1
1
  @import "index";
2
2
 
3
+ @include _warning(
4
+ "import-using-all",
5
+ "Importing using 'govuk/all' is deprecated. Update your import statement to import 'govuk/index'.",
6
+ $silence-further-warnings: false
7
+ );
8
+
3
9
  /*# sourceMappingURL=all.scss.map */
@@ -8,9 +8,10 @@
8
8
  }
9
9
 
10
10
  .govuk-details__summary {
11
- // Make the focus outline shrink-wrap the text content of the summary
12
- display: inline-block;
11
+ display: block;
12
+ }
13
13
 
14
+ .govuk-details[open] .govuk-details__summary {
14
15
  margin-bottom: govuk-spacing(1);
15
16
  }
16
17
 
@@ -72,6 +73,10 @@
72
73
  // Absolutely position the marker against this element
73
74
  position: relative;
74
75
 
76
+ // Make the focus outline shrink-wrap the text content of the summary
77
+ width: -webkit-fit-content;
78
+ width: fit-content;
79
+
75
80
  // Allow for absolutely positioned marker and align with disclosed text
76
81
  padding-left: govuk-spacing(4) + $govuk-border-width;
77
82
 
@@ -3,12 +3,9 @@
3
3
  $govuk-footer-border: $govuk-border-colour;
4
4
  $govuk-footer-text: $govuk-text-colour;
5
5
 
6
- // Based on the govuk-crest-2x.png image dimensions.
7
- $govuk-footer-crest-image-width-2x: 250px;
8
- $govuk-footer-crest-image-height-2x: 204px;
9
- // Half the 2x image so that it fits the regular 1x size.
10
- $govuk-footer-crest-image-width: ($govuk-footer-crest-image-width-2x / 2);
11
- $govuk-footer-crest-image-height: ($govuk-footer-crest-image-height-2x / 2);
6
+ // Royal Arms image dimensions
7
+ $govuk-footer-crest-image-width: 125px;
8
+ $govuk-footer-crest-image-height: 102px;
12
9
 
13
10
  .govuk-footer {
14
11
  @include govuk-font($size: if($govuk-new-typography-scale, 19, 16));
@@ -67,17 +64,18 @@
67
64
  }
68
65
 
69
66
  .govuk-footer__licence-description {
67
+ // This makes the license description reflow under the logo when space gets too narrow
70
68
  display: inline-block;
69
+ // This prevents the description from having orphans when space is narrow enough
70
+ // and makes the text reflow more nicely
71
+ text-wrap: balance;
71
72
  }
72
73
 
73
74
  .govuk-footer__copyright-logo {
74
75
  display: inline-block;
75
76
  min-width: $govuk-footer-crest-image-width;
76
77
  padding-top: ($govuk-footer-crest-image-height + govuk-spacing(2));
77
- background-image: govuk-image-url("govuk-crest.png");
78
- @include govuk-device-pixel-ratio {
79
- background-image: govuk-image-url("govuk-crest-2x.png");
80
- }
78
+ background-image: govuk-image-url("govuk-crest.svg");
81
79
  background-repeat: no-repeat;
82
80
  background-position: 50% 0%;
83
81
  background-size: $govuk-footer-crest-image-width $govuk-footer-crest-image-height;
@@ -90,7 +90,7 @@
90
90
  .govuk-service-navigation__toggle {
91
91
  @include govuk-font($size: 19, $weight: bold);
92
92
  display: inline-flex;
93
- margin: 0 0 govuk-spacing(2);
93
+ margin: govuk-spacing(2) 0;
94
94
  padding: 0;
95
95
  border: 0;
96
96
  color: $govuk-service-navigation-link-colour;
@@ -117,6 +117,12 @@
117
117
  &[hidden] {
118
118
  display: none;
119
119
  }
120
+
121
+ // If we have both a service name and navigation toggle, remove the
122
+ // margin-top so that there isn't a bunch of space between them
123
+ .govuk-service-navigation__service-name + .govuk-service-navigation__wrapper & {
124
+ margin-top: 0;
125
+ }
120
126
  }
121
127
 
122
128
  .govuk-service-navigation__list {
@@ -2,14 +2,12 @@
2
2
  .govuk-warning-text {
3
3
  @include govuk-font($size: 19);
4
4
  @include govuk-responsive-margin(6, "bottom");
5
+ @include govuk-typography-weight-bold;
5
6
  position: relative;
6
7
  padding: govuk-spacing(2) 0;
7
8
  }
8
9
 
9
10
  .govuk-warning-text__icon {
10
- // We apply this here and not at the parent level because the actual text is
11
- // a <strong> and so will always be bold
12
- @include govuk-typography-weight-bold;
13
11
  box-sizing: border-box;
14
12
 
15
13
  display: inline-block;
@@ -59,6 +57,9 @@
59
57
  @include govuk-text-colour;
60
58
  display: block;
61
59
  padding-left: 45px;
60
+ // While `<strong>` is styled `bold` or `bolder` by user-agents
61
+ // this can be reset by the app's stylesheet
62
+ font-weight: inherit;
62
63
  }
63
64
  }
64
65
 
@@ -1,7 +1,7 @@
1
1
  :root {
2
2
  // This variable is automatically overwritten during builds and releases.
3
3
  // It doesn't need to be updated manually.
4
- --govuk-frontend-version: "5.6.0";
4
+ --govuk-frontend-version: "5.8.0";
5
5
 
6
6
  // CSS custom property for each breakpoint
7
7
  @each $name, $value in $govuk-breakpoints {
@@ -60,7 +60,8 @@ $_govuk-organisation-colours: (
60
60
  "`department-for-communities-local-government` became `ministry-of-housing-communities-local-government` in 2018."
61
61
  ),
62
62
  "department-for-culture-media-sport": (
63
- colour: #d6006e
63
+ colour: #ed1588,
64
+ contrast-safe: #d6177a
64
65
  ),
65
66
  "department-for-digital-culture-media-sport": (
66
67
  colour: #d40072,
@@ -75,8 +76,8 @@ $_govuk-organisation-colours: (
75
76
  contrast-safe: #00852f
76
77
  ),
77
78
  "department-for-environment-food-rural-affairs": (
78
- colour: #00af43,
79
- contrast-safe: #008733
79
+ colour: #00a33b,
80
+ contrast-safe: #008531
80
81
  ),
81
82
  "department-for-exiting-the-european-union": (
82
83
  colour: #009fe3,
@@ -98,7 +99,7 @@ $_govuk-organisation-colours: (
98
99
  "`department-for-levelling-up-housing-communities` was renamed to `ministry-of-housing-communities-local-government` in 2024."
99
100
  ),
100
101
  "department-for-science-innovation-technology": (
101
- colour: #00f9f8,
102
+ colour: #00f8f8,
102
103
  contrast-safe: #008180
103
104
  ),
104
105
  "department-for-transport": (
@@ -128,7 +129,7 @@ $_govuk-organisation-colours: (
128
129
  deprecation-message: "`foreign-commonwealth-office` became `foreign-commonwealth-development-office` in 2018."
129
130
  ),
130
131
  "foreign-commonwealth-development-office": (
131
- colour: #002269
132
+ colour: #012069
132
133
  ),
133
134
  "government-equalities-office": (
134
135
  colour: #0056b8,
@@ -168,17 +169,21 @@ $_govuk-organisation-colours: (
168
169
  colour: #9c182f
169
170
  ),
170
171
  "office-of-the-secretary-of-state-for-scotland": (
171
- colour: #00205c
172
+ colour: #00205c,
173
+ deprecation-message: "`office-of-the-secretary-of-state-for-scotland` was renamed to `scotland-office` in 2024."
172
174
  ),
173
175
  "office-of-the-secretary-of-state-for-wales": (
174
- colour: #a8353a
176
+ colour: #a8353a,
177
+ deprecation-message: "`office-of-the-secretary-of-state-for-wales` was renamed to `wales-office` in 2024."
175
178
  ),
176
179
  "prime-ministers-office-10-downing-street": (
177
180
  colour: #0b0c0c
178
181
  ),
179
182
  "scotland-office": (
180
- colour: #002663,
181
- deprecation-message: "`scotland-office` became `office-of-the-secretary-of-state-for-scotland` in 2018."
183
+ colour: #00205c
184
+ ),
185
+ "serious-fraud-office": (
186
+ colour: #82368c
182
187
  ),
183
188
  "uk-export-finance": (
184
189
  colour: #cf102d
@@ -189,8 +194,7 @@ $_govuk-organisation-colours: (
189
194
  "`uk-trade-investment` became `department-for-international-trade` in 2016. As of 2023, it is equivalent to `department-for-business-trade`."
190
195
  ),
191
196
  "wales-office": (
192
- colour: #a33038,
193
- deprecation-message: "`wales-office` became `office-of-the-secretary-of-state-for-wales` in 2018."
197
+ colour: #a33038
194
198
  )
195
199
  );
196
200
 
@@ -216,8 +216,7 @@ $_govuk-typography-scale-modern: (
216
216
  ),
217
217
  27: (
218
218
  // Made same as 24 on mobile (consider deprecating this size)
219
- null:
220
- (
219
+ null: (
221
220
  font-size: 21px,
222
221
  line-height: 25px
223
222
  ),
@@ -232,8 +231,7 @@ $_govuk-typography-scale-modern: (
232
231
  ),
233
232
  24: (
234
233
  // Bump up mobile size from 18/20 to 21/25
235
- null:
236
- (
234
+ null: (
237
235
  font-size: 21px,
238
236
  line-height: 25px
239
237
  ),
@@ -248,8 +246,7 @@ $_govuk-typography-scale-modern: (
248
246
  ),
249
247
  19: (
250
248
  // Stay at 19/25 at all sizes
251
- null:
252
- (
249
+ null: (
253
250
  font-size: 19px,
254
251
  line-height: 25px
255
252
  ),
@@ -260,8 +257,7 @@ $_govuk-typography-scale-modern: (
260
257
  ),
261
258
  16: (
262
259
  // Stay at 16/20 at all sizes
263
- null:
264
- (
260
+ null: (
265
261
  font-size: 16px,
266
262
  line-height: 20px
267
263
  ),
@@ -272,8 +268,7 @@ $_govuk-typography-scale-modern: (
272
268
  ),
273
269
  14: (
274
270
  // Stay at 14/20 at all sizes (consider deprecating this size)
275
- null:
276
- (
271
+ null: (
277
272
  font-size: 14px,
278
273
  line-height: 20px
279
274
  ),
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-govuk-formbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.1
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-10 00:00:00.000000000 Z
10
+ date: 2025-01-31 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: govuk_design_system_formbuilder
@@ -197,7 +196,6 @@ licenses:
197
196
  - MIT
198
197
  metadata:
199
198
  rubygems_mfa_required: 'true'
200
- post_install_message:
201
199
  rdoc_options: []
202
200
  require_paths:
203
201
  - lib
@@ -212,8 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
210
  - !ruby/object:Gem::Version
213
211
  version: '0'
214
212
  requirements: []
215
- rubygems_version: 3.5.16
216
- signing_key:
213
+ rubygems_version: 3.6.2
217
214
  specification_version: 4
218
215
  summary: Repackaging of UK.GOV forms for Rails 7 asset pipeline
219
216
  test_files: []