@stackoverflow/stacks 1.3.0 → 1.3.1

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 (36) hide show
  1. package/dist/controllers/s-expandable-control.d.ts +1 -1
  2. package/dist/controllers/s-tooltip.d.ts +16 -1
  3. package/dist/css/stacks.css +715 -527
  4. package/dist/css/stacks.min.css +1 -1
  5. package/dist/js/stacks.js +153 -84
  6. package/dist/js/stacks.min.js +1 -1
  7. package/lib/css/atomic/misc.less +1 -1
  8. package/lib/css/atomic/typography.less +0 -6
  9. package/lib/css/components/activity-indicator.less +18 -17
  10. package/lib/css/components/avatars.less +50 -131
  11. package/lib/css/components/breadcrumbs.less +4 -4
  12. package/lib/css/components/buttons.less +8 -48
  13. package/lib/css/components/empty-states.less +15 -0
  14. package/lib/css/components/{collapsible.less → expandable.less} +0 -0
  15. package/lib/css/components/inputs.less +37 -101
  16. package/lib/css/components/labels.less +98 -0
  17. package/lib/css/components/notices.less +190 -163
  18. package/lib/css/components/post-summary.less +4 -4
  19. package/lib/css/components/progress-bars.less +1 -1
  20. package/lib/css/components/prose.less +4 -4
  21. package/lib/css/components/spinner.less +39 -1
  22. package/lib/css/components/tables.less +1 -5
  23. package/lib/css/components/uploader.less +70 -84
  24. package/lib/css/exports/constants-colors.less +14 -0
  25. package/lib/css/stacks-dynamic.less +0 -1
  26. package/lib/css/stacks-static.less +3 -2
  27. package/lib/ts/controllers/s-expandable-control.ts +23 -19
  28. package/lib/ts/controllers/s-modal.ts +16 -16
  29. package/lib/ts/controllers/s-navigation-tablist.ts +13 -13
  30. package/lib/ts/controllers/s-popover.ts +26 -18
  31. package/lib/ts/controllers/s-table.ts +31 -29
  32. package/lib/ts/controllers/s-tooltip.ts +62 -23
  33. package/lib/ts/stacks.ts +8 -4
  34. package/package.json +17 -17
  35. package/lib/css/components/banners.less +0 -80
  36. package/lib/css/components/blank-states.less +0 -26
@@ -7,106 +7,91 @@
7
7
  // visit https://stackoverflow.design/
8
8
 
9
9
  .s-uploader {
10
- align-items: center;
11
- background-color: var(--black-025);
12
- border-radius: var(--br-lg);
13
- display: flex;
14
- min-height: var(--su-static128);
15
- justify-content: center;
16
- padding: var(--su8) var(--su16);
17
- position: relative;
18
- text-align: center;
19
-
20
- // Add the dashed border as an SVG background mask
21
- &:before {
22
- --s-uploader-background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='5' ry='5' stroke='%23000000' stroke-width='8' stroke-dasharray='7%2c 22' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
23
- content: '';
24
- display: block;
25
- position: absolute;
26
- top: 0;
27
- left: 0;
28
- right: 0;
29
- bottom: 0;
30
- background-color: var(--black-150);
31
- -webkit-mask-image: var(--s-uploader-background-image);
32
- mask-image: var(--s-uploader-background-image);
33
- border-radius: var(--br-lg);
10
+ --_bg: var(--black-025);
11
+ --_bg-focus: var(--black-050);
12
+ --_bg-bc: var(--black-150);
13
+ --_focus-ring-color: var(--focus-ring);
34
14
 
35
- .highcontrast-mode({
36
- background-color: var(--black-400);
37
- });
38
- }
15
+ // Static custom properties (not redefined but repeated enough to warrant a custom property)
16
+ --_bg-b-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='5' ry='5' stroke='%23000000' stroke-width='8' stroke-dasharray='7%2c 22' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
39
17
 
40
- &.has-error {
41
- background-color: var(--red-050);
18
+ .highcontrast-mode({
19
+ --_bg-bc-hc: var(--black-400);
20
+ });
42
21
 
43
- &:before {
44
- background-color: var(--red-400);
22
+ &.is-active {
23
+ --_bg: var(--black-050);
24
+ --_bg-bc: var(--black-200);
25
+ }
45
26
 
46
- .highcontrast-mode({
47
- background-color: var(--red-400);
48
- });
49
- }
27
+ &.is-disabled {
28
+ opacity: var(--_o-disabled-static);
29
+ }
50
30
 
51
- .s-link {
52
- color: var(--red-900);
53
- }
31
+ &.has-error {
32
+ --_bg: var(--red-050);
33
+ --_bg-focus: var(--red-100);
34
+ --_bg-bc: var(--red-400);
35
+ --_bg-bc-hc-state: var(--red-400);
36
+ --_focus-ring-color: var(--focus-ring-error);
37
+ --_link-fc: var(--red-900);
54
38
  }
55
39
 
56
40
  &.has-success {
57
- background-color: var(--green-025);
58
-
59
- &:before {
60
- background-color: var(--green-400);
61
-
62
- .highcontrast-mode({
63
- background-color: var(--green-400);
64
- });
65
- }
66
-
67
- .s-link {
68
- color: var(--green-900);
69
- }
41
+ --_bg: var(--green-025);
42
+ --_bg-focus: var(--green-050);
43
+ --_bg-bc: var(--green-400);
44
+ --_bg-bc-hc-state: var(--green-400);
45
+ --_focus-ring-color: var(--focus-ring-success);
46
+ --_link-fc: var(--green-900);
70
47
  }
71
48
 
72
49
  &.has-warning {
73
- background-color: var(--yellow-050);
74
-
75
- &:before {
76
- background-color: var(--yellow-400);
77
-
78
- .highcontrast-mode({
79
- background-color: var(--yellow-400);
80
- });
81
- }
82
-
83
- .s-link {
84
- color: var(--yellow-900);
85
- }
50
+ --_bg: var(--yellow-050);
51
+ --_bg-focus: var(--yellow-100);
52
+ --_bg-bc: var(--yellow-400);
53
+ --_bg-bc-hc-state: var(--yellow-400);
54
+ --_focus-ring-color: var(--focus-ring-warning);
55
+ --_link-fc: var(--yellow-900);
86
56
  }
87
57
 
88
- &.is-active {
89
- background-color: var(--black-050);
90
-
91
- &:before {
92
- background-color: var(--black-200);
93
-
94
- .highcontrast-mode({
95
- background-color: var(--black);
96
- });
58
+ &.has-error,
59
+ &.has-success,
60
+ &.has-warning {
61
+ .s-link {
62
+ color: var(--_link-fc);
97
63
  }
98
64
  }
99
65
 
100
- &.is-disabled {
101
- opacity: 0.5;
102
- }
103
-
104
66
  // This is to for safari shadow DOM
105
67
  // see https://github.com/StackExchange/Stacks/pull/690#issuecomment-861028193
106
68
  input[type="file"]::file-selector-button {
107
69
  cursor: pointer;
108
70
  }
109
-
71
+ .s-uploader--container {
72
+ align-items: center;
73
+ background-color: var(--_bg);
74
+ border-radius: var(--br-lg);
75
+ display: flex;
76
+ flex-direction: column;
77
+ justify-content: center;
78
+ min-height: var(--su-static128);
79
+ padding: var(--su8) var(--su16);
80
+ position: relative;
81
+ text-align: center;
82
+
83
+ // Add the dashed border as an SVG background mask
84
+ &:before {
85
+ -webkit-mask-image: var(--_bg-b-image);
86
+ mask-image: var(--_bg-b-image);
87
+ background-color: var(--_bg-bc-hc-state, var(--_bg-bc-hc, var(--_bg-bc)));
88
+ content: '';
89
+ border-radius: var(--br-lg);
90
+ display: block;
91
+ position: absolute;
92
+ inset: 0;
93
+ }
94
+ }
110
95
  .s-uploader--input {
111
96
  cursor: pointer;
112
97
  height: 100%;
@@ -114,13 +99,13 @@
114
99
  opacity: 0;
115
100
  position: absolute;
116
101
  width: 100%;
102
+ z-index: var(--zi-selected);
117
103
 
118
104
  &:focus:focus-visible + .s-uploader--container {
119
- background-color: var(--black-050);
120
- box-shadow: 0 0 0 var(--su-static4) var(--focus-ring);
105
+ background-color: var(--_bg-focus);
106
+ box-shadow: 0 0 0 var(--su-static4) var(--_focus-ring-color);
121
107
  }
122
108
  }
123
-
124
109
  .s-uploader--preview {
125
110
  max-width: 100%;
126
111
  pointer-events: none;
@@ -185,8 +170,7 @@
185
170
  object-fit: cover;
186
171
  }
187
172
  &:not(img) {
188
- --s-uploader--preview-document-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' fill='%23535A60' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath d='M3 3a2 2 0 012-2h6l4 4v10a2 2 0 01-2 2H5a2 2 0 01-2-2V3zm7-1.5V6h4.5L10 1.5z'%3E%3C/path%3E%3C/svg%3E");
189
- background-image: var(--s-uploader--preview-document-icon);
173
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' fill='%23535A60' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath d='M3 3a2 2 0 012-2h6l4 4v10a2 2 0 01-2 2H5a2 2 0 01-2-2V3zm7-1.5V6h4.5L10 1.5z'%3E%3C/path%3E%3C/svg%3E");
190
174
  background-position: center;
191
175
  background-repeat: no-repeat;
192
176
  }
@@ -207,4 +191,6 @@
207
191
  top: var(--su8);
208
192
  z-index: var(--zi-active);
209
193
  }
194
+
195
+ position: relative;
210
196
  }
@@ -409,6 +409,10 @@
409
409
  --focus-ring-error: hsla(@red-h, 62%, 47%, 15%);
410
410
  --focus-ring-muted: hsla(@black-h, @black-s, 15%, 10%);
411
411
 
412
+ // Opacity
413
+ --_o-disabled: 0.5;
414
+ --_o-disabled-static: 0.5;
415
+
412
416
  // Shadows
413
417
  --bs-sm: 0 1px 2px hsla(0, 0%, 0%, 0.05), 0 1px 4px hsla(0, 0%, 0%, 0.05), 0 2px 8px hsla(0, 0%, 0%, 0.05);
414
418
  --bs-md: 0 1px 3px hsla(0, 0%, 0%, 0.06), 0 2px 6px hsla(0, 0%, 0%, 0.06), 0 3px 8px hsla(0, 0%, 0%, 0.09);
@@ -605,6 +609,10 @@
605
609
  --focus-ring-error: hsla(@red-h, 62%, 52%, 30%);
606
610
  --focus-ring-muted: hsla(@black-h, @black-s, 100%, 10%);
607
611
 
612
+ // Opacity
613
+ --_o-disabled: 0.5;
614
+ --_o-disabled-static: 0.5;
615
+
608
616
  // Shadows
609
617
  --bs-sm: 0 1px 2px hsla(0, 0%, 0%, 0.1), 0 1px 4px hsla(0, 0%, 0%, 0.1), 0 2px 8px hsla(0, 0%, 0%, 0.1);
610
618
  --bs-md: 0 1px 3px hsla(0, 0%, 0%, 0.11), 0 2px 6px hsla(0, 0%, 0%, 0.11), 0 3px 8px hsla(0, 0%, 0%, 0.14);
@@ -816,6 +824,9 @@
816
824
  --focus-ring-error: fade(@red-600, 90%);
817
825
  --focus-ring-muted: fade(@black-400, 95%);
818
826
 
827
+ // Opacity
828
+ --_o-disabled: 0.8;
829
+
819
830
  // Shadows
820
831
  --bs-sm: none;
821
832
  --bs-md: none;
@@ -972,6 +983,9 @@
972
983
  --fc-medium: var(--black-700);
973
984
  --fc-light: var(--black-500);
974
985
 
986
+ // Opacity
987
+ --_o-disabled: 0.8;
988
+
975
989
  // Shadows
976
990
  --bs-sm: none;
977
991
  --bs-md: none;
@@ -18,7 +18,6 @@
18
18
  @import "base/body.less";
19
19
 
20
20
  // -- COMPONENTS
21
- @import "components/banners.less";
22
21
  @import "components/buttons.less";
23
22
  @import "components/links.less";
24
23
  @import "components/link-previews.less";
@@ -20,13 +20,14 @@
20
20
  @import "components/activity-indicator.less";
21
21
  @import "components/avatars.less";
22
22
  @import "components/badges.less";
23
- @import "components/blank-states.less";
23
+ @import "components/empty-states.less";
24
24
  @import "components/breadcrumbs.less";
25
25
  @import "components/button-groups.less";
26
26
  @import "components/cards.less";
27
27
  @import "components/code-blocks.less";
28
- @import "components/collapsible.less";
28
+ @import "components/expandable.less";
29
29
  @import "components/inputs.less";
30
+ @import "components/labels.less";
30
31
  @import "components/menu.less";
31
32
  @import "components/modals.less";
32
33
  @import "components/navigation.less";
@@ -1,7 +1,7 @@
1
1
  import * as Stacks from '../stacks';
2
2
 
3
3
  // Radio buttons only trigger a change event when they're *checked*, but not when
4
- // they're *unchecked*. Therefore, if we have an active `s-collapsible-control` in
4
+ // they're *unchecked*. Therefore, if we have an active `s-expandable-control` in
5
5
  // the document, we listen for change events on *all* radio buttons and find any
6
6
  // other radio buttons in the same `name` group, triggering a custom event on all
7
7
  // of them so the controller can re-evaluate.
@@ -20,7 +20,7 @@ function globalChangeListener(e: UIEvent) {
20
20
  if (other === e.target) {
21
21
  return;
22
22
  }
23
- var customEvent;
23
+ let customEvent;
24
24
  try {
25
25
  customEvent = new Event(RADIO_OFF_EVENT);
26
26
  } catch (ex) {
@@ -32,7 +32,7 @@ function globalChangeListener(e: UIEvent) {
32
32
  });
33
33
  }
34
34
 
35
- var refCount = 0;
35
+ let refCount = 0;
36
36
  function globalChangeListenerRequired(required: boolean) {
37
37
  if (required) {
38
38
  refCount++;
@@ -52,16 +52,16 @@ export class ExpandableController extends Stacks.StacksController {
52
52
  private events!: string[];
53
53
  private isCheckable!: boolean;
54
54
  private isRadio!: boolean;
55
- private lastKeydownClickTimestamp: number = 0;
55
+ private lastKeydownClickTimestamp = 0;
56
56
 
57
57
  initialize() {
58
58
  if (this.element.nodeName === "INPUT" && ["radio", "checkbox"].indexOf((<HTMLInputElement>this.element).type) >= 0) {
59
- this.isCollapsed = this._isCollapsedForCheckable;
59
+ this.isCollapsed = this._isCollapsedForCheckable.bind(this);
60
60
  this.events = ["change", RADIO_OFF_EVENT];
61
61
  this.isCheckable = true;
62
62
  this.isRadio = (<HTMLInputElement>this.element).type === "radio";
63
63
  } else {
64
- this.isCollapsed = this._isCollapsedForClickable;
64
+ this.isCollapsed = this._isCollapsedForClickable.bind(this);
65
65
  this.events = ["click", "keydown"];
66
66
  }
67
67
  this.listener = this.listener.bind(this);
@@ -71,7 +71,7 @@ export class ExpandableController extends Stacks.StacksController {
71
71
  // for non-checkable elements, the initial source of truth is the collapsed/expanded
72
72
  // state of the controlled element (unless the element doesn't exist)
73
73
  _isCollapsedForClickable() {
74
- var cc = this.controlledCollapsibles;
74
+ const cc = this.controlledExpandables;
75
75
  // the element is considered collapsed if *any* target element is collapsed
76
76
  return cc.length > 0 ? !cc.every(element => element.classList.contains("is-expanded")) : this.element.getAttribute("aria-expanded") === "false";
77
77
  };
@@ -82,7 +82,7 @@ export class ExpandableController extends Stacks.StacksController {
82
82
  };
83
83
 
84
84
 
85
- get controlledCollapsibles() {
85
+ get controlledExpandables() {
86
86
  const attr = this.element.getAttribute("aria-controls");
87
87
  if (!attr) {
88
88
  throw `[aria-controls="targetId1 ... targetIdN"] attribute required`;
@@ -104,8 +104,8 @@ export class ExpandableController extends Stacks.StacksController {
104
104
  if (!this.data.has("toggle-class")) {
105
105
  return;
106
106
  }
107
- var cl = this.element.classList;
108
- var toggleClass = this.data.get("toggle-class");
107
+ const cl = this.element.classList;
108
+ const toggleClass = this.data.get("toggle-class");
109
109
  if (!toggleClass) {
110
110
  throw "couldn't find toggle class"
111
111
  }
@@ -115,7 +115,7 @@ export class ExpandableController extends Stacks.StacksController {
115
115
  };
116
116
 
117
117
  listener(e: Event) {
118
- var newCollapsed;
118
+ let newCollapsed;
119
119
  if (this.isCheckable) {
120
120
  newCollapsed = !(<HTMLInputElement>this.element).checked;
121
121
  } else {
@@ -142,7 +142,7 @@ export class ExpandableController extends Stacks.StacksController {
142
142
  }
143
143
  }
144
144
  this.element.setAttribute("aria-expanded", newCollapsed ? "false" : "true");
145
- for (let controlledElement of this.controlledCollapsibles) {
145
+ for (const controlledElement of this.controlledExpandables) {
146
146
  controlledElement.classList.toggle("is-expanded", !newCollapsed);
147
147
  }
148
148
  this._dispatchShowHideEvent(!newCollapsed);
@@ -151,7 +151,7 @@ export class ExpandableController extends Stacks.StacksController {
151
151
 
152
152
  connect() {
153
153
  this.events.forEach(e => {
154
- this.element.addEventListener(e, this.listener);
154
+ this.element.addEventListener(e, this.listener.bind(this));
155
155
  }, this);
156
156
 
157
157
  if (this.isRadio) {
@@ -159,15 +159,19 @@ export class ExpandableController extends Stacks.StacksController {
159
159
  }
160
160
 
161
161
  // synchronize state -- in all cases, this means setting the correct `aria-expanded`
162
- // attribute; for checkable controls this also means setting the `is-collapsed` class
163
- this.element.setAttribute("aria-expanded", this.isCollapsed() ? "false" : "true");
162
+ // attribute; for checkable controls this also means setting the `is-collapsed` class.
163
+ // Note: aria-expanded is currently an invalid attribute on radio elements
164
+ // Support for aria-expanded is being debated by the W3C https://github.com/w3c/aria/issues/1404 as recently as June 2022
165
+ if (!this.isRadio) {
166
+ this.element.setAttribute("aria-expanded", this.isCollapsed() ? "false" : "true");
167
+ }
164
168
  if (this.isCheckable) {
165
- var cc = this.controlledCollapsibles;
169
+ const cc = this.controlledExpandables;
166
170
  if (cc.length) {
167
- var expected = !this.isCollapsed();
171
+ const expected = !this.isCollapsed();
168
172
  // if any element does not match the expected state, set them all to the expected state
169
173
  if (cc.some(element => element.classList.contains("is-expanded") !== expected)) {
170
- for (let controlledElement of this.controlledCollapsibles) {
174
+ for (const controlledElement of this.controlledExpandables) {
171
175
  controlledElement.classList.toggle("is-expanded", expected);
172
176
  }
173
177
  this._dispatchShowHideEvent(expected);
@@ -179,7 +183,7 @@ export class ExpandableController extends Stacks.StacksController {
179
183
 
180
184
  disconnect() {
181
185
  this.events.forEach(e => {
182
- this.element.removeEventListener(e, this.listener);
186
+ this.element.removeEventListener(e, this.listener.bind(this));
183
187
  }, this);
184
188
 
185
189
  if (this.isRadio) {
@@ -6,12 +6,12 @@ export class ModalController extends Stacks.StacksController {
6
6
  private modalTarget!: HTMLElement;
7
7
  private initialFocusTargets!: HTMLElement[];
8
8
 
9
- private _boundClickFn!: any;
10
- private _boundKeypressFn!: any;
9
+ private _boundClickFn!: (event: MouseEvent) => void;
10
+ private _boundKeypressFn!: (event: KeyboardEvent) => void;
11
11
 
12
12
  private returnElement!: HTMLElement;
13
13
 
14
- private _boundTabTrap!: any;
14
+ private _boundTabTrap!: (event: KeyboardEvent) => void;
15
15
 
16
16
  connect () {
17
17
  this.validate();
@@ -50,7 +50,7 @@ export class ModalController extends Stacks.StacksController {
50
50
  */
51
51
  private validate() {
52
52
  // check for returnElement support
53
- var returnElementSelector = this.data.get("return-element");
53
+ const returnElementSelector = this.data.get("return-element");
54
54
  if (returnElementSelector) {
55
55
  this.returnElement = <HTMLElement>document.querySelector(returnElementSelector);
56
56
 
@@ -65,8 +65,8 @@ export class ModalController extends Stacks.StacksController {
65
65
  * @param show Optional parameter that force shows/hides the element or toggles it if left undefined
66
66
  */
67
67
  private _toggle (show?: boolean | undefined, dispatcher: Event|Element|null = null) {
68
- var toShow = show;
69
- var isVisible = this.modalTarget.getAttribute("aria-hidden") === "false";
68
+ let toShow = show;
69
+ const isVisible = this.modalTarget.getAttribute("aria-hidden") === "false";
70
70
 
71
71
  // if we're letting the class toggle, we need to figure out if the popover is visible manually
72
72
  if (typeof toShow === "undefined") {
@@ -78,10 +78,10 @@ export class ModalController extends Stacks.StacksController {
78
78
  return;
79
79
  }
80
80
 
81
- let dispatchingElement = this.getDispatcher(dispatcher);
81
+ const dispatchingElement = this.getDispatcher(dispatcher);
82
82
 
83
83
  // show/hide events trigger before toggling the class
84
- var triggeredEvent = this.triggerEvent(toShow ? "show" : "hide", {
84
+ const triggeredEvent = this.triggerEvent(toShow ? "show" : "hide", {
85
85
  returnElement: this.returnElement,
86
86
  dispatcher: this.getDispatcher(dispatchingElement)
87
87
  }, this.modalTarget);
@@ -105,7 +105,7 @@ export class ModalController extends Stacks.StacksController {
105
105
  }
106
106
 
107
107
  // check for transitionend support
108
- var supportsTransitionEnd = (<HTMLElement>this.modalTarget).ontransitionend !== undefined;
108
+ const supportsTransitionEnd = (this.modalTarget).ontransitionend !== undefined;
109
109
 
110
110
  // shown/hidden events trigger after toggling the class
111
111
  if (supportsTransitionEnd) {
@@ -182,7 +182,7 @@ export class ModalController extends Stacks.StacksController {
182
182
  */
183
183
  private focusInsideModal() {
184
184
  this.modalTarget.addEventListener("s-modal:shown", () => {
185
- var initialFocus = this.firstVisible(this.initialFocusTargets) ?? this.firstVisible(this.getAllTabbables());
185
+ const initialFocus = this.firstVisible(this.initialFocusTargets) ?? this.firstVisible(this.getAllTabbables());
186
186
  initialFocus?.focus();
187
187
  }, {once: true });
188
188
  }
@@ -194,7 +194,7 @@ export class ModalController extends Stacks.StacksController {
194
194
 
195
195
  // If somehow the user has tabbed out of the modal or if focus started outside the modal, push them to the first item.
196
196
  if (!this.modalTarget.contains(<Element>e.target)) {
197
- var focusTarget = this.firstVisible(this.getAllTabbables());
197
+ const focusTarget = this.firstVisible(this.getAllTabbables());
198
198
  if (focusTarget) {
199
199
  e.preventDefault();
200
200
  focusTarget.focus();
@@ -205,10 +205,10 @@ export class ModalController extends Stacks.StacksController {
205
205
 
206
206
  // If we observe a tab keydown and we're on an edge, cycle the focus to the other side.
207
207
  if (e.key === "Tab") {
208
- var tabbables = this.getAllTabbables();
208
+ const tabbables = this.getAllTabbables();
209
209
 
210
- var firstTabbable = this.firstVisible(tabbables);
211
- var lastTabbable = this.lastVisible(tabbables);
210
+ const firstTabbable = this.firstVisible(tabbables);
211
+ const lastTabbable = this.lastVisible(tabbables);
212
212
 
213
213
  if (firstTabbable && lastTabbable) {
214
214
  if (firstTabbable === lastTabbable) {
@@ -252,7 +252,7 @@ export class ModalController extends Stacks.StacksController {
252
252
  * Forces the popover to hide if a user clicks outside of it or its reference element
253
253
  */
254
254
  private hideOnOutsideClick (e: Event) {
255
- var target = <Node>e.target;
255
+ const target = <Node>e.target;
256
256
  // check if the document was clicked inside either the toggle element or the modal itself
257
257
  // note: .contains also returns true if the node itself matches the target element
258
258
  if (!this.modalTarget.querySelector(".s-modal--dialog")!.contains(target) && document.body.contains(target)) {
@@ -311,7 +311,7 @@ export function hideModal(element: HTMLElement) {
311
311
  * @param show whether to force show/hide the modal; toggles the modal if left undefined
312
312
  */
313
313
  function toggleModal(element: HTMLElement, show?: boolean | undefined) {
314
- var controller = Stacks.application.getControllerForElementAndIdentifier(element, "s-modal") as ModalController;
314
+ const controller = Stacks.application.getControllerForElementAndIdentifier(element, "s-modal") as ModalController;
315
315
 
316
316
  if (!controller) {
317
317
  throw "Unable to get s-modal controller from element";
@@ -2,8 +2,8 @@ import * as Stacks from "../stacks";
2
2
 
3
3
  export class TabListController extends Stacks.StacksController {
4
4
 
5
- private boundSelectTab: any; // (event: MouseEvent) => void;
6
- private boundHandleKeydown: any // (event: KeyboardEvent) => void;
5
+ private boundSelectTab!: (event: MouseEvent) => void;
6
+ private boundHandleKeydown!: (event: KeyboardEvent) => void;
7
7
 
8
8
  connect() {
9
9
  super.connect();
@@ -11,7 +11,7 @@ export class TabListController extends Stacks.StacksController {
11
11
  this.boundSelectTab = this.selectTab.bind(this);
12
12
  this.boundHandleKeydown = this.handleKeydown.bind(this);
13
13
 
14
- for (let tab of this.tabTargets) {
14
+ for (const tab of this.tabTargets) {
15
15
  tab.addEventListener("click", this.boundSelectTab);
16
16
  tab.addEventListener("keydown", this.boundHandleKeydown);
17
17
  }
@@ -20,7 +20,7 @@ export class TabListController extends Stacks.StacksController {
20
20
  disconnect() {
21
21
  super.disconnect();
22
22
 
23
- for (let tab of this.tabTargets) {
23
+ for (const tab of this.tabTargets) {
24
24
  tab.removeEventListener("click", this.boundSelectTab);
25
25
  tab.removeEventListener("keydown", this.boundHandleKeydown);
26
26
  }
@@ -29,8 +29,8 @@ export class TabListController extends Stacks.StacksController {
29
29
  /**
30
30
  * Gets all tabs within the controller.
31
31
  */
32
- get tabTargets() {
33
- return <HTMLElement[]>Array.from(this.element.querySelectorAll("[role=tab]"));
32
+ get tabTargets(): HTMLElement[] {
33
+ return Array.from(this.element.querySelectorAll("[role=tab]"));
34
34
  }
35
35
 
36
36
  /**
@@ -47,8 +47,8 @@ export class TabListController extends Stacks.StacksController {
47
47
  handleKeydown(event: KeyboardEvent) {
48
48
  let tabElement = <HTMLElement>event.currentTarget;
49
49
 
50
- var tabs = this.tabTargets;
51
- var tabIndex = tabs.indexOf(tabElement);
50
+ const tabs = this.tabTargets;
51
+ let tabIndex = tabs.indexOf(tabElement);
52
52
 
53
53
  if (event.key === "ArrowRight") {
54
54
  tabIndex++;
@@ -62,7 +62,7 @@ export class TabListController extends Stacks.StacksController {
62
62
  if (tabIndex < 0) { tabIndex = tabs.length - 1; }
63
63
  if (tabIndex >= tabs.length) { tabIndex = 0; }
64
64
 
65
- tabElement = <HTMLElement>tabs[tabIndex];
65
+ tabElement = tabs[tabIndex];
66
66
  this.switchToTab(tabElement);
67
67
 
68
68
  // Focus the newly selected tab so it can receive keyboard events.
@@ -75,7 +75,7 @@ export class TabListController extends Stacks.StacksController {
75
75
  */
76
76
  private switchToTab(newTab: HTMLElement) {
77
77
 
78
- var oldTab = this.selectedTab;
78
+ const oldTab = this.selectedTab;
79
79
  if (oldTab === newTab) { return; }
80
80
 
81
81
  if (this.triggerEvent("select", { oldTab, newTab }).defaultPrevented) { return; }
@@ -98,9 +98,9 @@ export class TabListController extends Stacks.StacksController {
98
98
  * is not a valid tab, all tabs will be unselected.
99
99
  */
100
100
  public set selectedTab(selectedTab: HTMLElement | null) {
101
- for (let tab of this.tabTargets) {
102
- let panelId = tab.getAttribute('aria-controls');
103
- let panel = panelId ? document.getElementById(panelId) : null;
101
+ for (const tab of this.tabTargets) {
102
+ const panelId = tab.getAttribute('aria-controls');
103
+ const panel = panelId ? document.getElementById(panelId) : null;
104
104
 
105
105
  if (tab === selectedTab) {
106
106
  tab.classList.add('is-selected');