提交 5039d4a0 编写于 作者: J Joao Moreno

strict null checks: abstractTree

上级 d51f8c7f
......@@ -50,6 +50,12 @@
"./vs/base/browser/ui/scrollbar/verticalScrollbar.ts",
"./vs/base/browser/ui/splitview/panelview.ts",
"./vs/base/browser/ui/splitview/splitview.ts",
"./vs/base/browser/ui/tree/abstractTree.ts",
// "./vs/base/browser/ui/tree/dataTree.ts",
// "./vs/base/browser/ui/tree/indexTree.ts",
// "./vs/base/browser/ui/tree/indexTreeModel.ts",
// "./vs/base/browser/ui/tree/objectTree.ts",
// "./vs/base/browser/ui/tree/objectTreeModel.ts",
"./vs/base/browser/ui/tree/tree.ts",
"./vs/base/browser/ui/widget.ts",
"./vs/base/common/json.ts",
......
......@@ -44,7 +44,7 @@ export interface IListGestureEvent<T> {
export interface IListContextMenuEvent<T> {
browserEvent: UIEvent;
element: T;
element: T | undefined;
index: number;
anchor: HTMLElement | { x: number; y: number; };
anchor: HTMLElement | { x: number; y: number; } | undefined;
}
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getOrDefault } from 'vs/base/common/objects';
import { getOrDefault2 } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import * as DOM from 'vs/base/browser/dom';
......@@ -39,7 +39,7 @@ interface IItem<T> {
element: T;
size: number;
templateId: string;
row: IRow;
row: IRow | null;
}
export interface IListViewOptions {
......@@ -48,7 +48,7 @@ export interface IListViewOptions {
setRowLineHeight?: boolean;
}
const DefaultOptions: IListViewOptions = {
const DefaultOptions = {
useShadows: true,
verticalScrollMode: ScrollbarVisibility.Auto,
setRowLineHeight: true
......@@ -105,8 +105,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
alwaysConsumeMouseWheel: true,
horizontal: ScrollbarVisibility.Hidden,
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows)
vertical: getOrDefault2(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
useShadows: getOrDefault2(options, o => o.useShadows, DefaultOptions.useShadows)
});
this._domNode.appendChild(this.scrollableElement.getDomNode());
......@@ -125,7 +125,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e));
onDragOver(this.onDragOver, this, this.disposables);
this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight);
this.setRowLineHeight = getOrDefault2(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight);
this.layout();
}
......@@ -242,7 +242,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
return this.items[index].element;
}
domElement(index: number): HTMLElement {
domElement(index: number): HTMLElement | null {
const row = this.items[index].row;
return row && row.domNode;
}
......@@ -312,18 +312,18 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
item.row = this.cache.alloc(item.templateId);
}
if (!item.row.domNode.parentElement) {
if (!item.row.domNode!.parentElement) {
if (beforeElement) {
this.rowsContainer.insertBefore(item.row.domNode, beforeElement);
this.rowsContainer.insertBefore(item.row.domNode!, beforeElement);
} else {
this.rowsContainer.appendChild(item.row.domNode);
this.rowsContainer.appendChild(item.row.domNode!);
}
}
item.row.domNode.style.height = `${item.size}px`;
item.row.domNode!.style.height = `${item.size}px`;
if (this.setRowLineHeight) {
item.row.domNode.style.lineHeight = `${item.size}px`;
item.row.domNode!.style.lineHeight = `${item.size}px`;
}
this.updateItemInDOM(item, index);
......@@ -333,11 +333,11 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
}
private updateItemInDOM(item: IItem<T>, index: number): void {
item.row.domNode.style.top = `${this.elementTop(index)}px`;
item.row.domNode.setAttribute('data-index', `${index}`);
item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
item.row.domNode.setAttribute('aria-setsize', `${this.length}`);
item.row.domNode.setAttribute('aria-posinset', `${index + 1}`);
item.row!.domNode!.style.top = `${this.elementTop(index)}px`;
item.row!.domNode!.setAttribute('data-index', `${index}`);
item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`);
item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`);
}
private removeItemFromDOM(index: number): void {
......@@ -345,10 +345,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const renderer = this.renderers.get(item.templateId);
if (renderer.disposeElement) {
renderer.disposeElement(item.element, index, item.row.templateData);
renderer.disposeElement(item.element, index, item.row!.templateData);
}
this.cache.release(item.row);
this.cache.release(item.row!);
item.row = null;
}
......@@ -388,21 +388,21 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
@memoize get onTap(): Event<IListGestureEvent<T>> { return filterEvent(mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)), e => e.index >= 0); }
private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent<T> {
const index = this.getItemIndexFromEventTarget(browserEvent.target);
const index = this.getItemIndexFromEventTarget(browserEvent.target || null);
const item = index < 0 ? undefined : this.items[index];
const element = item && item.element;
return { browserEvent, index, element };
}
private toTouchEvent(browserEvent: TouchEvent): IListTouchEvent<T> {
const index = this.getItemIndexFromEventTarget(browserEvent.target);
const index = this.getItemIndexFromEventTarget(browserEvent.target || null);
const item = index < 0 ? undefined : this.items[index];
const element = item && item.element;
return { browserEvent, index, element };
}
private toGestureEvent(browserEvent: GestureEvent): IListGestureEvent<T> {
const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget);
const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget || null);
const item = index < 0 ? undefined : this.items[index];
const element = item && item.element;
return { browserEvent, index, element };
......@@ -455,7 +455,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.dragAndDropScrollTimeout = window.setTimeout(() => {
this.cancelDragAndDropScrollInterval();
this.dragAndDropScrollTimeout = null;
this.dragAndDropScrollTimeout = -1;
}, 1000);
}
}
......@@ -463,7 +463,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private cancelDragAndDropScrollInterval(): void {
if (this.dragAndDropScrollInterval) {
window.clearInterval(this.dragAndDropScrollInterval);
this.dragAndDropScrollInterval = null;
this.dragAndDropScrollInterval = -1;
}
this.cancelDragAndDropScrollTimeout();
......@@ -472,15 +472,16 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private cancelDragAndDropScrollTimeout(): void {
if (this.dragAndDropScrollTimeout) {
window.clearTimeout(this.dragAndDropScrollTimeout);
this.dragAndDropScrollTimeout = null;
this.dragAndDropScrollTimeout = -1;
}
}
// Util
private getItemIndexFromEventTarget(target: EventTarget): number {
while (target instanceof HTMLElement && target !== this.rowsContainer) {
const element = target as HTMLElement;
private getItemIndexFromEventTarget(target: EventTarget | null): number {
let element: HTMLElement | null = target as (HTMLElement | null);
while (element instanceof HTMLElement && element !== this.rowsContainer) {
const rawIndex = element.getAttribute('data-index');
if (rawIndex) {
......@@ -491,7 +492,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
}
}
target = element.parentElement;
element = element.parentElement;
}
return -1;
......@@ -532,16 +533,14 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (item.row) {
const renderer = this.renderers.get(item.row.templateId);
renderer.disposeTemplate(item.row.templateData);
item.row = null;
}
}
this.items = null;
this.items = [];
}
if (this._domNode && this._domNode.parentElement) {
if (this._domNode && this._domNode.parentNode) {
this._domNode.parentNode.removeChild(this._domNode);
this._domNode = null;
}
this.disposables = dispose(this.disposables);
......
......@@ -180,7 +180,6 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
}
dispose() {
this.indexes = null;
this._onChange = dispose(this._onChange);
}
}
......@@ -224,8 +223,9 @@ class TraitSpliceable<T> implements ISpliceable<T> {
return this.trait.splice(start, deleteCount, elements.map(e => false));
}
const pastElementsWithTrait = this.trait.get().map(i => this.getId(this.view.element(i)));
const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.getId(e)) > -1);
const getId = this.getId;
const pastElementsWithTrait = this.trait.get().map(i => getId(this.view.element(i)));
const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(getId(e)) > -1);
this.trait.splice(start, deleteCount, elementsWithTrait);
}
......@@ -357,6 +357,11 @@ class DOMFocusController<T> implements IDisposable {
}
const focusedDomElement = this.view.domElement(focus[0]);
if (!focusedDomElement) {
return;
}
const tabIndexElement = focusedDomElement.querySelector('[tabIndex]');
if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.tabIndex === -1) {
......@@ -421,7 +426,7 @@ class MouseController<T> implements IDisposable {
.map(event => {
const index = this.list.getFocus()[0];
const element = this.view.element(index);
const anchor = this.view.domElement(index);
const anchor = this.view.domElement(index) || undefined;
return { index, element, anchor, browserEvent: event.browserEvent };
})
.event;
......@@ -436,7 +441,7 @@ class MouseController<T> implements IDisposable {
.map(browserEvent => {
const index = this.list.getFocus()[0];
const element = this.view.element(index);
const anchor = this.view.domElement(index);
const anchor = this.view.domElement(index) || undefined;
return { index, element, anchor, browserEvent };
})
.filter(({ anchor }) => !!anchor)
......@@ -974,10 +979,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.styleElement = DOM.createStyleSheet(this.view.domNode);
this.styleController = options.styleController;
if (!this.styleController) {
this.styleController = new DefaultStyleController(this.styleElement, this.idPrefix);
}
this.styleController = options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix);
this.spliceable = new CombinedSpliceable([
new TraitSpliceable(this.focus, this.view, options.identityProvider),
......@@ -987,8 +989,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.disposables = [this.focus, this.selection, this.view, this._onDidDispose];
this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null);
this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null);
this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null!);
this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null!);
this.disposables.push(new DOMFocusController(this, this.view));
......
......@@ -14,7 +14,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { ITreeModel, ITreeNode, ITreeRenderer, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
export function createComposedTreeListOptions<T, R extends { element: T }>(options?: IListOptions<T>): IListOptions<R> {
export function createComposedTreeListOptions<T, R extends { element: T }>(options?: IListOptions<T>): IListOptions<R> | undefined {
if (!options) {
return undefined;
}
......@@ -22,18 +22,20 @@ export function createComposedTreeListOptions<T, R extends { element: T }>(optio
let identityProvider: IIdentityProvider<R> | undefined = undefined;
if (options.identityProvider) {
identityProvider = el => options.identityProvider(el.element);
const ip = options.identityProvider;
identityProvider = el => ip(el.element);
}
let multipleSelectionController: IMultipleSelectionController<R> | undefined = undefined;
if (options.multipleSelectionController) {
const msc = options.multipleSelectionController;
multipleSelectionController = {
isSelectionSingleChangeEvent(e) {
return options.multipleSelectionController.isSelectionSingleChangeEvent({ ...e, element: e.element } as any);
return msc.isSelectionSingleChangeEvent({ ...e, element: e.element } as any);
},
isSelectionRangeChangeEvent(e) {
return options.multipleSelectionController.isSelectionRangeChangeEvent({ ...e, element: e.element } as any);
return msc.isSelectionRangeChangeEvent({ ...e, element: e.element } as any);
}
};
}
......@@ -41,9 +43,10 @@ export function createComposedTreeListOptions<T, R extends { element: T }>(optio
let accessibilityProvider: IAccessibilityProvider<R> | undefined = undefined;
if (options.accessibilityProvider) {
const ap = options.accessibilityProvider;
accessibilityProvider = {
getAriaLabel(e) {
return options.accessibilityProvider.getAriaLabel(e.element);
return ap.getAriaLabel(e.element);
}
};
}
......@@ -192,7 +195,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<T, TFilterData, any>[],
options?: ITreeOptions<T, TFilterData>
options: ITreeOptions<T, TFilterData> = {}
) {
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
......@@ -356,8 +359,12 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
private onMouseClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
const node = e.element;
const location = this.model.getNodeLocation(node);
if (!node) {
return;
}
const location = this.model.getNodeLocation(node);
this.model.toggleCollapsed(location);
}
......@@ -436,7 +443,5 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
dispose(): void {
this.disposables = dispose(this.disposables);
this.view.dispose();
this.view = null;
this.model = null;
}
}
\ No newline at end of file
......@@ -94,9 +94,9 @@ export interface ITreeModel<T, TFilterData, TRef> {
getNodeLocation(node: ITreeNode<T, any>): TRef;
getParentNodeLocation(location: TRef): TRef | null;
getParentElement(location: TRef): T | null;
getFirstChildElement(location: TRef): T | null;
getLastAncestorElement(location: TRef): T | null;
getParentElement(location: TRef | null): T | null;
getFirstChildElement(location: TRef | null): T | null;
getLastAncestorElement(location: TRef | null): T | null;
isCollapsed(location: TRef): boolean;
setCollapsed(location: TRef, collapsed: boolean): boolean;
......
......@@ -223,6 +223,11 @@ export function getOrDefault<T, R>(obj: T, fn: (obj: T) => R, defaultValue: R |
return typeof result === 'undefined' ? defaultValue : result;
}
export function getOrDefault2<T, R>(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R {
const result = fn(obj);
return typeof result === 'undefined' ? defaultValue : result;
}
type obj = { [key: string]: any; };
/**
* Returns an object that has keys for each value that is different in the base object. Keys
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册