@stackoverflow/stacks 1.6.2 → 1.6.4

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.
@@ -1,8 +1,12 @@
1
- import { createPopper, Placement } from '@popperjs/core';
2
- import type * as Popper from '@popperjs/core';
1
+ import { createPopper, Placement } from "@popperjs/core";
2
+ import type * as Popper from "@popperjs/core";
3
3
  import * as Stacks from "../stacks";
4
4
 
5
- type OutsideClickBehavior = "always" | "never" | "if-in-viewport" | "after-dismissal";
5
+ type OutsideClickBehavior =
6
+ | "always"
7
+ | "never"
8
+ | "if-in-viewport"
9
+ | "after-dismissal";
6
10
 
7
11
  export abstract class BasePopoverController extends Stacks.StacksController {
8
12
  private popper!: Popper.Instance;
@@ -31,7 +35,9 @@ export abstract class BasePopoverController extends Stacks.StacksController {
31
35
  */
32
36
  get isVisible() {
33
37
  const popoverElement = this.popoverElement;
34
- return popoverElement ? popoverElement.classList.contains("is-visible") : false;
38
+ return popoverElement
39
+ ? popoverElement.classList.contains("is-visible")
40
+ : false;
35
41
  }
36
42
 
37
43
  /**
@@ -39,28 +45,43 @@ export abstract class BasePopoverController extends Stacks.StacksController {
39
45
  */
40
46
  get isInViewport() {
41
47
  const element = this.popoverElement;
42
- if (!this.isVisible || !element) { return false; }
48
+ if (!this.isVisible || !element) {
49
+ return false;
50
+ }
43
51
 
44
52
  // From https://stackoverflow.com/a/5354536. Theoretically, this could be calculated using Popper's detectOverflow function,
45
53
  // but it's unclear how to access that with our current configuration.
46
54
 
47
55
  const rect = element.getBoundingClientRect();
48
- const viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
49
- const viewWidth = Math.max(document.documentElement.clientWidth, window.innerWidth);
50
-
51
- return rect.bottom > 0 && rect.top < viewHeight && rect.right > 0 && rect.left < viewWidth;
56
+ const viewHeight = Math.max(
57
+ document.documentElement.clientHeight,
58
+ window.innerHeight
59
+ );
60
+ const viewWidth = Math.max(
61
+ document.documentElement.clientWidth,
62
+ window.innerWidth
63
+ );
64
+
65
+ return (
66
+ rect.bottom > 0 &&
67
+ rect.top < viewHeight &&
68
+ rect.right > 0 &&
69
+ rect.left < viewWidth
70
+ );
52
71
  }
53
72
 
54
73
  protected get shouldHideOnOutsideClick() {
55
- const hideBehavior = <OutsideClickBehavior>this.data.get("hide-on-outside-click");
74
+ const hideBehavior = <OutsideClickBehavior>(
75
+ this.data.get("hide-on-outside-click")
76
+ );
56
77
  switch (hideBehavior) {
57
78
  case "after-dismissal":
58
79
  case "never":
59
80
  return false;
60
81
  case "if-in-viewport":
61
- return this.isInViewport;
82
+ return this.isInViewport;
62
83
  default:
63
- return true;
84
+ return true;
64
85
  }
65
86
  }
66
87
 
@@ -97,21 +118,27 @@ export abstract class BasePopoverController extends Stacks.StacksController {
97
118
  /**
98
119
  * Toggles the visibility of the popover
99
120
  */
100
- toggle(dispatcher: Event|Element|null = null) {
121
+ toggle(dispatcher: Event | Element | null = null) {
101
122
  this.isVisible ? this.hide(dispatcher) : this.show(dispatcher);
102
123
  }
103
124
 
104
125
  /**
105
126
  * Shows the popover if not already visible
106
127
  */
107
- show(dispatcher: Event|Element|null = null) {
108
- if (this.isVisible) { return; }
128
+ show(dispatcher: Event | Element | null = null) {
129
+ if (this.isVisible) {
130
+ return;
131
+ }
109
132
 
110
133
  const dispatcherElement = this.getDispatcher(dispatcher);
111
134
 
112
- if (this.triggerEvent("show", {
113
- dispatcher: dispatcherElement
114
- }).defaultPrevented) { return; }
135
+ if (
136
+ this.triggerEvent("show", {
137
+ dispatcher: dispatcherElement,
138
+ }).defaultPrevented
139
+ ) {
140
+ return;
141
+ }
115
142
 
116
143
  if (!this.popper) {
117
144
  this.initializePopper();
@@ -128,14 +155,20 @@ export abstract class BasePopoverController extends Stacks.StacksController {
128
155
  /**
129
156
  * Hides the popover if not already hidden
130
157
  */
131
- hide(dispatcher: Event|Element|null = null) {
132
- if (!this.isVisible) { return; }
158
+ hide(dispatcher: Event | Element | null = null) {
159
+ if (!this.isVisible) {
160
+ return;
161
+ }
133
162
 
134
163
  const dispatcherElement = this.getDispatcher(dispatcher);
135
164
 
136
- if (this.triggerEvent("hide", {
137
- dispatcher: dispatcherElement
138
- }).defaultPrevented) { return; }
165
+ if (
166
+ this.triggerEvent("hide", {
167
+ dispatcher: dispatcherElement,
168
+ }).defaultPrevented
169
+ ) {
170
+ return;
171
+ }
139
172
 
140
173
  this.popoverElement.classList.remove("is-visible");
141
174
 
@@ -148,7 +181,10 @@ export abstract class BasePopoverController extends Stacks.StacksController {
148
181
  }
149
182
 
150
183
  // on first interaction, hide-on-outside-click with value "after-dismissal" reverts to the default behavior
151
- if (<OutsideClickBehavior>this.data.get("hide-on-outside-click") === "after-dismissal") {
184
+ if (
185
+ <OutsideClickBehavior>this.data.get("hide-on-outside-click") ===
186
+ "after-dismissal"
187
+ ) {
152
188
  this.data.delete("hide-on-outside-click");
153
189
  }
154
190
 
@@ -158,20 +194,20 @@ export abstract class BasePopoverController extends Stacks.StacksController {
158
194
  /**
159
195
  * Binds document events for this popover and fires the shown event
160
196
  */
161
- protected shown(dispatcher: Element|null = null) {
197
+ protected shown(dispatcher: Element | null = null) {
162
198
  this.bindDocumentEvents();
163
199
  this.triggerEvent("shown", {
164
- dispatcher: dispatcher
200
+ dispatcher: dispatcher,
165
201
  });
166
202
  }
167
203
 
168
204
  /**
169
205
  * Unbinds document events for this popover and fires the hidden event
170
206
  */
171
- protected hidden(dispatcher: Element|null = null) {
207
+ protected hidden(dispatcher: Element | null = null) {
172
208
  this.unbindDocumentEvents();
173
209
  this.triggerEvent("hidden", {
174
- dispatcher: dispatcher
210
+ dispatcher: dispatcher,
175
211
  });
176
212
  }
177
213
 
@@ -187,21 +223,21 @@ export abstract class BasePopoverController extends Stacks.StacksController {
187
223
  */
188
224
  private initializePopper() {
189
225
  this.popper = createPopper(this.referenceElement, this.popoverElement, {
190
- placement: this.data.get("placement") as Placement || "bottom",
226
+ placement: (this.data.get("placement") as Placement) || "bottom",
191
227
  modifiers: [
192
228
  {
193
229
  name: "offset",
194
230
  options: {
195
231
  offset: [0, 10], // The entire popover should be 10px away from the element
196
- }
232
+ },
197
233
  },
198
234
  {
199
235
  name: "arrow",
200
236
  options: {
201
- element: ".s-popover--arrow"
237
+ element: ".s-popover--arrow",
202
238
  },
203
239
  },
204
- ]
240
+ ],
205
241
  });
206
242
  }
207
243
 
@@ -215,14 +251,21 @@ export abstract class BasePopoverController extends Stacks.StacksController {
215
251
 
216
252
  // if there is an alternative reference selector and that element exists, use it (and throw if it isn't found)
217
253
  if (referenceSelector) {
218
- this.referenceElement = <HTMLElement>this.element.querySelector(referenceSelector);
254
+ this.referenceElement = <HTMLElement>(
255
+ this.element.querySelector(referenceSelector)
256
+ );
219
257
 
220
258
  if (!this.referenceElement) {
221
- throw "Unable to find element by reference selector: " + referenceSelector;
259
+ throw (
260
+ "Unable to find element by reference selector: " +
261
+ referenceSelector
262
+ );
222
263
  }
223
264
  }
224
265
 
225
- const popoverId = this.referenceElement.getAttribute(this.popoverSelectorAttribute);
266
+ const popoverId = this.referenceElement.getAttribute(
267
+ this.popoverSelectorAttribute
268
+ );
226
269
 
227
270
  let popoverElement: HTMLElement | null = null;
228
271
 
@@ -230,7 +273,7 @@ export abstract class BasePopoverController extends Stacks.StacksController {
230
273
  if (popoverId) {
231
274
  popoverElement = document.getElementById(popoverId);
232
275
 
233
- if (!popoverElement){
276
+ if (!popoverElement) {
234
277
  throw `[${this.popoverSelectorAttribute}="{POPOVER_ID}"] required`;
235
278
  }
236
279
  }
@@ -250,14 +293,14 @@ export abstract class BasePopoverController extends Stacks.StacksController {
250
293
  * Determines the correct dispatching element from a potential input
251
294
  * @param dispatcher The event or element to get the dispatcher from
252
295
  */
253
- protected getDispatcher(dispatcher: Event|Element|null = null) : Element {
296
+ protected getDispatcher(
297
+ dispatcher: Event | Element | null = null
298
+ ): Element {
254
299
  if (dispatcher instanceof Event) {
255
300
  return <Element>dispatcher.target;
256
- }
257
- else if (dispatcher instanceof Element) {
301
+ } else if (dispatcher instanceof Element) {
258
302
  return dispatcher;
259
- }
260
- else {
303
+ } else {
261
304
  return this.element;
262
305
  }
263
306
  }
@@ -278,12 +321,12 @@ export class PopoverController extends BasePopoverController {
278
321
  protected popoverSelectorAttribute = "aria-controls";
279
322
 
280
323
  private boundHideOnOutsideClick!: (event: MouseEvent) => void;
281
- private boundHideOnEscapePress!: (event: KeyboardEvent) => void;
324
+ private boundHideOnEscapePress!: (event: KeyboardEvent) => void;
282
325
 
283
326
  /**
284
327
  * Toggles optional classes and accessibility attributes in addition to BasePopoverController.shown
285
328
  */
286
- protected override shown(dispatcher: Element|null = null) {
329
+ protected override shown(dispatcher: Element | null = null) {
287
330
  this.toggleOptionalClasses(true);
288
331
  this.toggleAccessibilityAttributes(true);
289
332
  super.shown(dispatcher);
@@ -292,13 +335,12 @@ export class PopoverController extends BasePopoverController {
292
335
  /**
293
336
  * Toggles optional classes and accessibility attributes in addition to BasePopoverController.hidden
294
337
  */
295
- protected override hidden(dispatcher: Element|null = null) {
338
+ protected override hidden(dispatcher: Element | null = null) {
296
339
  this.toggleOptionalClasses(false);
297
340
  this.toggleAccessibilityAttributes(false);
298
341
  super.hidden(dispatcher);
299
342
  }
300
343
 
301
-
302
344
  /**
303
345
  * Initializes accessibility attributes in addition to BasePopoverController.connect
304
346
  */
@@ -312,8 +354,10 @@ export class PopoverController extends BasePopoverController {
312
354
  * Binds global events to the document for hiding popovers on user interaction
313
355
  */
314
356
  protected bindDocumentEvents() {
315
- this.boundHideOnOutsideClick = this.boundHideOnOutsideClick || this.hideOnOutsideClick.bind(this);
316
- this.boundHideOnEscapePress = this.boundHideOnEscapePress || this.hideOnEscapePress.bind(this);
357
+ this.boundHideOnOutsideClick =
358
+ this.boundHideOnOutsideClick || this.hideOnOutsideClick.bind(this);
359
+ this.boundHideOnEscapePress =
360
+ this.boundHideOnEscapePress || this.hideOnEscapePress.bind(this);
317
361
 
318
362
  document.addEventListener("mousedown", this.boundHideOnOutsideClick);
319
363
  document.addEventListener("keyup", this.boundHideOnEscapePress);
@@ -335,10 +379,15 @@ export class PopoverController extends BasePopoverController {
335
379
  const target = <Node>e.target;
336
380
  // check if the document was clicked inside either the reference element or the popover itself
337
381
  // note: .contains also returns true if the node itself matches the target element
338
- if (this.shouldHideOnOutsideClick && !this.referenceElement.contains(target) && !this.popoverElement.contains(target) && document.body.contains(target)) {
382
+ if (
383
+ this.shouldHideOnOutsideClick &&
384
+ !this.referenceElement.contains(target) &&
385
+ !this.popoverElement.contains(target) &&
386
+ document.body.contains(target)
387
+ ) {
339
388
  this.hide(e);
340
389
  }
341
- };
390
+ }
342
391
 
343
392
  /**
344
393
  * Forces the popover to hide if the user presses escape while it, one of its childen, or the reference element are focused
@@ -357,7 +406,7 @@ export class PopoverController extends BasePopoverController {
357
406
  }
358
407
 
359
408
  this.hide(e);
360
- };
409
+ }
361
410
 
362
411
  /**
363
412
  * Toggles all classes on the originating element based on the `class-toggle` data
@@ -380,7 +429,8 @@ export class PopoverController extends BasePopoverController {
380
429
  * @param {boolean=} show - A boolean indicating whether this is being triggered by a show or hide.
381
430
  */
382
431
  private toggleAccessibilityAttributes(show?: boolean) {
383
- const expandedValue = show?.toString() || this.referenceElement.ariaExpanded || "false";
432
+ const expandedValue =
433
+ show?.toString() || this.referenceElement.ariaExpanded || "false";
384
434
  this.referenceElement.ariaExpanded = expandedValue;
385
435
  this.referenceElement.setAttribute("aria-expanded", expandedValue);
386
436
  }
@@ -447,21 +497,26 @@ export interface PopoverOptions {
447
497
  * If the popover does not have a parent element, it will be inserted as a immediately after the reference element.
448
498
  * @param options an optional collection of options to use when configuring the popover.
449
499
  */
450
- export function attachPopover(element: Element, popover: Element | string, options?: PopoverOptions)
451
- {
500
+ export function attachPopover(
501
+ element: Element,
502
+ popover: Element | string,
503
+ options?: PopoverOptions
504
+ ) {
452
505
  const { referenceElement, popover: existingPopover } = getPopover(element);
453
506
 
454
507
  if (existingPopover) {
455
- throw `element already has popover with id="${existingPopover.id}"`
508
+ throw `element already has popover with id="${existingPopover.id}"`;
456
509
  }
457
510
 
458
511
  if (!referenceElement) {
459
- throw `element has invalid data-s-popover-reference-selector attribute`
512
+ throw `element has invalid data-s-popover-reference-selector attribute`;
460
513
  }
461
514
 
462
- if (typeof popover === 'string') {
515
+ if (typeof popover === "string") {
463
516
  // eslint-disable-next-line no-unsanitized/method
464
- const elements = document.createRange().createContextualFragment(popover).children;
517
+ const elements = document
518
+ .createRange()
519
+ .createContextualFragment(popover).children;
465
520
  if (elements.length !== 1) {
466
521
  throw "popover should contain a single element";
467
522
  }
@@ -471,7 +526,7 @@ export function attachPopover(element: Element, popover: Element | string, optio
471
526
  const existingId = referenceElement.getAttribute("aria-controls");
472
527
  let popoverId = popover.id;
473
528
 
474
- if (!popover.classList.contains('s-popover')) {
529
+ if (!popover.classList.contains("s-popover")) {
475
530
  throw `popover should have the "s-popover" class but had class="${popover.className}"`;
476
531
  }
477
532
 
@@ -480,7 +535,8 @@ export function attachPopover(element: Element, popover: Element | string, optio
480
535
  }
481
536
 
482
537
  if (!popoverId) {
483
- popoverId = "--stacks-s-popover-" + Math.random().toString(36).substring(2, 10);
538
+ popoverId =
539
+ "--stacks-s-popover-" + Math.random().toString(36).substring(2, 10);
484
540
  popover.id = popoverId;
485
541
  }
486
542
 
@@ -496,7 +552,10 @@ export function attachPopover(element: Element, popover: Element | string, optio
496
552
 
497
553
  if (options) {
498
554
  if (options.toggleOnClick) {
499
- referenceElement.setAttribute("data-action", "click->s-popover#toggle");
555
+ referenceElement.setAttribute(
556
+ "data-action",
557
+ "click->s-popover#toggle"
558
+ );
500
559
  }
501
560
  if (options.placement) {
502
561
  element.setAttribute("data-s-popover-placement", options.placement);
@@ -513,7 +572,8 @@ export function attachPopover(element: Element, popover: Element | string, optio
513
572
  * @returns The popover that was attached to the element.
514
573
  */
515
574
  export function detachPopover(element: Element) {
516
- const { isPopover, controller, referenceElement, popover } = getPopover(element);
575
+ const { isPopover, controller, referenceElement, popover } =
576
+ getPopover(element);
517
577
 
518
578
  // Hide the popover so its events fire.
519
579
  controller?.hide();
@@ -534,13 +594,13 @@ export function detachPopover(element: Element) {
534
594
 
535
595
  interface GetPopoverResult {
536
596
  /** indicates whether or not the element has s-popover in its `data-controller` class */
537
- isPopover: boolean,
597
+ isPopover: boolean;
538
598
  /** element's existing `PopoverController` or null it it has not been configured yet */
539
- controller: PopoverController | null,
599
+ controller: PopoverController | null;
540
600
  /** popover's reference element as would live in `referenceSelector` or null if invalid */
541
- referenceElement: Element | null,
601
+ referenceElement: Element | null;
542
602
  /** popover currently associated with the controller, or null if one does not exist in the DOM */
543
- popover: HTMLElement | null
603
+ popover: HTMLElement | null;
544
604
  }
545
605
 
546
606
  /**
@@ -549,11 +609,21 @@ interface GetPopoverResult {
549
609
  * @param element An element that may have `data-controller="s-popover"`.
550
610
  */
551
611
  function getPopover(element: Element): GetPopoverResult {
552
- const isPopover = element.getAttribute("data-controller")?.includes("s-popover") || false;
553
- const controller = Stacks.application.getControllerForElementAndIdentifier(element, "s-popover") as PopoverController;
554
- const referenceSelector = element.getAttribute("data-s-popover-reference-selector");
555
- const referenceElement = referenceSelector ? element.querySelector(referenceSelector) : element;
556
- const popoverId = referenceElement ? referenceElement.getAttribute("aria-controls") : null;
612
+ const isPopover =
613
+ element.getAttribute("data-controller")?.includes("s-popover") || false;
614
+ const controller = Stacks.application.getControllerForElementAndIdentifier(
615
+ element,
616
+ "s-popover"
617
+ ) as PopoverController;
618
+ const referenceSelector = element.getAttribute(
619
+ "data-s-popover-reference-selector"
620
+ );
621
+ const referenceElement = referenceSelector
622
+ ? element.querySelector(referenceSelector)
623
+ : element;
624
+ const popoverId = referenceElement
625
+ ? referenceElement.getAttribute("aria-controls")
626
+ : null;
557
627
  const popover = popoverId ? document.getElementById(popoverId) : null;
558
628
  return { isPopover, controller, referenceElement, popover };
559
629
  }
@@ -564,12 +634,18 @@ function getPopover(element: Element): GetPopoverResult {
564
634
  * @param controllerName The name of the controller to add/remove
565
635
  * @param include Whether to add the controllerName value
566
636
  */
567
- function toggleController(el: Element, controllerName: string, include: boolean) {
568
- const controllers = new Set(el.getAttribute('data-controller')?.split(/\s+/));
637
+ function toggleController(
638
+ el: Element,
639
+ controllerName: string,
640
+ include: boolean
641
+ ) {
642
+ const controllers = new Set(
643
+ el.getAttribute("data-controller")?.split(/\s+/)
644
+ );
569
645
  if (include) {
570
646
  controllers.add(controllerName);
571
647
  } else {
572
648
  controllers.delete(controllerName);
573
649
  }
574
- el.setAttribute('data-controller', Array.from(controllers).join(' '))
650
+ el.setAttribute("data-controller", Array.from(controllers).join(" "));
575
651
  }
@@ -7,19 +7,29 @@ export class TableController extends Stacks.StacksController {
7
7
 
8
8
  setCurrentSort(headElem: Element, direction: "asc" | "desc" | "none") {
9
9
  if (["asc", "desc", "none"].indexOf(direction) < 0) {
10
- throw "direction must be one of asc, desc, or none"
10
+ throw "direction must be one of asc, desc, or none";
11
11
  }
12
12
  // eslint-disable-next-line @typescript-eslint/no-this-alias
13
13
  const controller = this;
14
14
  this.columnTargets.forEach(function (target) {
15
15
  const isCurrrent = target === headElem;
16
16
 
17
- target.classList.toggle("is-sorted", isCurrrent && direction !== "none");
17
+ target.classList.toggle(
18
+ "is-sorted",
19
+ isCurrrent && direction !== "none"
20
+ );
18
21
 
19
- target.querySelectorAll(".js-sorting-indicator").forEach(function (icon) {
20
- const visible = isCurrrent ? direction : "none";
21
- icon.classList.toggle("d-none", !icon.classList.contains("js-sorting-indicator-" + visible));
22
- });
22
+ target
23
+ .querySelectorAll(".js-sorting-indicator")
24
+ .forEach(function (icon) {
25
+ const visible = isCurrrent ? direction : "none";
26
+ icon.classList.toggle(
27
+ "d-none",
28
+ !icon.classList.contains(
29
+ "js-sorting-indicator-" + visible
30
+ )
31
+ );
32
+ });
23
33
 
24
34
  if (!isCurrrent || direction === "none") {
25
35
  controller.removeElementData(target, "sort-direction");
@@ -27,7 +37,7 @@ export class TableController extends Stacks.StacksController {
27
37
  controller.setElementData(target, "sort-direction", direction);
28
38
  }
29
39
  });
30
- };
40
+ }
31
41
 
32
42
  sort(evt: Event) {
33
43
  // eslint-disable-next-line @typescript-eslint/no-this-alias
@@ -42,7 +52,8 @@ export class TableController extends Stacks.StacksController {
42
52
  // the column slot number of the clicked header
43
53
  const colno = getCellSlot(colHead);
44
54
 
45
- if (colno < 0) { // this shouldn't happen if the clicked element is actually a column head
55
+ if (colno < 0) {
56
+ // this shouldn't happen if the clicked element is actually a column head
46
57
  return;
47
58
  }
48
59
 
@@ -52,7 +63,8 @@ export class TableController extends Stacks.StacksController {
52
63
 
53
64
  // the default behavior when clicking a header is to sort by this column in ascending
54
65
  // direction, *unless* it is already sorted that way
55
- const direction = this.getElementData(colHead, "sort-direction") === "asc" ? -1 : 1;
66
+ const direction =
67
+ this.getElementData(colHead, "sort-direction") === "asc" ? -1 : 1;
56
68
 
57
69
  const rows = Array.from(table.tBodies[0].rows);
58
70
 
@@ -82,9 +94,12 @@ export class TableController extends Stacks.StacksController {
82
94
  // unless the to-be-sorted-by value is explicitly provided on the element via this attribute,
83
95
  // the value we're using is the cell's text, trimmed of any whitespace
84
96
  const explicit = controller.getElementData(cell, "sort-val");
85
- const d = typeof explicit === "string" ? explicit : cell.textContent!.trim();
97
+ const d =
98
+ typeof explicit === "string"
99
+ ? explicit
100
+ : cell.textContent?.trim() ?? "";
86
101
 
87
- if ((d !== "") && (`${parseInt(d, 10)}` !== d)) {
102
+ if (d !== "" && `${parseInt(d, 10)}` !== d) {
88
103
  anyNonInt = true;
89
104
  }
90
105
  data.push([d, index]);
@@ -94,7 +109,10 @@ export class TableController extends Stacks.StacksController {
94
109
  // having the lowest possible value (i.e. sorted to the top if ascending, bottom if descending)
95
110
  if (!anyNonInt) {
96
111
  data.forEach(function (tuple) {
97
- tuple[0] = tuple[0] === "" ? Number.MIN_VALUE : parseInt(tuple[0] as string, 10);
112
+ tuple[0] =
113
+ tuple[0] === ""
114
+ ? Number.MIN_VALUE
115
+ : parseInt(tuple[0] as string, 10);
98
116
  });
99
117
  }
100
118
 
@@ -117,7 +135,7 @@ export class TableController extends Stacks.StacksController {
117
135
  // this is the actual reordering of the table rows
118
136
  data.forEach(function (tup) {
119
137
  const row = rows[tup[1]];
120
- row.parentElement!.removeChild(row);
138
+ row.parentElement?.removeChild(row);
121
139
  if (firstBottomRow) {
122
140
  tbody.insertBefore(row, firstBottomRow);
123
141
  } else {
@@ -129,26 +147,35 @@ export class TableController extends Stacks.StacksController {
129
147
  // will cause sorting in descending direction
130
148
  this.setCurrentSort(colHead, direction === 1 ? "asc" : "desc");
131
149
  }
132
-
133
150
  }
134
151
 
135
- function buildIndex(section: HTMLTableSectionElement): HTMLTableCellElement[][] {
152
+ function buildIndex(
153
+ section: HTMLTableSectionElement
154
+ ): HTMLTableCellElement[][] {
136
155
  const result = buildIndexOrGetCellSlot(section);
137
156
  if (!(result instanceof Array)) {
138
- throw "shouldn't happen"
157
+ throw "shouldn't happen";
139
158
  }
140
159
  return result;
141
160
  }
142
161
 
143
162
  function getCellSlot(cell: HTMLTableCellElement): number {
144
- if (!(cell.parentElement && cell.parentElement.parentElement instanceof HTMLTableSectionElement)) {
145
- throw "invalid table"
163
+ if (
164
+ !(
165
+ cell.parentElement &&
166
+ cell.parentElement.parentElement instanceof HTMLTableSectionElement
167
+ )
168
+ ) {
169
+ throw "invalid table";
146
170
  }
147
- const result = buildIndexOrGetCellSlot(cell.parentElement.parentElement, cell);
171
+ const result = buildIndexOrGetCellSlot(
172
+ cell.parentElement.parentElement,
173
+ cell
174
+ );
148
175
  if (typeof result !== "number") {
149
- throw "shouldn't happen"
176
+ throw "shouldn't happen";
150
177
  }
151
- return result
178
+ return result;
152
179
  }
153
180
 
154
181
  // Just because a <td> is the 4th *child* of its <tr> doesn't mean it belongs to the 4th *column*
@@ -166,9 +193,12 @@ function getCellSlot(cell: HTMLTableCellElement): number {
166
193
  //
167
194
  // If the second argument is given, it's a <td> or <th> that we're trying to find, and the algorithm
168
195
  // stops as soon as it has found it and the function returns its slot number.
169
- function buildIndexOrGetCellSlot(section: HTMLTableSectionElement, findCell?: HTMLTableCellElement) {
196
+ function buildIndexOrGetCellSlot(
197
+ section: HTMLTableSectionElement,
198
+ findCell?: HTMLTableCellElement
199
+ ) {
170
200
  const index = [];
171
- let curRow = section.children[0];
201
+ let curRow: Element | null = section.children[0];
172
202
 
173
203
  // the elements of these two arrays are synchronized; the first array contains table cell elements,
174
204
  // the second one contains a number that indicates for how many more rows this elements will
@@ -177,13 +207,22 @@ function buildIndexOrGetCellSlot(section: HTMLTableSectionElement, findCell?: HT
177
207
  const growingRowsLeft: number[] = [];
178
208
 
179
209
  // continue while we have actual <tr>'s left *or* we still have rowspan'ed elements that aren't done
180
- while (curRow || growingRowsLeft.some(function (e) { return e !== 0; })) {
210
+ while (
211
+ curRow ||
212
+ growingRowsLeft.some(function (e) {
213
+ return e !== 0;
214
+ })
215
+ ) {
181
216
  const curIndexRow: HTMLTableCellElement[] = [];
182
217
  index.push(curIndexRow);
183
218
 
184
219
  let curSlot = 0;
185
220
  if (curRow) {
186
- for (let curCellInd = 0; curCellInd < curRow.children.length; curCellInd++) {
221
+ for (
222
+ let curCellInd = 0;
223
+ curCellInd < curRow.children.length;
224
+ curCellInd++
225
+ ) {
187
226
  while (growingRowsLeft[curSlot]) {
188
227
  growingRowsLeft[curSlot]--;
189
228
  curIndexRow[curSlot] = growing[curSlot];
@@ -191,7 +230,7 @@ function buildIndexOrGetCellSlot(section: HTMLTableSectionElement, findCell?: HT
191
230
  }
192
231
  const cell = curRow.children[curCellInd];
193
232
  if (!(cell instanceof HTMLTableCellElement)) {
194
- throw "invalid table"
233
+ throw "invalid table";
195
234
  }
196
235
  if (getComputedStyle(cell).display === "none") {
197
236
  continue;
@@ -215,8 +254,10 @@ function buildIndexOrGetCellSlot(section: HTMLTableSectionElement, findCell?: HT
215
254
  curSlot++;
216
255
  }
217
256
  if (curRow) {
218
- curRow = curRow.nextElementSibling!;
257
+ curRow = curRow.nextElementSibling;
219
258
  }
220
259
  }
221
- return findCell ? -1 : index; /* if findCell was given but we end up here, that means it isn't in this section */
260
+ return findCell
261
+ ? -1
262
+ : index; /* if findCell was given but we end up here, that means it isn't in this section */
222
263
  }