|
1 | | -import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop, State, Watch, h, writeTask } from '@stencil/core'; |
| 1 | +import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Listen, Prop, State, Watch, h, writeTask } from '@stencil/core'; |
2 | 2 |
|
3 | 3 | import { config } from '../../global/config'; |
4 | 4 | import { getIonMode } from '../../global/ionic-global'; |
@@ -92,6 +92,12 @@ export class Segment implements ComponentInterface { |
92 | 92 | } |
93 | 93 | } |
94 | 94 |
|
| 95 | + /** |
| 96 | + * If `true`, navigating to an `ion-segment-button` with the keyboard will focus and select the element. |
| 97 | + * If `false`, keyboard navigation will only focus the `ion-segment-button` element. |
| 98 | + */ |
| 99 | + @Prop() selectOnFocus = false; |
| 100 | + |
95 | 101 | /** |
96 | 102 | * Emitted when the value property has changed and any |
97 | 103 | * dragging pointer has been released from `ion-segment`. |
@@ -136,6 +142,7 @@ export class Segment implements ComponentInterface { |
136 | 142 |
|
137 | 143 | async componentDidLoad() { |
138 | 144 | this.setCheckedClasses(); |
| 145 | + this.ensureFocusable(); |
139 | 146 |
|
140 | 147 | this.gesture = (await import('../../utils/gesture')).createGesture({ |
141 | 148 | el: this.el, |
@@ -431,6 +438,74 @@ export class Segment implements ComponentInterface { |
431 | 438 | this.checked = current; |
432 | 439 | } |
433 | 440 |
|
| 441 | + private getSegmentButton = (selector: 'first' | 'last' | 'next' | 'previous'): HTMLIonSegmentButtonElement | null => { |
| 442 | + const buttons = this.getButtons().filter(button => !button.disabled); |
| 443 | + const currIndex = buttons.findIndex(button => button === document.activeElement); |
| 444 | + |
| 445 | + switch (selector) { |
| 446 | + case 'first': |
| 447 | + return buttons[0]; |
| 448 | + case 'last': |
| 449 | + return buttons[buttons.length - 1]; |
| 450 | + case 'next': |
| 451 | + return buttons[currIndex + 1] || buttons[0]; |
| 452 | + case 'previous': |
| 453 | + return buttons[currIndex - 1] || buttons[buttons.length - 1]; |
| 454 | + default: |
| 455 | + return null; |
| 456 | + } |
| 457 | + } |
| 458 | + |
| 459 | + @Listen('keydown') |
| 460 | + onKeyDown(ev: KeyboardEvent) { |
| 461 | + const isRTL = document.dir === 'rtl'; |
| 462 | + let keyDownSelectsButton = this.selectOnFocus; |
| 463 | + let current; |
| 464 | + switch (ev.key) { |
| 465 | + case 'ArrowRight': |
| 466 | + ev.preventDefault(); |
| 467 | + current = isRTL ? this.getSegmentButton('previous') : this.getSegmentButton('next'); |
| 468 | + break; |
| 469 | + case 'ArrowLeft': |
| 470 | + ev.preventDefault(); |
| 471 | + current = isRTL ? this.getSegmentButton('next') : this.getSegmentButton('previous') |
| 472 | + break; |
| 473 | + case 'Home': |
| 474 | + ev.preventDefault(); |
| 475 | + current = this.getSegmentButton('first'); |
| 476 | + break; |
| 477 | + case 'End': |
| 478 | + ev.preventDefault(); |
| 479 | + current = this.getSegmentButton('last'); |
| 480 | + break; |
| 481 | + case ' ': |
| 482 | + case 'Enter': |
| 483 | + ev.preventDefault(); |
| 484 | + current = document.activeElement as HTMLIonSegmentButtonElement; |
| 485 | + keyDownSelectsButton = true; |
| 486 | + default: |
| 487 | + break; |
| 488 | + } |
| 489 | + |
| 490 | + if (!current) { return; } |
| 491 | + |
| 492 | + if (keyDownSelectsButton) { |
| 493 | + const previous = this.checked || current; |
| 494 | + this.checkButton(previous, current); |
| 495 | + } |
| 496 | + current.focus(); |
| 497 | + } |
| 498 | + |
| 499 | + /* By default, focus is delegated to the selected `ion-segment-button`. |
| 500 | + * If there is no selected button, focus will instead pass to the first child button. |
| 501 | + **/ |
| 502 | + private ensureFocusable() { |
| 503 | + if (this.value !== undefined) { return }; |
| 504 | + |
| 505 | + const buttons = this.getButtons(); |
| 506 | + buttons[0]?.setAttribute('tabindex', '0'); |
| 507 | + } |
| 508 | + |
434 | 509 | render() { |
435 | 510 | const mode = getIonMode(this); |
436 | 511 | return ( |
|
0 commit comments