mirror of
https://github.com/desktop/desktop
synced 2024-10-31 11:07:25 +00:00
Add dropdown button event handling
This commit is contained in:
parent
178912ef2c
commit
fb788e1881
4 changed files with 77 additions and 15 deletions
|
@ -131,19 +131,16 @@ export class MenuListItem extends React.Component<IMenuListItemProps, {}> {
|
|||
private renderLabel() {
|
||||
const { item, renderLabel } = this.props
|
||||
|
||||
if(renderLabel !== undefined) {
|
||||
if (renderLabel !== undefined) {
|
||||
return renderLabel(item)
|
||||
}
|
||||
|
||||
if (item.type === 'separator') {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<AccessText
|
||||
text={item.label}
|
||||
highlight={this.props.highlightAccessKey}
|
||||
/>
|
||||
<AccessText text={item.label} highlight={this.props.highlightAccessKey} />
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -138,21 +138,27 @@ export class MenuPane extends React.Component<IMenuPaneProps> {
|
|||
}
|
||||
|
||||
private tryMoveSelectionByFirstCharacter(key: string, source: ClickSource) {
|
||||
if (key.length > 1 || !isPrintableCharacterKey(key) || !this.props.allowFirstCharacterNavigation) {
|
||||
if (
|
||||
key.length > 1 ||
|
||||
!isPrintableCharacterKey(key) ||
|
||||
!this.props.allowFirstCharacterNavigation
|
||||
) {
|
||||
return
|
||||
}
|
||||
const { items, selectedItem } = this.props
|
||||
const char = key.toLowerCase();
|
||||
const char = key.toLowerCase()
|
||||
const currentRow = selectedItem ? items.indexOf(selectedItem) + 1 : 0
|
||||
const start = currentRow + 1 > items.length ? 0 : currentRow + 1;
|
||||
const start = currentRow + 1 > items.length ? 0 : currentRow + 1
|
||||
|
||||
const firstChars = items.map(v =>
|
||||
v.type === 'separator' ? '' : v.label.trim()[0].toLowerCase()
|
||||
)
|
||||
|
||||
const firstChars = items.map(v => v.type === 'separator' ? '' : v.label.trim()[0].toLowerCase())
|
||||
|
||||
// Check menu items after selected
|
||||
let ix: number = firstChars.indexOf(char, start)
|
||||
|
||||
// check menu items before selected
|
||||
if(ix === -1) {
|
||||
if (ix === -1) {
|
||||
ix = firstChars.indexOf(char, 0)
|
||||
}
|
||||
|
||||
|
@ -286,5 +292,5 @@ const supportedKeys = [
|
|||
const isSupportedKey = (key: string): key is typeof supportedKeys[number] =>
|
||||
(supportedKeys as readonly string[]).includes(key)
|
||||
|
||||
const isPrintableCharacterKey = (key: string) => key.length === 1 && key.match(/\S/);
|
||||
|
||||
const isPrintableCharacterKey = (key: string) =>
|
||||
key.length === 1 && key.match(/\S/)
|
||||
|
|
|
@ -70,6 +70,7 @@ export class DropdownSelectButton<
|
|||
IDropdownSelectButtonState<T>
|
||||
> {
|
||||
private invokeButtonRef: HTMLButtonElement | null = null
|
||||
private dropdownButtonRef: HTMLButtonElement | null = null
|
||||
private optionsContainerRef: HTMLDivElement | null = null
|
||||
|
||||
public constructor(props: IDropdownSelectButtonProps<T>) {
|
||||
|
@ -140,12 +141,13 @@ export class DropdownSelectButton<
|
|||
depth: number | undefined,
|
||||
event: React.KeyboardEvent<HTMLDivElement>
|
||||
) => {
|
||||
if (event.key !== 'Escape') {
|
||||
if (event.key !== 'Escape' && event.key !== 'Esc') {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.dropdownButtonRef?.focus()
|
||||
this.setState({ showButtonOptions: false })
|
||||
}
|
||||
|
||||
|
@ -167,10 +169,58 @@ export class DropdownSelectButton<
|
|||
this.setState({ showButtonOptions: !this.state.showButtonOptions })
|
||||
}
|
||||
|
||||
private onDropdownButtonKeyDown = (
|
||||
event: React.KeyboardEvent<HTMLButtonElement>
|
||||
) => {
|
||||
const { key } = event
|
||||
let flag = false
|
||||
|
||||
switch (key) {
|
||||
case ' ':
|
||||
case 'Enter':
|
||||
case 'ArrowDown':
|
||||
case 'Down':
|
||||
this.setState({
|
||||
selectedOption: this.props.options.at(0) ?? null,
|
||||
showButtonOptions: true,
|
||||
})
|
||||
flag = true
|
||||
break
|
||||
|
||||
case 'Esc':
|
||||
case 'Escape':
|
||||
this.dropdownButtonRef?.focus()
|
||||
this.setState({ showButtonOptions: false })
|
||||
flag = true
|
||||
break
|
||||
|
||||
case 'Up':
|
||||
case 'ArrowUp':
|
||||
this.setState({
|
||||
selectedOption: this.props.options.at(-1) ?? null,
|
||||
showButtonOptions: true,
|
||||
})
|
||||
flag = true
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
private onInvokeButtonRef = (buttonRef: HTMLButtonElement | null) => {
|
||||
this.invokeButtonRef = buttonRef
|
||||
}
|
||||
|
||||
private onDropdownButtonRef = (buttonRef: HTMLButtonElement | null) => {
|
||||
this.dropdownButtonRef = buttonRef
|
||||
}
|
||||
|
||||
private onOptionsContainerRef = (ref: HTMLDivElement | null) => {
|
||||
this.optionsContainerRef = ref
|
||||
}
|
||||
|
@ -284,6 +334,8 @@ export class DropdownSelectButton<
|
|||
<Button
|
||||
className={dropdownClasses}
|
||||
onClick={this.openSplitButtonDropdown}
|
||||
onKeyDown={this.onDropdownButtonKeyDown}
|
||||
onButtonRef={this.onDropdownButtonRef}
|
||||
type="button"
|
||||
ariaExpanded={showButtonOptions}
|
||||
ariaHaspopup={true}
|
||||
|
|
|
@ -27,6 +27,12 @@ export interface IButtonProps {
|
|||
*/
|
||||
readonly onMouseEnter?: (event: React.MouseEvent<HTMLButtonElement>) => void
|
||||
|
||||
/**
|
||||
* A function that's called when the user moves over the button with
|
||||
* a pointer device.
|
||||
*/
|
||||
readonly onKeyDown?: (event: React.KeyboardEvent<HTMLButtonElement>) => void
|
||||
|
||||
/** An optional tooltip to render when hovering over the button */
|
||||
readonly tooltip?: string
|
||||
|
||||
|
@ -185,6 +191,7 @@ export class Button extends React.Component<IButtonProps, {}> {
|
|||
<button
|
||||
className={className}
|
||||
onClick={disabled ? preventDefault : this.onClick}
|
||||
onKeyDown={this.props.onKeyDown}
|
||||
onContextMenu={disabled ? preventDefault : this.onContextMenu}
|
||||
type={this.props.type || 'button'}
|
||||
ref={this.innerButtonRef}
|
||||
|
|
Loading…
Reference in a new issue