diff --git a/app/src/ui/changes/filter-changes-list.tsx b/app/src/ui/changes/filter-changes-list.tsx index 011b90026d3..41f319ebbbd 100644 --- a/app/src/ui/changes/filter-changes-list.tsx +++ b/app/src/ui/changes/filter-changes-list.tsx @@ -154,7 +154,7 @@ interface IFilterChangesListProps { readonly onChangesListScrolled: (scrollTop: number) => void /* The scrollTop of the compareList. It is stored to allow for scroll position persistence */ - // TBD: readonly changesListScrollTop?: number + readonly changesListScrollTop?: number /** * Called to open a file in its default application @@ -769,12 +769,10 @@ export class FilterChangesList extends React.Component< } private onItemContextMenu = ( - item: any, + item: IChangesListItem, event: React.MouseEvent ) => { - const row = 0 /// TBD; - const { workingDirectory } = this.props - const file = workingDirectory.files[row] + const file = item.change if (this.props.isCommitting) { return @@ -816,8 +814,7 @@ export class FilterChangesList extends React.Component< } } - // TBD: make private - public onScroll = (scrollTop: number, clientHeight: number) => { + private onScroll = (scrollTop: number, clientHeight: number) => { this.props.onChangesListScrolled(scrollTop) } @@ -1009,16 +1006,12 @@ export class FilterChangesList extends React.Component< ) } - // TBD: make private - public onRowDoubleClick = (row: number) => { - const file = this.props.workingDirectory.files[row] - - this.props.onOpenItemInExternalEditor(file.path) + private onChangedFileDoubleClick = (item: IChangesListItem) => { + this.props.onOpenItemInExternalEditor(item.change.path) } - // TBD: make private - public onRowKeyDown = ( - _row: number, + private onItemKeyDown = ( + _item: IChangesListItem, event: React.KeyboardEvent ) => { // The commit is already in-flight but this check prevents the @@ -1112,24 +1105,24 @@ export class FilterChangesList extends React.Component< filterText={this.state.filterText} onFilterTextChanged={this.onFilterTextChanged} selectedItem={this.state.selectedItem} + // selectionMode="multi"... renderItem={this.renderChangedFile} onItemClick={this.onChangedFileClick} - // selectionMode="multi"... - // onRowDoubleClick={this.onRowDoubleClick} - // onRowKeyboardFocus={this.onRowFocus} - // onRowBlur={this.onRowBlur} - // onScroll={this.onScroll} - // setScrollTop={this.props.changesListScrollTop} - // onRowKeyDown={this.onRowKeyDown} + onItemDoubleClick={this.onChangedFileDoubleClick} + onItemKeyboardFocus={this.onChangedFileFocus} + onItemBlur={this.onChangedFileBlur} + onScroll={this.onScroll} + setScrollTop={this.props.changesListScrollTop} + onItemKeyDown={this.onItemKeyDown} onSelectionChanged={this.onFileSelectionChanged} - groups={this.state.groups} // + groups={this.state.groups} invalidationProps={{ workingDirectory: workingDirectory, isCommitting: isCommitting, focusedRow: this.state.focusedRow, }} onItemContextMenu={this.onItemContextMenu} - // ariaLabel={filesDescription} + ariaLabel={filesDescription} /> {this.renderStashedChanges()} @@ -1138,13 +1131,11 @@ export class FilterChangesList extends React.Component< ) } - // TBD: Needs private once hooked into list - public onRowFocus = (changeListItem: IChangesListItem) => { + private onChangedFileFocus = (changeListItem: IChangesListItem) => { this.setState({ focusedRow: changeListItem.id }) } - // TBD: Needs private once hooked into list - public onRowBlur = (changeListItem: IChangesListItem) => { + private onChangedFileBlur = (changeListItem: IChangesListItem) => { if (this.state.focusedRow === changeListItem.id) { this.setState({ focusedRow: null }) } diff --git a/app/src/ui/lib/augmented-filter-list.tsx b/app/src/ui/lib/augmented-filter-list.tsx index 7fb6f84e30e..40f56183307 100644 --- a/app/src/ui/lib/augmented-filter-list.tsx +++ b/app/src/ui/lib/augmented-filter-list.tsx @@ -84,6 +84,8 @@ interface IAugmentedSectionFilterListProps { */ readonly onItemClick?: (item: T, source: ClickSource) => void + readonly onItemDoubleClick?: (item: T, source: ClickSource) => void + /** * This function will be called when the selection changes as a result of a * user keyboard or mouse action (i.e. not when props change). This function @@ -168,6 +170,31 @@ interface IAugmentedSectionFilterListProps { item: T, event: React.MouseEvent ) => void + + /** This function will be called only when an item obtains focus via keyboard */ + readonly onItemKeyboardFocus?: ( + item: T, + event: React.KeyboardEvent + ) => void + + /** This function will be called when a row loses focus */ + readonly onItemBlur?: ( + item: T, + event: React.FocusEvent + ) => void + + readonly onItemKeyDown?: (item: T, event: React.KeyboardEvent) => void + + readonly onScroll?: (scrollTop: number, clientHeight: number) => void + + /** + * The number of pixels from the top of the list indicating + * where to scroll do on rendering of the list. + */ + readonly setScrollTop?: number + + /** The aria-label attribute for the list component. */ + readonly ariaLabel?: string } interface IAugmentedSectionFilterListState { @@ -376,13 +403,19 @@ export class AugmentedSectionFilterList< } onSelectedRowChanged={this.onSelectedRowChanged} onRowClick={this.onRowClick} + onRowDoubleClick={this.onRowDoubleClick} onRowKeyDown={this.onRowKeyDown} onRowContextMenu={this.onRowContextMenu} + onRowKeyboardFocus={this.onRowKeyboardFocus} + onRowBlur={this.onRowBlur} canSelectRow={this.canSelectRow} invalidationProps={{ ...this.props, ...this.props.invalidationProps, }} + onScroll={this.props.onScroll} + setScrollTop={this.props.setScrollTop} + ariaLabel={this.props.ariaLabel} /> ) } @@ -483,6 +516,16 @@ export class AugmentedSectionFilterList< } } + private onRowDoubleClick = (index: RowIndexPath, source: ClickSource) => { + if (this.props.onItemDoubleClick) { + const row = this.state.rows[index.section][index.row] + + if (row.kind === 'item') { + this.props.onItemDoubleClick(row.item, source) + } + } + } + private onRowContextMenu = ( index: RowIndexPath, source: React.MouseEvent @@ -500,10 +543,54 @@ export class AugmentedSectionFilterList< this.props.onItemContextMenu(row.item, source) } + private onRowKeyboardFocus = ( + index: RowIndexPath, + source: React.KeyboardEvent + ) => { + if (!this.props.onItemKeyboardFocus) { + return + } + + const row = this.state.rows[index.section][index.row] + + if (row.kind !== 'item') { + return + } + + this.props.onItemKeyboardFocus(row.item, source) + } + + private onRowBlur = ( + index: RowIndexPath, + source: React.FocusEvent + ) => { + if (!this.props.onItemBlur) { + return + } + + const row = this.state.rows[index.section][index.row] + + if (row.kind !== 'item') { + return + } + + this.props.onItemBlur(row.item, source) + } + private onRowKeyDown = ( indexPath: RowIndexPath, event: React.KeyboardEvent ) => { + const row = this.state.rows[indexPath.section][indexPath.row] + + if (row.kind === 'item' && this.props.onItemKeyDown) { + this.props.onItemKeyDown(row.item, event) + } + + if (event.defaultPrevented) { + return + } + const list = this.list if (!list) { return