govuk_tech_docs 3.2.0 → 3.3.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish.yaml +1 -1
  3. data/CHANGELOG.md +26 -7
  4. data/README.md +2 -2
  5. data/example/source/code.html.md +3 -26
  6. data/lib/govuk_tech_docs/contribution_banner.rb +1 -1
  7. data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +3 -3
  8. data/lib/govuk_tech_docs/version.rb +1 -1
  9. data/lib/source/layouts/core.erb +1 -1
  10. data/node_modules/govuk-frontend/govuk/all.js +1548 -311
  11. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +70 -0
  12. data/node_modules/govuk-frontend/govuk/common/index.js +172 -0
  13. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +373 -0
  14. data/node_modules/govuk-frontend/govuk/common.js +138 -3
  15. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -0
  16. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +5 -6
  17. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +754 -36
  18. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +0 -2
  19. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +29 -21
  20. data/node_modules/govuk-frontend/govuk/components/button/button.js +365 -107
  21. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
  22. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +1092 -109
  23. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +3 -2
  24. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +30 -2
  25. data/node_modules/govuk-frontend/govuk/components/details/details.js +51 -33
  26. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +289 -6
  27. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +13 -23
  28. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +30 -24
  29. data/node_modules/govuk-frontend/govuk/components/header/header.js +59 -11
  30. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +13 -23
  31. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +252 -2
  32. data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +247 -0
  33. data/node_modules/govuk-frontend/govuk/components/pagination/_pagination.scss +2 -0
  34. data/node_modules/govuk-frontend/govuk/components/panel/_index.scss +1 -1
  35. data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +5 -12
  36. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +30 -2
  37. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +11 -0
  38. data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +1 -3
  39. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +10 -4
  40. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +45 -13
  41. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
  42. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +28 -0
  43. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
  44. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +5 -5
  45. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +5 -0
  46. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +13 -11
  47. data/node_modules/govuk-frontend/govuk/helpers/_media-queries.scss +2 -2
  48. data/node_modules/govuk-frontend/govuk/helpers/_shape-arrow.scss +1 -1
  49. data/node_modules/govuk-frontend/govuk/helpers/_spacing.scss +3 -3
  50. data/node_modules/govuk-frontend/govuk/helpers/_typography.scss +16 -9
  51. data/node_modules/govuk-frontend/govuk/i18n.js +390 -0
  52. data/node_modules/govuk-frontend/govuk/objects/_button-group.scss +10 -26
  53. data/node_modules/govuk-frontend/govuk/objects/_template.scss +1 -1
  54. data/node_modules/govuk-frontend/govuk/objects/_width-container.scss +0 -4
  55. data/node_modules/govuk-frontend/govuk/overrides/_spacing.scss +56 -12
  56. data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
  57. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
  58. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
  59. data/node_modules/govuk-frontend/govuk/settings/_spacing.scss +4 -8
  60. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
  61. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
  62. data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
  63. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
  64. data/node_modules/govuk-frontend/govuk/tools/_exports.scss +1 -1
  65. data/node_modules/govuk-frontend/govuk/tools/_font-url.scss +1 -1
  66. data/node_modules/govuk-frontend/govuk/tools/_image-url.scss +1 -1
  67. data/node_modules/govuk-frontend/govuk/tools/_px-to-em.scss +2 -2
  68. data/node_modules/govuk-frontend/govuk/tools/_px-to-rem.scss +1 -1
  69. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +21 -0
  70. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +300 -0
  71. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +21 -0
  72. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
  73. data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
  74. data/package-lock.json +12 -12
  75. data/package.json +1 -1
  76. metadata +17 -5
@@ -30,12 +30,12 @@
30
30
  @mixin govuk-link-decoration {
31
31
  text-decoration: underline;
32
32
 
33
- @if ($govuk-new-link-styles) {
34
- @if ($govuk-link-underline-thickness) {
33
+ @if $govuk-new-link-styles {
34
+ @if $govuk-link-underline-thickness {
35
35
  text-decoration-thickness: $govuk-link-underline-thickness;
36
36
  }
37
37
 
38
- @if ($govuk-link-underline-offset) {
38
+ @if $govuk-link-underline-offset {
39
39
  text-underline-offset: $govuk-link-underline-offset;
40
40
  }
41
41
  }
@@ -50,7 +50,7 @@
50
50
  /// @access public
51
51
 
52
52
  @mixin govuk-link-hover-decoration {
53
- @if ($govuk-new-link-styles and $govuk-link-hover-underline-thickness) {
53
+ @if $govuk-new-link-styles and $govuk-link-hover-underline-thickness {
54
54
  text-decoration-thickness: $govuk-link-hover-underline-thickness;
55
55
  // Disable ink skipping on underlines on hover. Browsers haven't
56
56
  // standardised on this part of the spec yet, so set both properties
@@ -104,7 +104,7 @@
104
104
  // we need to override the text colour for that combination of selectors so
105
105
  // so that unvisited links styled as buttons do not end up with dark blue
106
106
  // text when focussed.
107
- @include govuk-compatibility(govuk_template) {
107
+ @include _govuk-compatibility(govuk_template) {
108
108
  &:link:focus {
109
109
  color: $govuk-focus-text-colour;
110
110
  }
@@ -152,7 +152,7 @@
152
152
  // we need to override the text colour for that combination of selectors so
153
153
  // so that unvisited links styled as buttons do not end up with dark blue
154
154
  // text when focussed.
155
- @include govuk-compatibility(govuk_template) {
155
+ @include _govuk-compatibility(govuk_template) {
156
156
  &:link:focus {
157
157
  color: $govuk-focus-text-colour;
158
158
  }
@@ -200,7 +200,7 @@
200
200
  // we need to override the text colour for that combination of selectors so
201
201
  // so that unvisited links styled as buttons do not end up with dark blue
202
202
  // text when focussed.
203
- @include govuk-compatibility(govuk_template) {
203
+ @include _govuk-compatibility(govuk_template) {
204
204
  &:link:focus {
205
205
  color: $govuk-focus-text-colour;
206
206
  }
@@ -243,7 +243,7 @@
243
243
  // alphagov/govuk_template includes a specific a:link:focus selector designed
244
244
  // to make unvisited links a slightly darker blue when focussed, so we need to
245
245
  // override the text colour for that combination of selectors.
246
- @include govuk-compatibility(govuk_template) {
246
+ @include _govuk-compatibility(govuk_template) {
247
247
  &:link:focus {
248
248
  @include govuk-text-colour;
249
249
  }
@@ -275,7 +275,9 @@
275
275
  // Force a colour change on hover to work around a bug in Safari
276
276
  // https://bugs.webkit.org/show_bug.cgi?id=224483
277
277
  &:hover {
278
- color: rgba($govuk-text-colour, .99);
278
+ @if type-of($govuk-text-colour) == color {
279
+ color: rgba($govuk-text-colour, .99);
280
+ }
279
281
  }
280
282
 
281
283
  &:active,
@@ -286,7 +288,7 @@
286
288
  // alphagov/govuk_template includes a specific a:link:focus selector designed
287
289
  // to make unvisited links a slightly darker blue when focussed, so we need to
288
290
  // override the text colour for that combination of selectors.
289
- @include govuk-compatibility(govuk_template) {
291
+ @include _govuk-compatibility(govuk_template) {
290
292
  &:link:focus {
291
293
  @include govuk-text-colour;
292
294
  }
@@ -329,7 +331,7 @@
329
331
  // alphagov/govuk_template includes a specific a:link:focus selector designed
330
332
  // to make unvisited links a slightly darker blue when focussed, so we need to
331
333
  // override the text colour for that combination of selectors.
332
- @include govuk-compatibility(govuk_template) {
334
+ @include _govuk-compatibility(govuk_template) {
333
335
  &:link:focus {
334
336
  color: $govuk-focus-text-colour;
335
337
  }
@@ -12,7 +12,7 @@ $mq-static-breakpoint: if(variable-exists(govuk-ie8-breakpoint), $govuk-ie8-brea
12
12
 
13
13
  $mq-show-breakpoints: ();
14
14
 
15
- @if (variable-exists(govuk-show-breakpoints) and $govuk-show-breakpoints) {
15
+ @if variable-exists(govuk-show-breakpoints) and $govuk-show-breakpoints {
16
16
  $mq-show-breakpoints: map-keys($govuk-breakpoints);
17
17
  }
18
18
 
@@ -20,7 +20,7 @@ $mq-show-breakpoints: ();
20
20
  // 'rasterize' any media queries.
21
21
 
22
22
  $mq-responsive: true;
23
- @if (variable-exists(govuk-is-ie8) and $govuk-is-ie8) {
23
+ @if variable-exists(govuk-is-ie8) and $govuk-is-ie8 {
24
24
  $mq-responsive: false;
25
25
  }
26
26
 
@@ -46,7 +46,7 @@
46
46
 
47
47
  $perpendicular: $base / 2;
48
48
 
49
- @if ($height == null) {
49
+ @if not $height {
50
50
  $height: _govuk-equilateral-height($base);
51
51
  }
52
52
 
@@ -39,7 +39,7 @@
39
39
  }
40
40
 
41
41
  $is-negative: false;
42
- @if ($spacing-point < 0) {
42
+ @if $spacing-point < 0 {
43
43
  $is-negative: true;
44
44
  $spacing-point: abs($spacing-point);
45
45
  }
@@ -94,12 +94,12 @@
94
94
  // Loop through each breakpoint in the map
95
95
  @each $breakpoint, $breakpoint-value in $scale-map {
96
96
 
97
- @if ($adjustment) {
97
+ @if $adjustment {
98
98
  $breakpoint-value: $breakpoint-value + $adjustment;
99
99
  }
100
100
 
101
101
  // The 'null' breakpoint is for mobile.
102
- @if $breakpoint == null {
102
+ @if not $breakpoint {
103
103
 
104
104
  @if $direction == all {
105
105
  #{$property}: $breakpoint-value if($important, !important, null);
@@ -23,7 +23,7 @@
23
23
  // We do not need to include the GDS Transport font-face declarations if
24
24
  // alphagov/govuk_template is being used since nta will already be included by
25
25
  // default.
26
- @if ($govuk-include-default-font-face) {
26
+ @if $govuk-include-default-font-face {
27
27
  @include _govuk-font-face-gds-transport;
28
28
  }
29
29
 
@@ -86,12 +86,13 @@
86
86
 
87
87
  /// Responsive typography helper
88
88
  ///
89
- /// Takes a 'font map' as an argument and uses it to create font-size and
90
- /// line-height declarations for different breakpoints, and for print.
89
+ /// Takes a point from the responsive 'font map' as an argument (the size as it
90
+ /// would appear on tablet and above), and uses it to create font-size and
91
+ /// line-height declarations for different breakpoints, and print.
91
92
  ///
92
93
  /// Example font map:
93
94
  ///
94
- /// $my-font-map: (
95
+ /// 19: (
95
96
  /// null: (
96
97
  /// font-size: 16px,
97
98
  /// line-height: 20px
@@ -106,12 +107,15 @@
106
107
  /// )
107
108
  /// );
108
109
  ///
109
- /// @param {Map} $font-map - Font map
110
+ /// @param {Number} $size - Point from the spacing scale (the size as it would
111
+ /// appear on tablet and above)
110
112
  /// @param {Number} $override-line-height [false] - Non responsive custom line
111
113
  /// height. Omit to use the line height from the font map.
112
114
  /// @param {Boolean} $important [false] - Whether to mark declarations as
113
115
  /// `!important`.
114
116
  ///
117
+ /// @throw if `$size` is not a valid point from the spacing scale
118
+ ///
115
119
  /// @access public
116
120
 
117
121
  @mixin govuk-typography-responsive($size, $override-line-height: false, $important: false) {
@@ -142,7 +146,7 @@
142
146
  $font-size-rem: $font-size-rem if($important, !important, null);
143
147
  $line-height: $line-height if($important, !important, null);
144
148
 
145
- @if $breakpoint == null {
149
+ @if not $breakpoint {
146
150
  font-size: $font-size;
147
151
  @if $govuk-typography-use-rem {
148
152
  font-size: $font-size-rem;
@@ -167,11 +171,14 @@
167
171
 
168
172
  /// Font helper
169
173
  ///
170
- /// @param {Number} $size - Size of the font as it would appear on desktop -
171
- /// uses the responsive font size map
174
+ /// @param {Number | Boolean} $size Point from the spacing scale (the size as it
175
+ /// would appear on tablet and above). Use `false` to avoid setting a size.
172
176
  /// @param {String} $weight [regular] - Weight: `bold` or `regular`
173
177
  /// @param {Boolean} $tabular [false] - Whether to use tabular numbers or not
174
- /// @param {Number} $line-height [false] - Line-height, if overriding the default
178
+ /// @param {Number} $line-height [false] - Line-height, if overriding the
179
+ /// default
180
+ ///
181
+ /// @throw if `$size` is not a valid point from the spacing scale (or false)
175
182
  ///
176
183
  /// @access public
177
184
 
@@ -0,0 +1,390 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define('GOVUKFrontend', ['exports'], factory) :
4
+ (factory((global.GOVUKFrontend = {})));
5
+ }(this, (function (exports) { 'use strict';
6
+
7
+ /**
8
+ * Internal support for selecting messages to render, with placeholder
9
+ * interpolation and locale-aware number formatting and pluralisation
10
+ *
11
+ * @class
12
+ * @private
13
+ * @param {TranslationsFlattened} translations - Key-value pairs of the translation strings to use.
14
+ * @param {object} [config] - Configuration options for the function.
15
+ * @param {string} config.locale - An overriding locale for the PluralRules functionality.
16
+ */
17
+ function I18n (translations, config) {
18
+ // Make list of translations available throughout function
19
+ this.translations = translations || {};
20
+
21
+ // The locale to use for PluralRules and NumberFormat
22
+ this.locale = (config && config.locale) || document.documentElement.lang || 'en';
23
+ }
24
+
25
+ /**
26
+ * The most used function - takes the key for a given piece of UI text and
27
+ * returns the appropriate string.
28
+ *
29
+ * @param {string} lookupKey - The lookup key of the string to use.
30
+ * @param {object} options - Any options passed with the translation string, e.g: for string interpolation.
31
+ * @returns {string} The appropriate translation string.
32
+ */
33
+ I18n.prototype.t = function (lookupKey, options) {
34
+ if (!lookupKey) {
35
+ // Print a console error if no lookup key has been provided
36
+ throw new Error('i18n: lookup key missing')
37
+ }
38
+
39
+ // If the `count` option is set, determine which plural suffix is needed and
40
+ // change the lookupKey to match. We check to see if it's undefined instead of
41
+ // falsy, as this could legitimately be 0.
42
+ if (options && typeof options.count !== 'undefined') {
43
+ // Get the plural suffix
44
+ lookupKey = lookupKey + '.' + this.getPluralSuffix(lookupKey, options.count);
45
+ }
46
+
47
+ if (lookupKey in this.translations) {
48
+ // Fetch the translation string for that lookup key
49
+ var translationString = this.translations[lookupKey];
50
+
51
+ // Check for ${} placeholders in the translation string
52
+ if (translationString.match(/%{(.\S+)}/)) {
53
+ if (!options) {
54
+ throw new Error('i18n: cannot replace placeholders in string if no option data provided')
55
+ }
56
+
57
+ return this.replacePlaceholders(translationString, options)
58
+ } else {
59
+ return translationString
60
+ }
61
+ } else {
62
+ // If the key wasn't found in our translations object,
63
+ // return the lookup key itself as the fallback
64
+ return lookupKey
65
+ }
66
+ };
67
+
68
+ /**
69
+ * Takes a translation string with placeholders, and replaces the placeholders
70
+ * with the provided data
71
+ *
72
+ * @param {string} translationString - The translation string
73
+ * @param {object} options - Any options passed with the translation string, e.g: for string interpolation.
74
+ * @returns {string} The translation string to output, with ${} placeholders replaced
75
+ */
76
+ I18n.prototype.replacePlaceholders = function (translationString, options) {
77
+ var formatter;
78
+
79
+ if (this.hasIntlNumberFormatSupport()) {
80
+ formatter = new Intl.NumberFormat(this.locale);
81
+ }
82
+
83
+ return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) {
84
+ if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
85
+ var placeholderValue = options[placeholderKey];
86
+
87
+ // If a user has passed `false` as the value for the placeholder
88
+ // treat it as though the value should not be displayed
89
+ if (placeholderValue === false) {
90
+ return ''
91
+ }
92
+
93
+ // If the placeholder's value is a number, localise the number formatting
94
+ if (typeof placeholderValue === 'number' && formatter) {
95
+ return formatter.format(placeholderValue)
96
+ }
97
+
98
+ return placeholderValue
99
+ } else {
100
+ throw new Error('i18n: no data found to replace ' + placeholderWithBraces + ' placeholder in string')
101
+ }
102
+ })
103
+ };
104
+
105
+ /**
106
+ * Check to see if the browser supports Intl and Intl.PluralRules.
107
+ *
108
+ * It requires all conditions to be met in order to be supported:
109
+ * - The browser supports the Intl class (true in IE11)
110
+ * - The implementation of Intl supports PluralRules (NOT true in IE11)
111
+ * - The browser/OS has plural rules for the current locale (browser dependent)
112
+ *
113
+ * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
114
+ */
115
+ I18n.prototype.hasIntlPluralRulesSupport = function () {
116
+ return Boolean(window.Intl && ('PluralRules' in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length))
117
+ };
118
+
119
+ /**
120
+ * Check to see if the browser supports Intl and Intl.NumberFormat.
121
+ *
122
+ * It requires all conditions to be met in order to be supported:
123
+ * - The browser supports the Intl class (true in IE11)
124
+ * - The implementation of Intl supports NumberFormat (also true in IE11)
125
+ * - The browser/OS has number formatting rules for the current locale (browser dependent)
126
+ *
127
+ * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
128
+ */
129
+ I18n.prototype.hasIntlNumberFormatSupport = function () {
130
+ return Boolean(window.Intl && ('NumberFormat' in window.Intl && Intl.NumberFormat.supportedLocalesOf(this.locale).length))
131
+ };
132
+
133
+ /**
134
+ * Get the appropriate suffix for the plural form.
135
+ *
136
+ * Uses Intl.PluralRules (or our own fallback implementation) to get the
137
+ * 'preferred' form to use for the given count.
138
+ *
139
+ * Checks that a translation has been provided for that plural form – if it
140
+ * hasn't, it'll fall back to the 'other' plural form (unless that doesn't exist
141
+ * either, in which case an error will be thrown)
142
+ *
143
+ * @param {string} lookupKey - The lookup key of the string to use.
144
+ * @param {number} count - Number used to determine which pluralisation to use.
145
+ * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.
146
+ */
147
+ I18n.prototype.getPluralSuffix = function (lookupKey, count) {
148
+ // Validate that the number is actually a number.
149
+ //
150
+ // Number(count) will turn anything that can't be converted to a Number type
151
+ // into 'NaN'. isFinite filters out NaN, as it isn't a finite number.
152
+ count = Number(count);
153
+ if (!isFinite(count)) { return 'other' }
154
+
155
+ var preferredForm;
156
+
157
+ // Check to verify that all the requirements for Intl.PluralRules are met.
158
+ // If so, we can use that instead of our custom implementation. Otherwise,
159
+ // use the hardcoded fallback.
160
+ if (this.hasIntlPluralRulesSupport()) {
161
+ preferredForm = new Intl.PluralRules(this.locale).select(count);
162
+ } else {
163
+ preferredForm = this.selectPluralFormUsingFallbackRules(count);
164
+ }
165
+
166
+ // Use the correct plural form if provided
167
+ if (lookupKey + '.' + preferredForm in this.translations) {
168
+ return preferredForm
169
+ // Fall back to `other` if the plural form is missing, but log a warning
170
+ // to the console
171
+ } else if (lookupKey + '.other' in this.translations) {
172
+ if (console && 'warn' in console) {
173
+ console.warn('i18n: Missing plural form ".' + preferredForm + '" for "' +
174
+ this.locale + '" locale. Falling back to ".other".');
175
+ }
176
+
177
+ return 'other'
178
+ // If the required `other` plural form is missing, all we can do is error
179
+ } else {
180
+ throw new Error(
181
+ 'i18n: Plural form ".other" is required for "' + this.locale + '" locale'
182
+ )
183
+ }
184
+ };
185
+
186
+ /**
187
+ * Get the plural form using our fallback implementation
188
+ *
189
+ * This is split out into a separate function to make it easier to test the
190
+ * fallback behaviour in an environment where Intl.PluralRules exists.
191
+ *
192
+ * @param {number} count - Number used to determine which pluralisation to use.
193
+ * @returns {PluralRule} The pluralisation form for count in this locale.
194
+ */
195
+ I18n.prototype.selectPluralFormUsingFallbackRules = function (count) {
196
+ // Currently our custom code can only handle positive integers, so let's
197
+ // make sure our number is one of those.
198
+ count = Math.abs(Math.floor(count));
199
+
200
+ var ruleset = this.getPluralRulesForLocale();
201
+
202
+ if (ruleset) {
203
+ return I18n.pluralRules[ruleset](count)
204
+ }
205
+
206
+ return 'other'
207
+ };
208
+
209
+ /**
210
+ * Work out which pluralisation rules to use for the current locale
211
+ *
212
+ * The locale may include a regional indicator (such as en-GB), but we don't
213
+ * usually care about this part, as pluralisation rules are usually the same
214
+ * regardless of region. There are exceptions, however, (e.g. Portuguese) so
215
+ * this searches by both the full and shortened locale codes, just to be sure.
216
+ *
217
+ * @returns {PluralRuleName | undefined} The name of the pluralisation rule to use (a key for one
218
+ * of the functions in this.pluralRules)
219
+ */
220
+ I18n.prototype.getPluralRulesForLocale = function () {
221
+ var locale = this.locale;
222
+ var localeShort = locale.split('-')[0];
223
+
224
+ // Look through the plural rules map to find which `pluralRule` is
225
+ // appropriate for our current `locale`.
226
+ for (var pluralRule in I18n.pluralRulesMap) {
227
+ if (Object.prototype.hasOwnProperty.call(I18n.pluralRulesMap, pluralRule)) {
228
+ var languages = I18n.pluralRulesMap[pluralRule];
229
+ for (var i = 0; i < languages.length; i++) {
230
+ if (languages[i] === locale || languages[i] === localeShort) {
231
+ return pluralRule
232
+ }
233
+ }
234
+ }
235
+ }
236
+ };
237
+
238
+ /**
239
+ * Map of plural rules to languages where those rules apply.
240
+ *
241
+ * Note: These groups are named for the most dominant or recognisable language
242
+ * that uses each system. The groupings do not imply that the languages are
243
+ * related to one another. Many languages have evolved the same systems
244
+ * independently of one another.
245
+ *
246
+ * Code to support more languages can be found in the i18n spike:
247
+ * {@link https://github.com/alphagov/govuk-frontend/blob/spike-i18n-support/src/govuk/i18n.mjs}
248
+ *
249
+ * Languages currently supported:
250
+ *
251
+ * Arabic: Arabic (ar)
252
+ * Chinese: Burmese (my), Chinese (zh), Indonesian (id), Japanese (ja),
253
+ * Javanese (jv), Korean (ko), Malay (ms), Thai (th), Vietnamese (vi)
254
+ * French: Armenian (hy), Bangla (bn), French (fr), Gujarati (gu), Hindi (hi),
255
+ * Persian Farsi (fa), Punjabi (pa), Zulu (zu)
256
+ * German: Afrikaans (af), Albanian (sq), Azerbaijani (az), Basque (eu),
257
+ * Bulgarian (bg), Catalan (ca), Danish (da), Dutch (nl), English (en),
258
+ * Estonian (et), Finnish (fi), Georgian (ka), German (de), Greek (el),
259
+ * Hungarian (hu), Luxembourgish (lb), Norwegian (no), Somali (so),
260
+ * Swahili (sw), Swedish (sv), Tamil (ta), Telugu (te), Turkish (tr),
261
+ * Urdu (ur)
262
+ * Irish: Irish Gaelic (ga)
263
+ * Russian: Russian (ru), Ukrainian (uk)
264
+ * Scottish: Scottish Gaelic (gd)
265
+ * Spanish: European Portuguese (pt-PT), Italian (it), Spanish (es)
266
+ * Welsh: Welsh (cy)
267
+ *
268
+ * @type {Object<PluralRuleName, string[]>}
269
+ */
270
+ I18n.pluralRulesMap = {
271
+ arabic: ['ar'],
272
+ chinese: ['my', 'zh', 'id', 'ja', 'jv', 'ko', 'ms', 'th', 'vi'],
273
+ french: ['hy', 'bn', 'fr', 'gu', 'hi', 'fa', 'pa', 'zu'],
274
+ german: [
275
+ 'af', 'sq', 'az', 'eu', 'bg', 'ca', 'da', 'nl', 'en', 'et', 'fi', 'ka',
276
+ 'de', 'el', 'hu', 'lb', 'no', 'so', 'sw', 'sv', 'ta', 'te', 'tr', 'ur'
277
+ ],
278
+ irish: ['ga'],
279
+ russian: ['ru', 'uk'],
280
+ scottish: ['gd'],
281
+ spanish: ['pt-PT', 'it', 'es'],
282
+ welsh: ['cy']
283
+ };
284
+
285
+ /**
286
+ * Different pluralisation rule sets
287
+ *
288
+ * Returns the appropriate suffix for the plural form associated with `n`.
289
+ * Possible suffixes: 'zero', 'one', 'two', 'few', 'many', 'other' (the actual
290
+ * meaning of each differs per locale). 'other' should always exist, even in
291
+ * languages without plurals, such as Chinese.
292
+ * {@link https://cldr.unicode.org/index/cldr-spec/plural-rules}
293
+ *
294
+ * The count must be a positive integer. Negative numbers and decimals aren't accounted for
295
+ *
296
+ * @type {Object<string, function(number): PluralRule>}
297
+ */
298
+ I18n.pluralRules = {
299
+ arabic: function (n) {
300
+ if (n === 0) { return 'zero' }
301
+ if (n === 1) { return 'one' }
302
+ if (n === 2) { return 'two' }
303
+ if (n % 100 >= 3 && n % 100 <= 10) { return 'few' }
304
+ if (n % 100 >= 11 && n % 100 <= 99) { return 'many' }
305
+ return 'other'
306
+ },
307
+ chinese: function () {
308
+ return 'other'
309
+ },
310
+ french: function (n) {
311
+ return n === 0 || n === 1 ? 'one' : 'other'
312
+ },
313
+ german: function (n) {
314
+ return n === 1 ? 'one' : 'other'
315
+ },
316
+ irish: function (n) {
317
+ if (n === 1) { return 'one' }
318
+ if (n === 2) { return 'two' }
319
+ if (n >= 3 && n <= 6) { return 'few' }
320
+ if (n >= 7 && n <= 10) { return 'many' }
321
+ return 'other'
322
+ },
323
+ russian: function (n) {
324
+ var lastTwo = n % 100;
325
+ var last = lastTwo % 10;
326
+ if (last === 1 && lastTwo !== 11) { return 'one' }
327
+ if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) { return 'few' }
328
+ if (last === 0 || (last >= 5 && last <= 9) || (lastTwo >= 11 && lastTwo <= 14)) { return 'many' }
329
+ // Note: The 'other' suffix is only used by decimal numbers in Russian.
330
+ // We don't anticipate it being used, but it's here for consistency.
331
+ return 'other'
332
+ },
333
+ scottish: function (n) {
334
+ if (n === 1 || n === 11) { return 'one' }
335
+ if (n === 2 || n === 12) { return 'two' }
336
+ if ((n >= 3 && n <= 10) || (n >= 13 && n <= 19)) { return 'few' }
337
+ return 'other'
338
+ },
339
+ spanish: function (n) {
340
+ if (n === 1) { return 'one' }
341
+ if (n % 1000000 === 0 && n !== 0) { return 'many' }
342
+ return 'other'
343
+ },
344
+ welsh: function (n) {
345
+ if (n === 0) { return 'zero' }
346
+ if (n === 1) { return 'one' }
347
+ if (n === 2) { return 'two' }
348
+ if (n === 3) { return 'few' }
349
+ if (n === 6) { return 'many' }
350
+ return 'other'
351
+ }
352
+ };
353
+
354
+ /**
355
+ * Supported languages for plural rules
356
+ *
357
+ * @typedef {'arabic' | 'chinese' | 'french' | 'german' | 'irish' | 'russian' | 'scottish' | 'spanish' | 'welsh'} PluralRuleName
358
+ */
359
+
360
+ /**
361
+ * Plural rule category mnemonic tags
362
+ *
363
+ * @typedef {'zero' | 'one' | 'two' | 'few' | 'many' | 'other'} PluralRule
364
+ */
365
+
366
+ /**
367
+ * Translated message by plural rule they correspond to.
368
+ *
369
+ * Allows to group pluralised messages under a single key when passing
370
+ * translations to a component's constructor
371
+ *
372
+ * @typedef {object} TranslationPluralForms
373
+ * @property {string} [other] - General plural form
374
+ * @property {string} [zero] - Plural form used with 0
375
+ * @property {string} [one] - Plural form used with 1
376
+ * @property {string} [two] - Plural form used with 2
377
+ * @property {string} [few] - Plural form used for a few
378
+ * @property {string} [many] - Plural form used for many
379
+ */
380
+
381
+ /**
382
+ * Translated messages (flattened)
383
+ *
384
+ * @private
385
+ * @typedef {Object<string, string> | {}} TranslationsFlattened
386
+ */
387
+
388
+ exports.I18n = I18n;
389
+
390
+ })));
@@ -29,19 +29,12 @@
29
29
  // margins, but unfortunately the support isn't there (yet) and @supports
30
30
  // doesn't play nicely with it
31
31
  // (https://github.com/w3c/csswg-drafts/issues/3559)
32
- display: -webkit-box;
33
- display: -webkit-flex;
34
32
  display: -ms-flexbox;
35
33
  display: flex;
36
- -webkit-box-orient: vertical;
37
- -webkit-box-direction: normal;
38
- -webkit-flex-direction: column;
39
- -ms-flex-direction: column;
40
- flex-direction: column;
41
- -webkit-box-align: center;
42
- -webkit-align-items: center;
43
- -ms-flex-align: center;
44
- align-items: center;
34
+ -ms-flex-direction: column;
35
+ flex-direction: column;
36
+ -ms-flex-align: center;
37
+ align-items: center;
45
38
 
46
39
  // Give links within the button group the same font-size and line-height
47
40
  // as buttons.
@@ -71,22 +64,13 @@
71
64
  // Cancel out the column gap for the last item in each row
72
65
  margin-right: ($horizontal-gap * -1);
73
66
 
74
- -webkit-box-orient: horizontal;
67
+ -ms-flex-direction: row;
75
68
 
76
- -webkit-box-direction: normal;
77
-
78
- -webkit-flex-direction: row;
79
-
80
- -ms-flex-direction: row;
81
-
82
- flex-direction: row;
83
- -webkit-flex-wrap: wrap;
84
- -ms-flex-wrap: wrap;
85
- flex-wrap: wrap;
86
- -webkit-box-align: baseline;
87
- -webkit-align-items: baseline;
88
- -ms-flex-align: baseline;
89
- align-items: baseline;
69
+ flex-direction: row;
70
+ -ms-flex-wrap: wrap;
71
+ flex-wrap: wrap;
72
+ -ms-flex-align: baseline;
73
+ align-items: baseline;
90
74
 
91
75
  .govuk-button,
92
76
  .govuk-link {
@@ -11,7 +11,7 @@
11
11
  // Prevent automatic text sizing, as we already cater for small devices and
12
12
  // would like the browser to stay on 100% text zoom by default.
13
13
  -webkit-text-size-adjust: 100%;
14
- -ms-text-size-adjust: 100%;
14
+ -moz-text-size-adjust: 100%;
15
15
  text-size-adjust: 100%;
16
16
 
17
17
  // Force the scrollbar to always display in IE, to prevent horizontal page
@@ -28,9 +28,7 @@
28
28
 
29
29
  // Respect 'display cutout' safe area (avoids notches and rounded corners)
30
30
  @supports (margin: unquote("max(calc(0px))")) {
31
- $gutter-safe-area-right: -webkit-calc(#{$govuk-gutter-half} + env(safe-area-inset-right));
32
31
  $gutter-safe-area-right: calc(#{$govuk-gutter-half} + env(safe-area-inset-right));
33
- $gutter-safe-area-left: -webkit-calc(#{$govuk-gutter-half} + env(safe-area-inset-left));
34
32
  $gutter-safe-area-left: calc(#{$govuk-gutter-half} + env(safe-area-inset-left));
35
33
 
36
34
  // Use max() to pick largest margin, default or with safe area
@@ -46,9 +44,7 @@
46
44
 
47
45
  // Respect 'display cutout' safe area (avoids notches and rounded corners)
48
46
  @supports (margin: unquote("max(calc(0px))")) {
49
- $gutter-safe-area-right: -webkit-calc(#{$govuk-gutter-half} + env(safe-area-inset-right));
50
47
  $gutter-safe-area-right: calc(#{$govuk-gutter-half} + env(safe-area-inset-right));
51
- $gutter-safe-area-left: -webkit-calc(#{$govuk-gutter-half} + env(safe-area-inset-left));
52
48
  $gutter-safe-area-left: calc(#{$govuk-gutter-half} + env(safe-area-inset-left));
53
49
 
54
50
  // Use max() to pick largest margin, default or with safe area