Skip to content

Commit

Permalink
fix(tree): tree does not scroll to focused item (#UIM-89) (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
lskramarov authored and pimenovoleg committed Jul 17, 2019
1 parent c027191 commit 0e5e3b0
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,4 @@ export class ActiveDescendantKeyManager<T> extends ListKeyManager<Highlightable
this.activeItem.setActiveStyles();
}
}

}
8 changes: 6 additions & 2 deletions packages/mosaic/tree-select/tree-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,9 @@ describe('McTreeSelect', () => {
fixture.detectChanges();
flush();

(overlayContainerElement.querySelectorAll('mc-tree-option')[2] as HTMLElement).click();
const optionToClick = overlayContainerElement.querySelectorAll('mc-tree-option')[2] as HTMLElement;
optionToClick.focus();
optionToClick.click();
fixture.detectChanges();
flush();

Expand Down Expand Up @@ -1722,7 +1724,8 @@ describe('McTreeSelect', () => {
tick(10);

expect(formControl.value).toBe('Documents');
expect(fixture.componentInstance.options.toArray()[2].active).toBe(true);
expect(fixture.componentInstance.select.tree.keyManager.activeItem!.value)
.toBe('Documents');
}));

it('should not shift focus when the selected options are updated programmatically ' +
Expand Down Expand Up @@ -4581,6 +4584,7 @@ describe('McTreeSelect', () => {

const options: NodeListOf<HTMLElement> = overlayContainerElement.querySelectorAll('mc-tree-option');

options[2].focus();
options[2].click();
fixture.detectChanges();
flush();
Expand Down
3 changes: 1 addition & 2 deletions packages/mosaic/tree/_tree-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
background-color: mc-color($background, hover);
}

&.mc-focused,
&.mc-active {
&.mc-focused {
border-color: mc-color($primary);
}

Expand Down
71 changes: 23 additions & 48 deletions packages/mosaic/tree/tree-option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { CanDisable, toBoolean } from '@ptsecurity/mosaic/core';
export interface McTreeOptionParentComponent {
multiple: boolean;
selectionModel: SelectionModel<any>;
setSelectedOption: any;
setFocusedOption: any;
}

Expand Down Expand Up @@ -45,8 +46,10 @@ let uniqueIdCounter: number = 0;

class: 'mc-tree-option',
'[class.mc-selected]': 'selected',
'[class.mc-active]': 'active',
'[class.mc-focused]': 'hasFocus',

'(focus)': 'handleFocus()',
'(blur)': 'handleBlur()',
'(click)': 'selectViaInteraction($event)'
},
changeDetection: ChangeDetectionStrategy.OnPush,
Expand Down Expand Up @@ -102,18 +105,6 @@ export class McTreeOption extends CdkTreeNode<McTreeOption> implements CanDisabl

private _selected: boolean = false;

/**
* Whether or not the option is currently active and ready to be selected.
* An active option displays styles as if it is focused, but the
* focus is actually retained somewhere else. This comes in handy
* for components like autocomplete where focus must remain on the input.
*/
get active(): boolean {
return this._active;
}

private _active = false;

get id(): string {
return this._id;
}
Expand All @@ -124,6 +115,8 @@ export class McTreeOption extends CdkTreeNode<McTreeOption> implements CanDisabl
return this.parent.multiple;
}

hasFocus: boolean = false;

constructor(
protected elementRef: ElementRef,
protected changeDetectorRef: ChangeDetectorRef,
Expand Down Expand Up @@ -151,28 +144,25 @@ export class McTreeOption extends CdkTreeNode<McTreeOption> implements CanDisabl
this.changeDetectorRef.markForCheck();
}

/**
* This method sets display styles on the option to make it appear
* active. This is used by the ActiveDescendantKeyManager so key
* events will display the proper options as active on arrow key events.
*/
setActiveStyles(): void {
if (!this._active) {
this._active = true;
handleFocus() {
if (this.disabled || this.hasFocus) { return; }

this.changeDetectorRef.markForCheck();
this.hasFocus = true;

if (this.parent.setFocusedOption) {
this.parent.setFocusedOption(this);
}
}

/**
* This method removes display styles on the option that made it appear
* active. This is used by the ActiveDescendantKeyManager so key
* events will display the proper options as active on arrow key events.
*/
setInactiveStyles(): void {
if (this._active) {
this._active = false;
this.changeDetectorRef.markForCheck();
handleBlur() {
this.hasFocus = false;
}

focus(): void {
const element = this.getHostElement();

if (typeof element.focus === 'function') {
element.focus();
}
}

Expand All @@ -186,21 +176,6 @@ export class McTreeOption extends CdkTreeNode<McTreeOption> implements CanDisabl
return 0;
}

focus(): void {
const element = this.getHostElement();

if (typeof element.focus === 'function') {
element.focus();
}
}

// todo старая реализация, нужно восстановить tree-selection
// handleClick(): void {
// if (this.disabled) { return; }
//
// this.treeSelection.setFocusedOption(this);
// }

get viewValue(): string {
// TODO: Add input property alternative for node envs.
return (this.getHostElement().textContent || '').trim();
Expand All @@ -227,8 +202,8 @@ export class McTreeOption extends CdkTreeNode<McTreeOption> implements CanDisabl
this.changeDetectorRef.markForCheck();
this.emitSelectionChangeEvent(true);

if (this.parent.setFocusedOption) {
this.parent.setFocusedOption(this, $event);
if (this.parent.setSelectedOption) {
this.parent.setSelectedOption(this, $event);
}
}
}
Expand Down
16 changes: 9 additions & 7 deletions packages/mosaic/tree/tree-selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from '@angular/core';
import { NodeDef, ViewData } from '@angular/core/esm2015/src/view';
import { ControlValueAccessor, NgControl } from '@angular/forms';
import { ActiveDescendantKeyManager } from '@ptsecurity/cdk/a11y';
import { FocusKeyManager } from '@ptsecurity/cdk/a11y';
import {
END,
ENTER,
Expand Down Expand Up @@ -97,7 +97,7 @@ export class McTreeSelection extends McTreeSelectionBaseMixin<McTreeOption>

@ContentChildren(McTreeOption) options: QueryList<McTreeOption>;

keyManager: ActiveDescendantKeyManager<McTreeOption>;
keyManager: FocusKeyManager<McTreeOption>;

selectionModel: SelectionModel<any>;

Expand Down Expand Up @@ -163,7 +163,7 @@ export class McTreeSelection extends McTreeSelectionBaseMixin<McTreeOption>
}

ngAfterContentInit(): void {
this.keyManager = new ActiveDescendantKeyManager<McTreeOption>(this.options)
this.keyManager = new FocusKeyManager<McTreeOption>(this.options)
.withVerticalOrientation(true)
.withHorizontalOrientation(null);

Expand Down Expand Up @@ -256,9 +256,7 @@ export class McTreeSelection extends McTreeSelectionBaseMixin<McTreeOption>
this.keyManager.withScrollSize(Math.floor(this.getHeight() / this.options.first.getHeight()));
}

setFocusedOption(option: McTreeOption, $event?: KeyboardEvent) {
this.keyManager.setActiveItem(option);

setSelectedOption(option: McTreeOption, $event?: KeyboardEvent) {
const withShift = $event ? hasModifierKey($event, 'shiftKey') : false;
const withCtrl = $event ? hasModifierKey($event, 'ctrlKey') : false;

Expand Down Expand Up @@ -296,11 +294,15 @@ export class McTreeSelection extends McTreeSelectionBaseMixin<McTreeOption>
}
}

setFocusedOption(option: McTreeOption): void {
this.keyManager.setActiveItem(option);
}

toggleFocusedOption(): void {
const focusedOption = this.keyManager.activeItem;

if (focusedOption) {
this.setFocusedOption(focusedOption);
this.setSelectedOption(focusedOption);
}
}

Expand Down

0 comments on commit 0e5e3b0

Please sign in to comment.