Grid-style field type keyboard navigation
10 open threads
Closes #3390914
Merge request reports
Activity
added 3 commits
-
2a77c6a3...b2b03c80 - 2 commits from branch
project:11.x
- 83cd186f - Grid-style field type keyboard navigation
-
2a77c6a3...b2b03c80 - 2 commits from branch
281 281 $form['add'] = [ 282 282 '#type' => 'container', 283 283 '#attributes' => [ 284 'class' => 'add-field-container', 284 'class' => [ 285 'js-click-to-select-container', 509 (clickToSelectEl) => { 510 const input = clickToSelectEl.querySelector('input'); 511 if (input) { 512 Drupal.behaviors.clickToSelect.clickHandler(clickToSelectEl, input); 508 once( 509 'field-click-to-select', 510 '.js-click-to-select-container', 511 context, 512 ).forEach((container) => { 513 const itemClassName = 'js-click-to-select'; 514 const items = container.querySelectorAll(`.${itemClassName}`); 515 items.forEach((item) => { 516 const input = item.querySelector('input'); 517 if (!input) { 518 return; 519 } - Comment on lines +514 to +519
We can avoid the awkard check with css selectors
514 const items = container.querySelectorAll(`.${itemClassName}`); 515 items.forEach((item) => { 516 const input = item.querySelector('input'); 517 if (!input) { 518 return; 519 } 514 const items = container.querySelectorAll(`.${itemClassName}:has(input)`); 515 items.forEach((item) => { 516 const input = item.querySelector('input');
517 587 if (input.checked) { 518 this.selectHandler(clickToSelectEl, input); 588 this.selectHandler(items, item, input); 519 589 } 520 }, 521 ); 590 }); 591 }); 522 592 }, 523 593 // Adds click event listener to the field card. 524 clickHandler(clickToSelectEl, input) { 525 $(clickToSelectEl).on('click', (event) => { 526 const clickToSelect = event.target.closest('.js-click-to-select'); 527 this.selectHandler(clickToSelect, input); 528 $(input).trigger('updateOptions'); 594 itemClickHandler(items, item, input) { 597 // by keyboard navigation. 598 if ( 599 event.type === 'click' && 600 event.clientX !== 0 && 601 event.clientY !== 0 602 ) { 603 this.selectHandler(items, item, input); 604 input.dispatchEvent(new Event('updateOptions')); 605 } 529 606 }); 530 607 }, 531 608 // Handles adding and removing classes for the different states. 532 selectHandler(clickToSelect, input) { 533 $(input).on('focus', () => clickToSelect.classList.add('focus')); 534 $(input).on('blur', () => clickToSelect.classList.remove('focus')); 609 selectHandler(items, item, input) { 601 event.clientY !== 0 602 ) { 603 this.selectHandler(items, item, input); 604 input.dispatchEvent(new Event('updateOptions')); 605 } 529 606 }); 530 607 }, 531 608 // Handles adding and removing classes for the different states. 532 selectHandler(clickToSelect, input) { 533 $(input).on('focus', () => clickToSelect.classList.add('focus')); 534 $(input).on('blur', () => clickToSelect.classList.remove('focus')); 609 selectHandler(items, item, input) { 610 input.addEventListener('focus', () => { 611 item.classList.add('focus'); 612 }); 613 input.addEventListener('blur', () => item.classList.remove('focus')); 533 $(input).on('focus', () => clickToSelect.classList.add('focus')); 534 $(input).on('blur', () => clickToSelect.classList.remove('focus')); 609 selectHandler(items, item, input) { 610 input.addEventListener('focus', () => { 611 item.classList.add('focus'); 612 }); 613 input.addEventListener('blur', () => item.classList.remove('focus')); 535 614 input.checked = true; 536 document 537 .querySelectorAll('.js-click-to-select.selected') 538 .forEach((item) => { 539 item.classList.remove('selected'); 615 [...items] 616 .filter((selected) => selected.classList.contains('selected')) 617 .forEach((selected) => { 618 selected.classList.remove('selected'); 524 clickHandler(clickToSelectEl, input) { 525 $(clickToSelectEl).on('click', (event) => { 526 const clickToSelect = event.target.closest('.js-click-to-select'); 527 this.selectHandler(clickToSelect, input); 528 $(input).trigger('updateOptions'); 594 itemClickHandler(items, item, input) { 595 item.addEventListener('click', (event) => { 596 // Checking if it's a real click and event is not triggered 597 // by keyboard navigation. 598 if ( 599 event.type === 'click' && 600 event.clientX !== 0 && 601 event.clientY !== 0 602 ) { 603 this.selectHandler(items, item, input); 604 input.dispatchEvent(new Event('updateOptions')); 520 521 this.itemClickHandler(items, item, input); 522 523 // Adding grid-style keyboard navigation. 524 // For the reference https://www.w3.org/WAI/ARIA/apg/patterns/grid/ 525 input.addEventListener('keydown', (event) => { 526 const columns = window 527 .getComputedStyle(container) 528 .getPropertyValue('grid-template-columns') 529 .split(' ').length; 530 if (columns > 1) { 531 const currentIndex = Array.from(items).indexOf( 532 document.activeElement.closest(`.${itemClassName}`), 533 ); 534 let newIndex; 535 let isReachedEdge = false; 516 const input = item.querySelector('input'); 517 if (!input) { 518 return; 519 } 520 521 this.itemClickHandler(items, item, input); 522 523 // Adding grid-style keyboard navigation. 524 // For the reference https://www.w3.org/WAI/ARIA/apg/patterns/grid/ 525 input.addEventListener('keydown', (event) => { 526 const columns = window 527 .getComputedStyle(container) 528 .getPropertyValue('grid-template-columns') 529 .split(' ').length; 530 if (columns > 1) { 531 const currentIndex = Array.from(items).indexOf( 542 } 543 break; 544 case 'ArrowDown': 545 newIndex = currentIndex + columns; 546 if (newIndex >= items.length) { 547 isReachedEdge = true; 548 } 549 break; 550 case 'ArrowLeft': 551 newIndex = currentIndex - 1; 552 if ([columns - 1, -1].includes(newIndex % columns)) { 553 isReachedEdge = true; 513 554 } 555 break; 556 case 'ArrowRight': 557 newIndex = currentIndex + 1;
Please register or sign in to reply