Skip to content

fix(tree): tree does not scroll to focused item (#UIM-89) #172

Merged
merged 2 commits into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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