提交 8c3bf7a8 编写于 作者: J Joao Moreno

fixes #68240

上级 4d8bb929
......@@ -41,6 +41,11 @@ export interface IListViewDragAndDrop<T> extends IListDragAndDrop<T> {
getDragElements(element: T): T[];
}
export interface IAriaSetProvider<T> {
getSetSize(element: T, index: number, listLength: number): number;
getPosInSet(element: T, index: number): number;
}
export interface IListViewOptions<T> {
readonly dnd?: IListViewDragAndDrop<T>;
readonly useShadows?: boolean;
......@@ -49,7 +54,7 @@ export interface IListViewOptions<T> {
readonly supportDynamicHeights?: boolean;
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly disableAriaRoles?: boolean;
readonly ariaSetProvider?: IAriaSetProvider<T>;
}
const DefaultOptions = {
......@@ -64,8 +69,7 @@ const DefaultOptions = {
onDragOver() { return false; },
drop() { }
},
horizontalScrolling: false,
disableAriaRoles: false
horizontalScrolling: false
};
export class ElementsDragAndDropData<T> implements IDragAndDropData {
......@@ -144,6 +148,9 @@ function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined):
export class ListView<T> implements ISpliceable<T>, IDisposable {
private static InstanceCount = 0;
readonly domId = `list_id_${++ListView.InstanceCount}`;
readonly domNode: HTMLElement;
private items: IItem<T>[];
......@@ -167,7 +174,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private setRowLineHeight: boolean;
private supportDynamicHeights: boolean;
private horizontalScrolling: boolean;
private disableAriaRoles: boolean;
private ariaSetProvider: IAriaSetProvider<T>;
private scrollWidth: number | undefined;
private canUseTranslate3d: boolean | undefined = undefined;
......@@ -198,7 +205,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
container: HTMLElement,
private virtualDelegate: IListVirtualDelegate<T>,
renderers: IListRenderer<any /* TODO@joao */, any>[],
options: IListViewOptions<T> = DefaultOptions
options: IListViewOptions<T> = DefaultOptions as IListViewOptions<T>
) {
if (options.horizontalScrolling && options.supportDynamicHeights) {
throw new Error('Horizontal scrolling and dynamic heights not supported simultaneously');
......@@ -219,12 +226,16 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.domNode = document.createElement('div');
this.domNode.className = 'monaco-list';
DOM.addClass(this.domNode, this.domId);
this.domNode.tabIndex = 0;
DOM.toggleClass(this.domNode, 'mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true);
this.horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling);
DOM.toggleClass(this.domNode, 'horizontal-scrolling', this.horizontalScrolling);
this.disableAriaRoles = getOrDefault(options, o => o.disableAriaRoles, DefaultOptions.disableAriaRoles);
this.ariaSetProvider = options.ariaSetProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 };
this.rowsContainer = document.createElement('div');
this.rowsContainer.className = 'monaco-list-rows';
......@@ -533,6 +544,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (!item.row) {
item.row = this.cache.alloc(item.templateId);
item.row!.domNode!.setAttribute('role', 'treeitem');
}
if (!item.row.domNode!.parentElement) {
......@@ -600,11 +612,9 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
item.row!.domNode!.setAttribute('data-index', `${index}`);
item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
if (!this.disableAriaRoles) {
item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`);
item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`);
}
item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaSetProvider.getSetSize(item.element, index, this.length)));
item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaSetProvider.getPosInSet(item.element, index)));
item.row!.domNode!.setAttribute('id', this.getElementDomId(index));
DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget);
}
......@@ -1083,6 +1093,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
return nextToLastItem.row.domNode;
}
getElementDomId(index: number): string {
return `${this.domId}_${index}`;
}
// Dispose
dispose() {
......
......@@ -17,7 +17,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from './list';
import { ListView, IListViewOptions, IListViewDragAndDrop } from './listView';
import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaSetProvider } from './listView';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
......@@ -179,16 +179,12 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
class FocusTrait<T> extends Trait<T> {
constructor(
private getDomId: (index: number) => string
) {
constructor() {
super('focused');
}
renderIndex(index: number, container: HTMLElement): void {
super.renderIndex(index, container);
container.setAttribute('role', 'treeitem');
container.setAttribute('id', this.getDomId(index));
if (this.contains(index)) {
container.setAttribute('aria-selected', 'true');
......@@ -818,7 +814,7 @@ export interface IListOptions<T> extends IListStyles {
readonly supportDynamicHeights?: boolean;
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly disableAriaRoles?: boolean;
readonly ariaSetProvider?: IAriaSetProvider<T>;
}
export interface IListStyles {
......@@ -1069,9 +1065,6 @@ export interface IListOptionsUpdate {
export class List<T> implements ISpliceable<T>, IDisposable {
private static InstanceCount = 0;
private idPrefix = `list_id_${++List.InstanceCount}`;
private focus: Trait<T>;
private selection: Trait<T>;
private eventBufferer = new EventBufferer();
......@@ -1167,7 +1160,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
renderers: IListRenderer<any /* TODO@joao */, any>[],
private _options: IListOptions<T> = DefaultOptions
) {
this.focus = new FocusTrait(i => this.getElementDomId(i));
this.focus = new FocusTrait();
this.selection = new Trait('selected');
mixin(_options, defaultStyles, false);
......@@ -1193,12 +1186,9 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.view.domNode.setAttribute('role', _options.ariaRole);
}
DOM.addClass(this.view.domNode, this.idPrefix);
this.view.domNode.tabIndex = 0;
this.styleElement = DOM.createStyleSheet(this.view.domNode);
this.styleController = _options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix);
this.styleController = _options.styleController || new DefaultStyleController(this.styleElement, this.view.domId);
this.spliceable = new CombinedSpliceable([
new TraitSpliceable(this.focus, this.view, _options.identityProvider),
......@@ -1528,10 +1518,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
return Math.abs((scrollTop - elementTop) / m);
}
private getElementDomId(index: number): string {
return `${this.idPrefix}_${index}`;
}
isDOMFocused(): boolean {
return this.view.domNode === document.activeElement;
}
......@@ -1572,7 +1558,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
const focus = this.focus.get();
if (focus.length > 0) {
this.view.domNode.setAttribute('aria-activedescendant', this.getElementDomId(focus[0]));
this.view.domNode.setAttribute('aria-activedescendant', this.view.getElementDomId(focus[0]));
} else {
this.view.domNode.removeAttribute('aria-activedescendant');
}
......
......@@ -151,7 +151,14 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
}
},
enableKeyboardNavigation: options.simpleKeyboardNavigation,
disableAriaRoles: true
ariaSetProvider: {
getSetSize(node) {
return node.parent!.visibleChildrenCount;
},
getPosInSet(node) {
return node.visibleChildIndex + 1;
}
}
};
}
......@@ -230,8 +237,6 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent;
templateData.twistie.style.marginLeft = `${indent}px`;
templateData.container.setAttribute('aria-posinset', String(node.visibleChildIndex + 1));
templateData.container.setAttribute('aria-setsize', String(node.parent!.visibleChildrenCount));
this.update(node, templateData);
this.renderer.renderElement(node, index, templateData.templateData);
......
......@@ -222,7 +222,8 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
typeof options.expandOnlyOnTwistieClick !== 'function' ? options.expandOnlyOnTwistieClick : (
e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T)
)
)
),
ariaSetProvider: undefined
};
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册