@ons/design-system 44.0.0 → 44.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/components/address-input/_macro.njk +1 -0
  2. package/components/address-input/autosuggest.address.js +0 -1
  3. package/components/autosuggest/_macro.njk +1 -0
  4. package/components/autosuggest/autosuggest.ui.js +1 -1
  5. package/components/checkboxes/_checkbox.scss +1 -1
  6. package/components/checkboxes/_macro.njk +3 -3
  7. package/components/collapsible/_collapsible.scss +1 -1
  8. package/components/input/_input.scss +6 -2
  9. package/components/message/_message.scss +3 -1
  10. package/components/modal/_macro.njk +23 -0
  11. package/components/modal/_modal.scss +32 -0
  12. package/components/modal/modal.dom.js +14 -0
  13. package/components/modal/modal.js +104 -0
  14. package/components/summary/_summary.scss +4 -2
  15. package/components/table/_table.scss +1 -1
  16. package/components/timeout-modal/_macro.njk +26 -0
  17. package/components/timeout-modal/timeout.dom.js +16 -0
  18. package/components/timeout-modal/timeout.js +289 -0
  19. package/css/census.css +1 -1
  20. package/css/error.css +1 -1
  21. package/css/main.css +1 -1
  22. package/favicons/census/cy/android-chrome-192x192.png +0 -0
  23. package/favicons/census/cy/android-chrome-512x512.png +0 -0
  24. package/favicons/census/cy/apple-touch-icon.png +0 -0
  25. package/favicons/census/cy/browserconfig.xml +12 -0
  26. package/favicons/census/cy/favicon-16x16.png +0 -0
  27. package/favicons/census/cy/favicon-32x32.png +0 -0
  28. package/favicons/census/cy/favicon.ico +0 -0
  29. package/favicons/census/cy/manifest.json +20 -0
  30. package/favicons/census/cy/mstitle-150x150.png +0 -0
  31. package/favicons/census/cy/mstitle-310x150.png +0 -0
  32. package/favicons/census/cy/mstitle-310x310.png +0 -0
  33. package/favicons/census/cy/mstitle-70x70.png +0 -0
  34. package/favicons/census/cy/opengraph.png +0 -0
  35. package/favicons/census/cy/safari-pinned-tab.svg +3 -0
  36. package/favicons/census/cy/site.webmanifest +19 -0
  37. package/favicons/census/cy/twitter.png +0 -0
  38. package/favicons/census/en/android-chrome-192x192.png +0 -0
  39. package/favicons/census/en/android-chrome-512x512.png +0 -0
  40. package/favicons/census/en/apple-touch-icon.png +0 -0
  41. package/favicons/census/en/browserconfig.xml +12 -0
  42. package/favicons/census/en/favicon-16x16.png +0 -0
  43. package/favicons/census/en/favicon-32x32.png +0 -0
  44. package/favicons/census/en/favicon.ico +0 -0
  45. package/favicons/census/en/manifest.json +20 -0
  46. package/favicons/census/en/mstitle-150x150.png +0 -0
  47. package/favicons/census/en/mstitle-310x150.png +0 -0
  48. package/favicons/census/en/mstitle-310x310.png +0 -0
  49. package/favicons/census/en/mstitle-70x70.png +0 -0
  50. package/favicons/census/en/opengraph.png +0 -0
  51. package/favicons/census/en/safari-pinned-tab.svg +3 -0
  52. package/favicons/census/en/site.webmanifest +19 -0
  53. package/favicons/census/en/twitter.png +0 -0
  54. package/favicons/census/ni/favicon.ico +0 -0
  55. package/package.json +8 -11
  56. package/scripts/main.es5.js +2 -2
  57. package/scripts/main.js +1 -1
  58. package/scss/base/_global.scss +9 -5
  59. package/scss/error.scss +0 -2
  60. package/scss/helpers/_functions.scss +4 -2
  61. package/scss/main.scss +65 -8
  62. package/scss/overrides/rtl.scss +4 -2
  63. package/scss/patternlib.scss +3 -2
  64. package/scss/utilities/_colors.scss +4 -4
  65. package/scss/utilities/_grid.scss +7 -5
  66. package/src/js/domready.js +17 -0
@@ -131,6 +131,7 @@
131
131
  "isEditable": params.isEditable,
132
132
  "ariaYouHaveSelected": params.ariaYouHaveSelected,
133
133
  "ariaMinChars": params.ariaMinChars,
134
+ "minChars": params.minChars,
134
135
  "ariaResultsLabel": params.ariaResultsLabel,
135
136
  "ariaOneResult": params.ariaOneResult,
136
137
  "ariaNResults": params.ariaNResults,
@@ -54,7 +54,6 @@ export default class AutosuggestAddress {
54
54
  suggestionFunction: this.suggestAddresses.bind(this),
55
55
  sanitisedQueryReplaceChars: this.addressReplaceChars,
56
56
  sanitisedQuerySplitNumsChars: this.sanitisedQuerySplitNumsChars,
57
- minChars: 3,
58
57
  suggestOnBoot: true,
59
58
  handleUpdate: true,
60
59
  });
@@ -6,6 +6,7 @@
6
6
  class="{% if not params.externalInitialiser %}ons-js-autosuggest {% endif %}{% if params.isEditable == false %}ons-js-address-not-editable{% endif %}{% if params.mandatory is defined and params.mandatory == true %} ons-js-address-mandatory{% endif %} {% if params.containerClasses is defined and params.containerClasses %} {{ params.containerClasses }}{% endif %} ons-autosuggest-input"
7
7
  data-instructions="{{ params.instructions }}"
8
8
  data-aria-you-have-selected="{{ params.ariaYouHaveSelected }}"
9
+ data-min-chars="{{ params.minChars }}"
9
10
  data-aria-min-chars="{{ params.ariaMinChars }}"
10
11
  data-aria-one-result="{{ params.ariaOneResult }}"
11
12
  data-aria-n-results="{{ params.ariaNResults }}"
@@ -52,6 +52,7 @@ export default class AutosuggestUI {
52
52
  // Settings
53
53
  this.autosuggestData = autosuggestData || context.getAttribute('data-autosuggest-data');
54
54
  this.ariaYouHaveSelected = ariaYouHaveSelected || context.getAttribute('data-aria-you-have-selected');
55
+ this.minChars = minChars || context.getAttribute('data-min-chars') || 3;
55
56
  this.ariaMinChars = ariaMinChars || context.getAttribute('data-aria-min-chars');
56
57
  this.ariaOneResult = ariaOneResult || context.getAttribute('data-aria-one-result');
57
58
  this.ariaNResults = ariaNResults || context.getAttribute('data-aria-n-results');
@@ -66,7 +67,6 @@ export default class AutosuggestUI {
66
67
  this.typeMore = typeMore || context.getAttribute('data-type-more');
67
68
  this.allowMultiple = context.getAttribute('data-allow-multiple') || false;
68
69
  this.listboxId = this.listbox.getAttribute('id');
69
- this.minChars = minChars || 3;
70
70
  this.resultLimit = resultLimit || 10;
71
71
  this.suggestOnBoot = suggestOnBoot;
72
72
  this.lang = lang || 'en';
@@ -124,7 +124,7 @@ $checkbox-padding: 11px;
124
124
  }
125
125
 
126
126
  &__input:focus + &__label::before {
127
- @extend .ons-input:focus;
127
+ @extend %ons-input-focus;
128
128
  }
129
129
 
130
130
  &__input:not(:checked) ~ &__other {
@@ -40,9 +40,9 @@
40
40
  {% endif %}
41
41
  {% if checkbox.other is defined and checkbox.other %}
42
42
  {% set otherClass = " ons-js-other" %}
43
- {% endif %}
44
- {% if checkbox.other.selectAllChildren is defined and checkbox.other.selectAllChildren == true %}
45
- {% set otherClass = otherClass + " ons-js-select-all-children" %}
43
+ {% if checkbox.other.selectAllChildren is defined and checkbox.other.selectAllChildren == true %}
44
+ {% set otherClass = otherClass + " ons-js-select-all-children" %}
45
+ {% endif %}
46
46
  {% endif %}
47
47
  <span class="ons-checkboxes__item{{ " ons-checkboxes__item--no-border" if params.borderless }}">
48
48
  {% set config = {
@@ -17,7 +17,7 @@ $collapsible-caret-width: 1.5rem;
17
17
 
18
18
  &:focus {
19
19
  .ons-collapsible__title {
20
- @extend a:focus;
20
+ @extend %a-focus;
21
21
  // extend collapsible focus background behind caret
22
22
  margin-left: -$collapsible-caret-width;
23
23
  padding-left: $collapsible-caret-width;
@@ -1,3 +1,8 @@
1
+ %ons-input-focus {
2
+ box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
3
+ outline: none;
4
+ }
5
+
1
6
  .ons-input {
2
7
  border: $input-border-width solid $color-input;
3
8
  border-radius: $input-radius;
@@ -31,8 +36,7 @@
31
36
  }
32
37
 
33
38
  &:focus {
34
- box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
35
- outline: none;
39
+ @extend %ons-input-focus;
36
40
  }
37
41
 
38
42
  &:disabled {
@@ -1,5 +1,7 @@
1
+ @use 'sass:math';
2
+
1
3
  .ons-message {
2
- border-radius: $grid-gutters / 2;
4
+ border-radius: math.div($grid-gutters, 2);
3
5
  margin-bottom: 2rem;
4
6
 
5
7
  &--sent {
@@ -0,0 +1,23 @@
1
+ {% macro onsModal(params) %}
2
+ {% set modalID = params.id | default('dialog') %}
3
+ <dialog class="ons-modal ons-js-modal {{ params.classes }}"
4
+ id="{{ modalID }}"
5
+ role="dialog"
6
+ aria-labelledby="ons-modal-title"
7
+ {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %} {{attribute}}="{{value}}"{% endfor %}{% endif %}
8
+ >
9
+ <h1 id="ons-modal-title" class="ons-modal__title">
10
+ {{ params.title }}
11
+ </h1>
12
+ <div class="ons-modal__body">
13
+ {{ (params.body if params else "") | safe }}{{ caller() if caller }}
14
+ </div>
15
+ {% if params.btnText %}
16
+ {% from "components/button/_macro.njk" import onsButton %}
17
+ {{ onsButton({
18
+ "text": params.btnText,
19
+ "classes": "ons-js-modal-btn ons-u-mt-s"
20
+ }) }}
21
+ {% endif %}
22
+ </dialog>
23
+ {% endmacro %}
@@ -0,0 +1,32 @@
1
+ $backdrop-colour: rgba(0, 0, 0, 0.8);
2
+
3
+ .ons-modal {
4
+ border: none;
5
+ border-radius: 0.4rem;
6
+ box-shadow: 0 0 7px 0 #000;
7
+ display: none;
8
+ margin-left: 2rem;
9
+ margin-right: 2rem;
10
+ max-width: 500px;
11
+ padding: 2rem;
12
+ width: auto;
13
+
14
+ @media screen and (min-width: 600px) {
15
+ margin-left: auto;
16
+ margin-right: auto;
17
+ width: 100%;
18
+ }
19
+
20
+ &::backdrop {
21
+ backdrop-filter: blur(3px);
22
+ background: $backdrop-colour;
23
+ }
24
+
25
+ & + .backdrop {
26
+ background: $backdrop-colour;
27
+ }
28
+ }
29
+
30
+ .ons-modal-overlay {
31
+ overflow: hidden;
32
+ }
@@ -0,0 +1,14 @@
1
+ import domready from '../../js/domready';
2
+
3
+ async function modals() {
4
+ const modals = [...document.querySelectorAll('.ons-js-modal')];
5
+ const timeouts = [...document.querySelectorAll('.ons-js-timeout-modal')];
6
+
7
+ if (modals.length && !timeouts.length) {
8
+ const Modal = (await import('./modal')).default;
9
+
10
+ modals.forEach(component => new Modal(component));
11
+ }
12
+ }
13
+
14
+ domready(modals);
@@ -0,0 +1,104 @@
1
+ import dialogPolyfill from 'dialog-polyfill';
2
+
3
+ const overLayClass = 'ons-modal-overlay';
4
+
5
+ export default class Modal {
6
+ constructor(component) {
7
+ this.component = component;
8
+ this.launcher = document.querySelector(`[data-modal-id=${component.id}]`);
9
+ this.closeButton = component.querySelector('.ons-js-modal-btn');
10
+ this.lastFocusedEl = null;
11
+
12
+ this.initialise();
13
+ }
14
+
15
+ initialise() {
16
+ if (!this.dialogSupported()) {
17
+ /* istanbul ignore next */
18
+ return;
19
+ }
20
+
21
+ if (this.launcher) {
22
+ this.launcher.addEventListener('click', this.openDialog.bind(this));
23
+ }
24
+
25
+ if (this.closeButton) {
26
+ this.closeButton.addEventListener('click', this.closeDialog.bind(this));
27
+ }
28
+ }
29
+
30
+ dialogSupported() {
31
+ if (typeof HTMLDialogElement === 'function') {
32
+ return true;
33
+ } else {
34
+ try {
35
+ dialogPolyfill.registerDialog(this.component);
36
+ return true;
37
+ } catch (error) {
38
+ /* istanbul ignore next */
39
+ return false;
40
+ }
41
+ }
42
+ }
43
+
44
+ openDialog(event) {
45
+ if (!this.isDialogOpen()) {
46
+ this.component.classList.add('ons-u-db');
47
+ document.querySelector('body').className += ' ' + overLayClass;
48
+ this.makePageContentInert();
49
+ this.saveLastFocusedEl();
50
+ if (event) {
51
+ const modal = document.getElementById(event.target.getAttribute('data-modal-id'));
52
+ modal.showModal();
53
+ } else {
54
+ this.component.showModal();
55
+ }
56
+ }
57
+ }
58
+
59
+ saveLastFocusedEl() {
60
+ this.lastFocusedEl = document.activeElement;
61
+ if (!this.lastFocusedEl || this.lastFocusedEl === document.body) {
62
+ this.lastFocusedEl = null;
63
+ } else if (document.querySelector) {
64
+ this.lastFocusedEl = document.querySelector(':focus');
65
+ }
66
+ }
67
+
68
+ setFocusOnLastFocusedEl(lastFocusedEl) {
69
+ if (lastFocusedEl) {
70
+ lastFocusedEl.focus();
71
+ }
72
+ }
73
+
74
+ makePageContentInert() {
75
+ if (document.querySelector('.ons-page')) {
76
+ document.querySelector('.ons-page').inert = true;
77
+ document.querySelector('.ons-page').setAttribute('aria-hidden', 'true');
78
+ }
79
+ }
80
+
81
+ removeInertFromPageContent() {
82
+ if (document.querySelector('.ons-page')) {
83
+ document.querySelector('.ons-page').inert = false;
84
+ document.querySelector('.ons-page').setAttribute('aria-hidden', 'false');
85
+ }
86
+ }
87
+
88
+ isDialogOpen() {
89
+ return this.component['open'];
90
+ }
91
+
92
+ closeDialog(event) {
93
+ if (this.isDialogOpen()) {
94
+ if (event) {
95
+ event.preventDefault();
96
+ }
97
+ this.component.classList.remove('ons-u-db');
98
+ document.querySelector('body').classList.remove(overLayClass);
99
+ this.component.close();
100
+ this.setFocusOnLastFocusedEl(this.lastFocusedEl);
101
+ this.removeInertFromPageContent();
102
+ }
103
+ }
104
+ }
@@ -1,3 +1,5 @@
1
+ @use 'sass:math';
2
+
1
3
  $summary-row-spacing: 1rem;
2
4
  $summary-col-spacing: 1rem;
3
5
  $hub-row-spacing: 1.3rem;
@@ -101,8 +103,8 @@ $hub-row-spacing: 1.3rem;
101
103
  padding-right: $summary-col-spacing;
102
104
 
103
105
  @include mq('s') {
104
- padding-left: $summary-col-spacing / 2;
105
- padding-right: $summary-col-spacing / 2;
106
+ padding-left: math.div($summary-col-spacing, 2);
107
+ padding-right: math.div($summary-col-spacing, 2);
106
108
 
107
109
  &:first-child {
108
110
  padding-left: $summary-col-spacing;
@@ -211,7 +211,7 @@
211
211
  }
212
212
 
213
213
  &:focus {
214
- @extend a:focus;
214
+ @extend %a-focus;
215
215
  .ons-svg-icon {
216
216
  fill: $color-black;
217
217
  }
@@ -0,0 +1,26 @@
1
+ {% from "components/modal/_macro.njk" import onsModal %}
2
+ {% macro onsTimeoutModal(params) %}
3
+ {% call onsModal({
4
+ "title": params.title,
5
+ "btnText": params.btnText,
6
+ "classes": "ons-js-timeout-modal",
7
+ "attributes": {
8
+ "data-redirect-url": params.redirectUrl,
9
+ "data-timeout-time": params.serverTimeoutTime,
10
+ "data-show-modal-time": params.showModalTimeInSeconds,
11
+ "data-server-session-expiry-endpoint": params.serverSessionExpiryEndpoint,
12
+ "data-countdown-text": params.countdownText,
13
+ "data-redirecting-text": params.redirectingText,
14
+ "data-minutes-text-singular": params.minutesTextSingular,
15
+ "data-minutes-text-plural": params.minutesTextPlural,
16
+ "data-seconds-text-singular": params.secondsTextSingular,
17
+ "data-seconds-text-plural": params.secondsTextPlural,
18
+ "aria-describedby": "timeout-time-remaining"
19
+ }
20
+ })
21
+ %}
22
+ <p>{{ params.textFirstLine }}</p>
23
+ <p class="ons-js-timeout-timer" aria-hidden="true" aria-relevant="additions"></p>
24
+ <p class="ons-js-timeout-timer-acc ons-u-vh" role="status" id="timeout-time-remaining"></p>
25
+ {% endcall %}
26
+ {% endmacro %}
@@ -0,0 +1,16 @@
1
+ import domready from '../../js/domready';
2
+
3
+ async function modals() {
4
+ const timeouts = [...document.querySelectorAll('.ons-js-timeout-modal')];
5
+
6
+ if (timeouts.length) {
7
+ const Timeout = (await import('./timeout')).default;
8
+
9
+ timeouts.forEach(context => {
10
+ let url = context.getAttribute('data-server-session-expiry-endpoint');
11
+ new Timeout(context, url);
12
+ });
13
+ }
14
+ }
15
+
16
+ domready(modals);
@@ -0,0 +1,289 @@
1
+ import Modal from '../modal/modal';
2
+
3
+ export default class Timeout {
4
+ constructor(context, url, time) {
5
+ this.context = context;
6
+ this.sessionExpiryEndpoint = url;
7
+ this.initialExpiryTime = time;
8
+ this.continueButton = context.querySelector('.ons-js-modal-btn');
9
+ this.countdown = context.querySelector('.ons-js-timeout-timer');
10
+ this.accessibleCountdown = context.querySelector('.ons-js-timeout-timer-acc');
11
+ this.timeOutRedirectUrl = context.getAttribute('data-redirect-url');
12
+ this.modalVisibleInMilliseconds = context.getAttribute('data-show-modal-time') * 1000;
13
+
14
+ // Language dependent text strings
15
+ this.minutesTextSingular = context.getAttribute('data-minutes-text-singular');
16
+ this.minutesTextPlural = context.getAttribute('data-minutes-text-plural');
17
+ this.secondsTextSingular = context.getAttribute('data-seconds-text-singular');
18
+ this.secondsTextPlural = context.getAttribute('data-seconds-text-plural');
19
+ this.countdownText = context.getAttribute('data-countdown-text');
20
+ this.redirectingText = context.getAttribute('data-redirecting-text');
21
+
22
+ // Settings
23
+ this.expiryTimeInMilliseconds = 0;
24
+ this.expiryTime = '';
25
+ this.showModalTimeout = null;
26
+ this.timers = [];
27
+ this.timerRunOnce = false;
28
+
29
+ // Create modal instance
30
+ this.modal = new Modal(this.context);
31
+
32
+ // Start module
33
+ this.initialise();
34
+ }
35
+
36
+ async initialise() {
37
+ this.expiryTime = await this.setNewExpiryTime();
38
+ this.expiryTimeInMilliseconds = this.convertTimeToMilliSeconds(this.expiryTime);
39
+
40
+ this.bindEventListeners();
41
+ }
42
+
43
+ bindEventListeners() {
44
+ window.onload = this.startTimeout();
45
+ window.addEventListener('focus', this.handleWindowFocus.bind(this));
46
+ window.addEventListener('keydown', this.escToClose.bind(this));
47
+ this.continueButton.addEventListener('click', this.closeModalAndRestartTimeout.bind(this));
48
+ this.addThrottledEvents();
49
+ }
50
+
51
+ startTimeout() {
52
+ clearTimeout(this.showModalTimeout);
53
+ this.showModalTimeout = setTimeout(this.openModal.bind(this), this.expiryTimeInMilliseconds - this.modalVisibleInMilliseconds);
54
+ }
55
+
56
+ async openModal() {
57
+ const modalWillOpen = await this.hasExpiryTimeResetInAnotherTab();
58
+ if (modalWillOpen && !this.modal.isDialogOpen()) {
59
+ this.modal.openDialog();
60
+ this.startUiCountdown();
61
+ }
62
+ }
63
+
64
+ async startUiCountdown() {
65
+ this.clearTimers();
66
+ clearInterval(this.shouldModalCloseCheck);
67
+
68
+ this.shouldModalCloseCheck = setInterval(async () => {
69
+ await this.hasExpiryTimeResetInAnotherTab();
70
+ }, 20000);
71
+
72
+ let millseconds = this.modalVisibleInMilliseconds;
73
+ let seconds = millseconds / 1000;
74
+ let timers = this.timers;
75
+ let $this = this;
76
+
77
+ (async function runTimer() {
78
+ const minutesLeft = parseInt(seconds / 60, 10);
79
+ const secondsLeft = parseInt(seconds % 60, 10);
80
+ const timerExpired = minutesLeft < 1 && secondsLeft < 1;
81
+
82
+ const minutesText =
83
+ minutesLeft + ' ' + (minutesLeft >= 2 ? $this.minutesTextPlural : minutesLeft === 1 ? $this.minutesTextSingular : '');
84
+ const secondsText =
85
+ secondsLeft + ' ' + (secondsLeft >= 2 ? $this.secondsTextPlural : secondsLeft === 1 ? $this.secondsTextSingular : '');
86
+
87
+ let timeLeftText =
88
+ $this.countdownText +
89
+ ' <span class="ons-u-fw-b">' +
90
+ (minutesLeft > 0 ? minutesText : '') +
91
+ (minutesLeft > 0 && secondsLeft > 0 ? ' ' : '') +
92
+ (secondsLeft > 0 ? secondsText : '') +
93
+ '</span>.';
94
+
95
+ if (timerExpired) {
96
+ const shouldExpire = await $this.hasExpiryTimeResetInAnotherTab();
97
+
98
+ if (shouldExpire) {
99
+ $this.countdown.innerHTML = '<span class="ons-u-fw-b">' + $this.redirectingText + '</span>';
100
+ $this.accessibleCountdown.innerHTML = $this.redirectingText;
101
+ setTimeout($this.redirect.bind($this), 2000);
102
+ }
103
+ } else {
104
+ seconds--;
105
+ $this.expiryTimeInMilliseconds = seconds * 1000;
106
+ $this.countdown.innerHTML = timeLeftText;
107
+
108
+ if (minutesLeft < 1 && secondsLeft < 20) {
109
+ $this.accessibleCountdown.setAttribute('aria-live', 'assertive');
110
+ }
111
+
112
+ if (!$this.timerRunOnce) {
113
+ $this.accessibleCountdown.innerHTML = timeLeftText;
114
+ $this.timerRunOnce = true;
115
+ } else if (secondsLeft % 15 === 0) {
116
+ $this.accessibleCountdown.innerHTML = timeLeftText;
117
+ }
118
+
119
+ timers.push(setTimeout(runTimer.bind($this), 1000));
120
+ }
121
+ })();
122
+ }
123
+
124
+ async hasExpiryTimeResetInAnotherTab() {
125
+ const checkExpiryTime = await this.getExpiryTime();
126
+
127
+ if (checkExpiryTime != this.expiryTime) {
128
+ this.expiryTime = checkExpiryTime;
129
+ this.expiryTimeInMilliseconds = this.convertTimeToMilliSeconds(checkExpiryTime);
130
+ this.closeModalAndRestartTimeout(this.expiryTimeInMilliseconds);
131
+ } else {
132
+ return true;
133
+ }
134
+ }
135
+
136
+ closeModalAndRestartTimeout(timeInMilliSeconds) {
137
+ if (typeof timeInMilliSeconds !== 'string') {
138
+ timeInMilliSeconds = false;
139
+ }
140
+ if (this.modal.isDialogOpen()) {
141
+ this.modal.closeDialog();
142
+ }
143
+ this.restartTimeout(timeInMilliSeconds);
144
+ }
145
+
146
+ async restartTimeout(timeInMilliSeconds) {
147
+ this.clearTimers();
148
+ clearInterval(this.shouldModalCloseCheck);
149
+
150
+ if (timeInMilliSeconds) {
151
+ this.expiryTimeInMilliseconds = timeInMilliSeconds;
152
+ } else {
153
+ const createNewExpiryTime = await this.setNewExpiryTime();
154
+ this.expiryTime = createNewExpiryTime;
155
+ this.expiryTimeInMilliseconds = this.convertTimeToMilliSeconds(createNewExpiryTime);
156
+ }
157
+
158
+ this.startTimeout();
159
+ }
160
+
161
+ async handleWindowFocus() {
162
+ if (this.sessionExpiryEndpoint) {
163
+ const canSetNewExpiry = await this.setNewExpiryTime();
164
+ if (!canSetNewExpiry) {
165
+ this.redirect();
166
+ } else {
167
+ this.closeModalAndRestartTimeout();
168
+ }
169
+ }
170
+ }
171
+
172
+ escToClose(event) {
173
+ if (this.modal.isDialogOpen() && event.keyCode === 27) {
174
+ this.closeModalAndRestartTimeout();
175
+ }
176
+ }
177
+
178
+ async setNewExpiryTime() {
179
+ let newExpiryTime;
180
+ if (!this.sessionExpiryEndpoint) {
181
+ // For demo purposes
182
+ const demoTime = new Date(this.initialExpiryTime ? this.initialExpiryTime : Date.now() + 60 * 1000);
183
+ newExpiryTime = new Date(demoTime).toISOString();
184
+ } else {
185
+ newExpiryTime = await this.fetchExpiryTime('PATCH');
186
+ }
187
+ return newExpiryTime;
188
+ }
189
+
190
+ async getExpiryTime() {
191
+ if (this.sessionExpiryEndpoint) {
192
+ const currentExpiryTime = await this.fetchExpiryTime('GET');
193
+ return currentExpiryTime;
194
+ } else {
195
+ // For demo purposes
196
+ return this.expiryTime;
197
+ }
198
+ }
199
+
200
+ async fetchExpiryTime(fetchMethod) {
201
+ let response = await fetch(this.sessionExpiryEndpoint, {
202
+ method: fetchMethod,
203
+ headers: { 'Cache-Control': 'no-cache', 'Content-type': 'application/json; charset=UTF-8' },
204
+ });
205
+ if (!response.ok) {
206
+ this.redirect();
207
+ return false;
208
+ }
209
+
210
+ let json = await response.json();
211
+
212
+ return json.expires_at;
213
+ }
214
+
215
+ convertTimeToMilliSeconds(expiryTime) {
216
+ const time = new Date(expiryTime);
217
+ const calculateTimeInMilliSeconds = Math.abs(time - new Date());
218
+
219
+ return calculateTimeInMilliSeconds;
220
+ }
221
+
222
+ redirect() {
223
+ window.location.replace(this.timeOutRedirectUrl);
224
+ }
225
+
226
+ clearTimers() {
227
+ for (let i = 0; i < this.timers.length; i++) {
228
+ clearTimeout(this.timers[i]);
229
+ }
230
+ }
231
+
232
+ addThrottledEvents() {
233
+ window.onclick = this.throttle(() => {
234
+ /* istanbul ignore next */
235
+ if (!this.modal.isDialogOpen()) {
236
+ this.restartTimeout();
237
+ }
238
+ }, 61000);
239
+
240
+ window.onmousemove = this.throttle(() => {
241
+ /* istanbul ignore next */
242
+ if (!this.modal.isDialogOpen()) {
243
+ this.restartTimeout();
244
+ }
245
+ }, 61000);
246
+
247
+ window.onmousedown = this.throttle(() => {
248
+ /* istanbul ignore next */
249
+ if (!this.modal.isDialogOpen()) {
250
+ this.restartTimeout();
251
+ }
252
+ }, 61000);
253
+
254
+ window.onscroll = this.throttle(() => {
255
+ /* istanbul ignore next */
256
+ if (!this.modal.isDialogOpen()) {
257
+ this.restartTimeout();
258
+ }
259
+ }, 61000);
260
+
261
+ window.onkeypress = this.throttle(() => {
262
+ /* istanbul ignore next */
263
+ if (!this.modal.isDialogOpen()) {
264
+ this.restartTimeout();
265
+ }
266
+ }, 61000);
267
+
268
+ window.onkeyup = this.throttle(() => {
269
+ /* istanbul ignore next */
270
+ if (!this.modal.isDialogOpen()) {
271
+ this.restartTimeout();
272
+ }
273
+ }, 61000);
274
+ }
275
+
276
+ throttle(func, wait) {
277
+ let waiting = false;
278
+ return function() {
279
+ if (waiting) {
280
+ return;
281
+ }
282
+ waiting = true;
283
+ setTimeout(async () => {
284
+ func.apply(this, arguments);
285
+ waiting = false;
286
+ }, wait);
287
+ };
288
+ }
289
+ }