@vaadin/component-base 24.4.0-alpha12 → 24.4.0-alpha13

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/component-base",
3
- "version": "24.4.0-alpha12",
3
+ "version": "24.4.0-alpha13",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -42,5 +42,5 @@
42
42
  "@vaadin/testing-helpers": "^0.6.0",
43
43
  "sinon": "^13.0.2"
44
44
  },
45
- "gitHead": "811e8327e02a8ecdca0bb5d6528856e70d429d0c"
45
+ "gitHead": "97246b0703cd04a4b0ea5dcd49e2581d45cf6367"
46
46
  }
package/src/define.js CHANGED
@@ -9,7 +9,7 @@ export function defineCustomElement(CustomElement) {
9
9
  if (!defined) {
10
10
  Object.defineProperty(CustomElement, 'version', {
11
11
  get() {
12
- return '24.4.0-alpha12';
12
+ return '24.4.0-alpha13';
13
13
  },
14
14
  });
15
15
 
@@ -55,6 +55,13 @@ export class IronListAdapter {
55
55
  this._scrollLineHeight = this._getScrollLineHeight();
56
56
  this.scrollTarget.addEventListener('wheel', (e) => this.__onWheel(e));
57
57
 
58
+ this.scrollTarget.addEventListener('virtualizer-element-focused', (e) => this.__onElementFocused(e));
59
+ this.elementsContainer.addEventListener('focusin', (e) => {
60
+ this.scrollTarget.dispatchEvent(
61
+ new CustomEvent('virtualizer-element-focused', { detail: { element: this.__getFocusedElement() } }),
62
+ );
63
+ });
64
+
58
65
  if (this.reorderElements) {
59
66
  // Reordering the physical elements cancels the user's grab of the scroll bar handle on Safari.
60
67
  // Need to defer reordering until the user lets go of the scroll bar handle.
@@ -425,6 +432,75 @@ export class IronListAdapter {
425
432
  /** @private */
426
433
  toggleScrollListener() {}
427
434
 
435
+ /** @private */
436
+ __getFocusedElement(visibleElements = this.__getVisibleElements()) {
437
+ return visibleElements.find(
438
+ (element) =>
439
+ element.contains(this.elementsContainer.getRootNode().activeElement) ||
440
+ element.contains(this.scrollTarget.getRootNode().activeElement),
441
+ );
442
+ }
443
+
444
+ /** @private */
445
+ __nextFocusableSiblingMissing(focusedElement, visibleElements) {
446
+ return (
447
+ // Check if focused element is the last visible DOM element
448
+ visibleElements.indexOf(focusedElement) === visibleElements.length - 1 &&
449
+ // ...while there are more items available
450
+ this.size > focusedElement.__virtualIndex + 1
451
+ );
452
+ }
453
+
454
+ /** @private */
455
+ __previousFocusableSiblingMissing(focusedElement, visibleElements) {
456
+ return (
457
+ // Check if focused element is the first visible DOM element
458
+ visibleElements.indexOf(focusedElement) === 0 &&
459
+ // ...while there are preceding items available
460
+ focusedElement.__virtualIndex > 0
461
+ );
462
+ }
463
+
464
+ /** @private */
465
+ __onElementFocused(e) {
466
+ if (!this.reorderElements) {
467
+ return;
468
+ }
469
+
470
+ const focusedElement = e.detail.element;
471
+ if (!focusedElement) {
472
+ return;
473
+ }
474
+
475
+ // User has tabbed to or within a virtualizer element.
476
+ // Check if a next or previous focusable sibling is missing while it should be there (so the user can continue tabbing).
477
+ // The focusable sibling might be missing due to the elements not yet being in the correct DOM order.
478
+ // First try flushing (which also flushes any active __scrollReorderDebouncer).
479
+ const visibleElements = this.__getVisibleElements();
480
+ if (
481
+ this.__previousFocusableSiblingMissing(focusedElement, visibleElements) ||
482
+ this.__nextFocusableSiblingMissing(focusedElement, visibleElements)
483
+ ) {
484
+ this.flush();
485
+ }
486
+
487
+ // If the focusable sibling is still missing (because the focused element is at the edge of the viewport and
488
+ // the virtual scrolling logic hasn't had the need to recycle elements), scroll the virtualizer just enough to
489
+ // have the focusable sibling inside the visible viewport to force the virtualizer to recycle.
490
+ const reorderedVisibleElements = this.__getVisibleElements();
491
+ if (this.__nextFocusableSiblingMissing(focusedElement, reorderedVisibleElements)) {
492
+ this._scrollTop +=
493
+ Math.ceil(focusedElement.getBoundingClientRect().bottom) -
494
+ Math.floor(this.scrollTarget.getBoundingClientRect().bottom - 1);
495
+ this.flush();
496
+ } else if (this.__previousFocusableSiblingMissing(focusedElement, reorderedVisibleElements)) {
497
+ this._scrollTop -=
498
+ Math.ceil(this.scrollTarget.getBoundingClientRect().top + 1) -
499
+ Math.floor(focusedElement.getBoundingClientRect().top);
500
+ this.flush();
501
+ }
502
+ }
503
+
428
504
  _scrollHandler() {
429
505
  // The scroll target is hidden.
430
506
  if (this.scrollTarget.offsetHeight === 0) {
@@ -662,13 +738,7 @@ export class IronListAdapter {
662
738
 
663
739
  // Which row to use as a target?
664
740
  const visibleElements = this.__getVisibleElements();
665
-
666
- const elementWithFocus = visibleElements.find(
667
- (element) =>
668
- element.contains(this.elementsContainer.getRootNode().activeElement) ||
669
- element.contains(this.scrollTarget.getRootNode().activeElement),
670
- );
671
- const targetElement = elementWithFocus || visibleElements[0];
741
+ const targetElement = this.__getFocusedElement(visibleElements) || visibleElements[0];
672
742
  if (!targetElement) {
673
743
  // All elements are hidden, don't reorder
674
744
  return;