4632ec004f7969bb979d9f4761fc1ed9fc4294a19a94408368724aae0b08a4c5bd4e4a277b693b2125c47049ccd4184ea13d19e718e12ee31998d7dad33e98 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  12. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  13. return new (P || (P = Promise))(function (resolve, reject) {
  14. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  15. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  16. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  17. step((generator = generator.apply(thisArg, _arguments || [])).next());
  18. });
  19. };
  20. import { createStyleSheet } from '../../dom.js';
  21. import { DomEmitter, stopEvent } from '../../event.js';
  22. import { StandardKeyboardEvent } from '../../keyboardEvent.js';
  23. import { Gesture } from '../../touch.js';
  24. import { alert } from '../aria/aria.js';
  25. import { CombinedSpliceable } from './splice.js';
  26. import { binarySearch, firstOrDefault, range } from '../../../common/arrays.js';
  27. import { timeout } from '../../../common/async.js';
  28. import { Color } from '../../../common/color.js';
  29. import { memoize } from '../../../common/decorators.js';
  30. import { Emitter, Event, EventBufferer } from '../../../common/event.js';
  31. import { matchesPrefix } from '../../../common/filters.js';
  32. import { DisposableStore, dispose } from '../../../common/lifecycle.js';
  33. import { clamp } from '../../../common/numbers.js';
  34. import { mixin } from '../../../common/objects.js';
  35. import * as platform from '../../../common/platform.js';
  36. import { isNumber } from '../../../common/types.js';
  37. import './list.css';
  38. import { ListError } from './list.js';
  39. import { ListView } from './listView.js';
  40. class TraitRenderer {
  41. constructor(trait) {
  42. this.trait = trait;
  43. this.renderedElements = [];
  44. }
  45. get templateId() {
  46. return `template:${this.trait.name}`;
  47. }
  48. renderTemplate(container) {
  49. return container;
  50. }
  51. renderElement(element, index, templateData) {
  52. const renderedElementIndex = this.renderedElements.findIndex(el => el.templateData === templateData);
  53. if (renderedElementIndex >= 0) {
  54. const rendered = this.renderedElements[renderedElementIndex];
  55. this.trait.unrender(templateData);
  56. rendered.index = index;
  57. }
  58. else {
  59. const rendered = { index, templateData };
  60. this.renderedElements.push(rendered);
  61. }
  62. this.trait.renderIndex(index, templateData);
  63. }
  64. splice(start, deleteCount, insertCount) {
  65. const rendered = [];
  66. for (const renderedElement of this.renderedElements) {
  67. if (renderedElement.index < start) {
  68. rendered.push(renderedElement);
  69. }
  70. else if (renderedElement.index >= start + deleteCount) {
  71. rendered.push({
  72. index: renderedElement.index + insertCount - deleteCount,
  73. templateData: renderedElement.templateData
  74. });
  75. }
  76. }
  77. this.renderedElements = rendered;
  78. }
  79. renderIndexes(indexes) {
  80. for (const { index, templateData } of this.renderedElements) {
  81. if (indexes.indexOf(index) > -1) {
  82. this.trait.renderIndex(index, templateData);
  83. }
  84. }
  85. }
  86. disposeTemplate(templateData) {
  87. const index = this.renderedElements.findIndex(el => el.templateData === templateData);
  88. if (index < 0) {
  89. return;
  90. }
  91. this.renderedElements.splice(index, 1);
  92. }
  93. }
  94. class Trait {
  95. constructor(_trait) {
  96. this._trait = _trait;
  97. this.length = 0;
  98. this.indexes = [];
  99. this.sortedIndexes = [];
  100. this._onChange = new Emitter();
  101. this.onChange = this._onChange.event;
  102. }
  103. get name() { return this._trait; }
  104. get renderer() {
  105. return new TraitRenderer(this);
  106. }
  107. splice(start, deleteCount, elements) {
  108. var _a;
  109. deleteCount = Math.max(0, Math.min(deleteCount, this.length - start));
  110. const diff = elements.length - deleteCount;
  111. const end = start + deleteCount;
  112. const sortedIndexes = [
  113. ...this.sortedIndexes.filter(i => i < start),
  114. ...elements.map((hasTrait, i) => hasTrait ? i + start : -1).filter(i => i !== -1),
  115. ...this.sortedIndexes.filter(i => i >= end).map(i => i + diff)
  116. ];
  117. const length = this.length + diff;
  118. if (this.sortedIndexes.length > 0 && sortedIndexes.length === 0 && length > 0) {
  119. const first = (_a = this.sortedIndexes.find(index => index >= start)) !== null && _a !== void 0 ? _a : length - 1;
  120. sortedIndexes.push(Math.min(first, length - 1));
  121. }
  122. this.renderer.splice(start, deleteCount, elements.length);
  123. this._set(sortedIndexes, sortedIndexes);
  124. this.length = length;
  125. }
  126. renderIndex(index, container) {
  127. container.classList.toggle(this._trait, this.contains(index));
  128. }
  129. unrender(container) {
  130. container.classList.remove(this._trait);
  131. }
  132. /**
  133. * Sets the indexes which should have this trait.
  134. *
  135. * @param indexes Indexes which should have this trait.
  136. * @return The old indexes which had this trait.
  137. */
  138. set(indexes, browserEvent) {
  139. return this._set(indexes, [...indexes].sort(numericSort), browserEvent);
  140. }
  141. _set(indexes, sortedIndexes, browserEvent) {
  142. const result = this.indexes;
  143. const sortedResult = this.sortedIndexes;
  144. this.indexes = indexes;
  145. this.sortedIndexes = sortedIndexes;
  146. const toRender = disjunction(sortedResult, indexes);
  147. this.renderer.renderIndexes(toRender);
  148. this._onChange.fire({ indexes, browserEvent });
  149. return result;
  150. }
  151. get() {
  152. return this.indexes;
  153. }
  154. contains(index) {
  155. return binarySearch(this.sortedIndexes, index, numericSort) >= 0;
  156. }
  157. dispose() {
  158. dispose(this._onChange);
  159. }
  160. }
  161. __decorate([
  162. memoize
  163. ], Trait.prototype, "renderer", null);
  164. class SelectionTrait extends Trait {
  165. constructor(setAriaSelected) {
  166. super('selected');
  167. this.setAriaSelected = setAriaSelected;
  168. }
  169. renderIndex(index, container) {
  170. super.renderIndex(index, container);
  171. if (this.setAriaSelected) {
  172. if (this.contains(index)) {
  173. container.setAttribute('aria-selected', 'true');
  174. }
  175. else {
  176. container.setAttribute('aria-selected', 'false');
  177. }
  178. }
  179. }
  180. }
  181. /**
  182. * The TraitSpliceable is used as a util class to be able
  183. * to preserve traits across splice calls, given an identity
  184. * provider.
  185. */
  186. class TraitSpliceable {
  187. constructor(trait, view, identityProvider) {
  188. this.trait = trait;
  189. this.view = view;
  190. this.identityProvider = identityProvider;
  191. }
  192. splice(start, deleteCount, elements) {
  193. if (!this.identityProvider) {
  194. return this.trait.splice(start, deleteCount, elements.map(() => false));
  195. }
  196. const pastElementsWithTrait = this.trait.get().map(i => this.identityProvider.getId(this.view.element(i)).toString());
  197. const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.identityProvider.getId(e).toString()) > -1);
  198. this.trait.splice(start, deleteCount, elementsWithTrait);
  199. }
  200. }
  201. export function isInputElement(e) {
  202. return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA';
  203. }
  204. export function isMonacoEditor(e) {
  205. if (e.classList.contains('monaco-editor')) {
  206. return true;
  207. }
  208. if (e.classList.contains('monaco-list')) {
  209. return false;
  210. }
  211. if (!e.parentElement) {
  212. return false;
  213. }
  214. return isMonacoEditor(e.parentElement);
  215. }
  216. export function isButton(e) {
  217. if ((e.tagName === 'A' && e.classList.contains('monaco-button')) ||
  218. (e.tagName === 'DIV' && e.classList.contains('monaco-button-dropdown'))) {
  219. return true;
  220. }
  221. if (e.classList.contains('monaco-list')) {
  222. return false;
  223. }
  224. if (!e.parentElement) {
  225. return false;
  226. }
  227. return isButton(e.parentElement);
  228. }
  229. class KeyboardController {
  230. constructor(list, view, options) {
  231. this.list = list;
  232. this.view = view;
  233. this.disposables = new DisposableStore();
  234. this.multipleSelectionDisposables = new DisposableStore();
  235. this.onKeyDown.filter(e => e.keyCode === 3 /* KeyCode.Enter */).on(this.onEnter, this, this.disposables);
  236. this.onKeyDown.filter(e => e.keyCode === 16 /* KeyCode.UpArrow */).on(this.onUpArrow, this, this.disposables);
  237. this.onKeyDown.filter(e => e.keyCode === 18 /* KeyCode.DownArrow */).on(this.onDownArrow, this, this.disposables);
  238. this.onKeyDown.filter(e => e.keyCode === 11 /* KeyCode.PageUp */).on(this.onPageUpArrow, this, this.disposables);
  239. this.onKeyDown.filter(e => e.keyCode === 12 /* KeyCode.PageDown */).on(this.onPageDownArrow, this, this.disposables);
  240. this.onKeyDown.filter(e => e.keyCode === 9 /* KeyCode.Escape */).on(this.onEscape, this, this.disposables);
  241. if (options.multipleSelectionSupport !== false) {
  242. this.onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === 31 /* KeyCode.KeyA */).on(this.onCtrlA, this, this.multipleSelectionDisposables);
  243. }
  244. }
  245. get onKeyDown() {
  246. return this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)
  247. .filter(e => !isInputElement(e.target))
  248. .map(e => new StandardKeyboardEvent(e)));
  249. }
  250. updateOptions(optionsUpdate) {
  251. if (optionsUpdate.multipleSelectionSupport !== undefined) {
  252. this.multipleSelectionDisposables.clear();
  253. if (optionsUpdate.multipleSelectionSupport) {
  254. this.onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === 31 /* KeyCode.KeyA */).on(this.onCtrlA, this, this.multipleSelectionDisposables);
  255. }
  256. }
  257. }
  258. onEnter(e) {
  259. e.preventDefault();
  260. e.stopPropagation();
  261. this.list.setSelection(this.list.getFocus(), e.browserEvent);
  262. }
  263. onUpArrow(e) {
  264. e.preventDefault();
  265. e.stopPropagation();
  266. this.list.focusPrevious(1, false, e.browserEvent);
  267. const el = this.list.getFocus()[0];
  268. this.list.setAnchor(el);
  269. this.list.reveal(el);
  270. this.view.domNode.focus();
  271. }
  272. onDownArrow(e) {
  273. e.preventDefault();
  274. e.stopPropagation();
  275. this.list.focusNext(1, false, e.browserEvent);
  276. const el = this.list.getFocus()[0];
  277. this.list.setAnchor(el);
  278. this.list.reveal(el);
  279. this.view.domNode.focus();
  280. }
  281. onPageUpArrow(e) {
  282. e.preventDefault();
  283. e.stopPropagation();
  284. this.list.focusPreviousPage(e.browserEvent);
  285. const el = this.list.getFocus()[0];
  286. this.list.setAnchor(el);
  287. this.list.reveal(el);
  288. this.view.domNode.focus();
  289. }
  290. onPageDownArrow(e) {
  291. e.preventDefault();
  292. e.stopPropagation();
  293. this.list.focusNextPage(e.browserEvent);
  294. const el = this.list.getFocus()[0];
  295. this.list.setAnchor(el);
  296. this.list.reveal(el);
  297. this.view.domNode.focus();
  298. }
  299. onCtrlA(e) {
  300. e.preventDefault();
  301. e.stopPropagation();
  302. this.list.setSelection(range(this.list.length), e.browserEvent);
  303. this.list.setAnchor(undefined);
  304. this.view.domNode.focus();
  305. }
  306. onEscape(e) {
  307. if (this.list.getSelection().length) {
  308. e.preventDefault();
  309. e.stopPropagation();
  310. this.list.setSelection([], e.browserEvent);
  311. this.list.setAnchor(undefined);
  312. this.view.domNode.focus();
  313. }
  314. }
  315. dispose() {
  316. this.disposables.dispose();
  317. this.multipleSelectionDisposables.dispose();
  318. }
  319. }
  320. __decorate([
  321. memoize
  322. ], KeyboardController.prototype, "onKeyDown", null);
  323. export var TypeNavigationMode;
  324. (function (TypeNavigationMode) {
  325. TypeNavigationMode[TypeNavigationMode["Automatic"] = 0] = "Automatic";
  326. TypeNavigationMode[TypeNavigationMode["Trigger"] = 1] = "Trigger";
  327. })(TypeNavigationMode || (TypeNavigationMode = {}));
  328. var TypeNavigationControllerState;
  329. (function (TypeNavigationControllerState) {
  330. TypeNavigationControllerState[TypeNavigationControllerState["Idle"] = 0] = "Idle";
  331. TypeNavigationControllerState[TypeNavigationControllerState["Typing"] = 1] = "Typing";
  332. })(TypeNavigationControllerState || (TypeNavigationControllerState = {}));
  333. export const DefaultKeyboardNavigationDelegate = new class {
  334. mightProducePrintableCharacter(event) {
  335. if (event.ctrlKey || event.metaKey || event.altKey) {
  336. return false;
  337. }
  338. return (event.keyCode >= 31 /* KeyCode.KeyA */ && event.keyCode <= 56 /* KeyCode.KeyZ */)
  339. || (event.keyCode >= 21 /* KeyCode.Digit0 */ && event.keyCode <= 30 /* KeyCode.Digit9 */)
  340. || (event.keyCode >= 93 /* KeyCode.Numpad0 */ && event.keyCode <= 102 /* KeyCode.Numpad9 */)
  341. || (event.keyCode >= 80 /* KeyCode.Semicolon */ && event.keyCode <= 90 /* KeyCode.Quote */);
  342. }
  343. };
  344. class TypeNavigationController {
  345. constructor(list, view, keyboardNavigationLabelProvider, keyboardNavigationEventFilter, delegate) {
  346. this.list = list;
  347. this.view = view;
  348. this.keyboardNavigationLabelProvider = keyboardNavigationLabelProvider;
  349. this.keyboardNavigationEventFilter = keyboardNavigationEventFilter;
  350. this.delegate = delegate;
  351. this.enabled = false;
  352. this.state = TypeNavigationControllerState.Idle;
  353. this.mode = TypeNavigationMode.Automatic;
  354. this.triggered = false;
  355. this.previouslyFocused = -1;
  356. this.enabledDisposables = new DisposableStore();
  357. this.disposables = new DisposableStore();
  358. this.updateOptions(list.options);
  359. }
  360. updateOptions(options) {
  361. var _a, _b;
  362. if ((_a = options.typeNavigationEnabled) !== null && _a !== void 0 ? _a : true) {
  363. this.enable();
  364. }
  365. else {
  366. this.disable();
  367. }
  368. this.mode = (_b = options.typeNavigationMode) !== null && _b !== void 0 ? _b : TypeNavigationMode.Automatic;
  369. }
  370. enable() {
  371. if (this.enabled) {
  372. return;
  373. }
  374. let typing = false;
  375. const onChar = this.enabledDisposables.add(Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event))
  376. .filter(e => !isInputElement(e.target))
  377. .filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered)
  378. .map(event => new StandardKeyboardEvent(event))
  379. .filter(e => typing || this.keyboardNavigationEventFilter(e))
  380. .filter(e => this.delegate.mightProducePrintableCharacter(e))
  381. .forEach(stopEvent)
  382. .map(event => event.browserEvent.key)
  383. .event;
  384. const onClear = Event.debounce(onChar, () => null, 800, undefined, undefined, this.enabledDisposables);
  385. const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i), undefined, this.enabledDisposables);
  386. onInput(this.onInput, this, this.enabledDisposables);
  387. onClear(this.onClear, this, this.enabledDisposables);
  388. onChar(() => typing = true, undefined, this.enabledDisposables);
  389. onClear(() => typing = false, undefined, this.enabledDisposables);
  390. this.enabled = true;
  391. this.triggered = false;
  392. }
  393. disable() {
  394. if (!this.enabled) {
  395. return;
  396. }
  397. this.enabledDisposables.clear();
  398. this.enabled = false;
  399. this.triggered = false;
  400. }
  401. onClear() {
  402. var _a;
  403. const focus = this.list.getFocus();
  404. if (focus.length > 0 && focus[0] === this.previouslyFocused) {
  405. // List: re-announce element on typing end since typed keys will interrupt aria label of focused element
  406. // Do not announce if there was a focus change at the end to prevent duplication https://github.com/microsoft/vscode/issues/95961
  407. const ariaLabel = (_a = this.list.options.accessibilityProvider) === null || _a === void 0 ? void 0 : _a.getAriaLabel(this.list.element(focus[0]));
  408. if (ariaLabel) {
  409. alert(ariaLabel);
  410. }
  411. }
  412. this.previouslyFocused = -1;
  413. }
  414. onInput(word) {
  415. if (!word) {
  416. this.state = TypeNavigationControllerState.Idle;
  417. this.triggered = false;
  418. return;
  419. }
  420. const focus = this.list.getFocus();
  421. const start = focus.length > 0 ? focus[0] : 0;
  422. const delta = this.state === TypeNavigationControllerState.Idle ? 1 : 0;
  423. this.state = TypeNavigationControllerState.Typing;
  424. for (let i = 0; i < this.list.length; i++) {
  425. const index = (start + i + delta) % this.list.length;
  426. const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(this.view.element(index));
  427. const labelStr = label && label.toString();
  428. if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) {
  429. this.previouslyFocused = start;
  430. this.list.setFocus([index]);
  431. this.list.reveal(index);
  432. return;
  433. }
  434. }
  435. }
  436. dispose() {
  437. this.disable();
  438. this.enabledDisposables.dispose();
  439. this.disposables.dispose();
  440. }
  441. }
  442. class DOMFocusController {
  443. constructor(list, view) {
  444. this.list = list;
  445. this.view = view;
  446. this.disposables = new DisposableStore();
  447. const onKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event))
  448. .filter(e => !isInputElement(e.target))
  449. .map(e => new StandardKeyboardEvent(e));
  450. onKeyDown.filter(e => e.keyCode === 2 /* KeyCode.Tab */ && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey)
  451. .on(this.onTab, this, this.disposables);
  452. }
  453. onTab(e) {
  454. if (e.target !== this.view.domNode) {
  455. return;
  456. }
  457. const focus = this.list.getFocus();
  458. if (focus.length === 0) {
  459. return;
  460. }
  461. const focusedDomElement = this.view.domElement(focus[0]);
  462. if (!focusedDomElement) {
  463. return;
  464. }
  465. const tabIndexElement = focusedDomElement.querySelector('[tabIndex]');
  466. if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.tabIndex === -1) {
  467. return;
  468. }
  469. const style = window.getComputedStyle(tabIndexElement);
  470. if (style.visibility === 'hidden' || style.display === 'none') {
  471. return;
  472. }
  473. e.preventDefault();
  474. e.stopPropagation();
  475. tabIndexElement.focus();
  476. }
  477. dispose() {
  478. this.disposables.dispose();
  479. }
  480. }
  481. export function isSelectionSingleChangeEvent(event) {
  482. return platform.isMacintosh ? event.browserEvent.metaKey : event.browserEvent.ctrlKey;
  483. }
  484. export function isSelectionRangeChangeEvent(event) {
  485. return event.browserEvent.shiftKey;
  486. }
  487. function isMouseRightClick(event) {
  488. return event instanceof MouseEvent && event.button === 2;
  489. }
  490. const DefaultMultipleSelectionController = {
  491. isSelectionSingleChangeEvent,
  492. isSelectionRangeChangeEvent
  493. };
  494. export class MouseController {
  495. constructor(list) {
  496. this.list = list;
  497. this.disposables = new DisposableStore();
  498. this._onPointer = new Emitter();
  499. this.onPointer = this._onPointer.event;
  500. if (list.options.multipleSelectionSupport !== false) {
  501. this.multipleSelectionController = this.list.options.multipleSelectionController || DefaultMultipleSelectionController;
  502. }
  503. this.mouseSupport = typeof list.options.mouseSupport === 'undefined' || !!list.options.mouseSupport;
  504. if (this.mouseSupport) {
  505. list.onMouseDown(this.onMouseDown, this, this.disposables);
  506. list.onContextMenu(this.onContextMenu, this, this.disposables);
  507. list.onMouseDblClick(this.onDoubleClick, this, this.disposables);
  508. list.onTouchStart(this.onMouseDown, this, this.disposables);
  509. this.disposables.add(Gesture.addTarget(list.getHTMLElement()));
  510. }
  511. Event.any(list.onMouseClick, list.onMouseMiddleClick, list.onTap)(this.onViewPointer, this, this.disposables);
  512. }
  513. updateOptions(optionsUpdate) {
  514. if (optionsUpdate.multipleSelectionSupport !== undefined) {
  515. this.multipleSelectionController = undefined;
  516. if (optionsUpdate.multipleSelectionSupport) {
  517. this.multipleSelectionController = this.list.options.multipleSelectionController || DefaultMultipleSelectionController;
  518. }
  519. }
  520. }
  521. isSelectionSingleChangeEvent(event) {
  522. if (!this.multipleSelectionController) {
  523. return false;
  524. }
  525. return this.multipleSelectionController.isSelectionSingleChangeEvent(event);
  526. }
  527. isSelectionRangeChangeEvent(event) {
  528. if (!this.multipleSelectionController) {
  529. return false;
  530. }
  531. return this.multipleSelectionController.isSelectionRangeChangeEvent(event);
  532. }
  533. isSelectionChangeEvent(event) {
  534. return this.isSelectionSingleChangeEvent(event) || this.isSelectionRangeChangeEvent(event);
  535. }
  536. onMouseDown(e) {
  537. if (isMonacoEditor(e.browserEvent.target)) {
  538. return;
  539. }
  540. if (document.activeElement !== e.browserEvent.target) {
  541. this.list.domFocus();
  542. }
  543. }
  544. onContextMenu(e) {
  545. if (isMonacoEditor(e.browserEvent.target)) {
  546. return;
  547. }
  548. const focus = typeof e.index === 'undefined' ? [] : [e.index];
  549. this.list.setFocus(focus, e.browserEvent);
  550. }
  551. onViewPointer(e) {
  552. if (!this.mouseSupport) {
  553. return;
  554. }
  555. if (isInputElement(e.browserEvent.target) || isMonacoEditor(e.browserEvent.target)) {
  556. return;
  557. }
  558. const focus = e.index;
  559. if (typeof focus === 'undefined') {
  560. this.list.setFocus([], e.browserEvent);
  561. this.list.setSelection([], e.browserEvent);
  562. this.list.setAnchor(undefined);
  563. return;
  564. }
  565. if (this.isSelectionRangeChangeEvent(e)) {
  566. return this.changeSelection(e);
  567. }
  568. if (this.isSelectionChangeEvent(e)) {
  569. return this.changeSelection(e);
  570. }
  571. this.list.setFocus([focus], e.browserEvent);
  572. this.list.setAnchor(focus);
  573. if (!isMouseRightClick(e.browserEvent)) {
  574. this.list.setSelection([focus], e.browserEvent);
  575. }
  576. this._onPointer.fire(e);
  577. }
  578. onDoubleClick(e) {
  579. if (isInputElement(e.browserEvent.target) || isMonacoEditor(e.browserEvent.target)) {
  580. return;
  581. }
  582. if (this.isSelectionChangeEvent(e)) {
  583. return;
  584. }
  585. const focus = this.list.getFocus();
  586. this.list.setSelection(focus, e.browserEvent);
  587. }
  588. changeSelection(e) {
  589. const focus = e.index;
  590. let anchor = this.list.getAnchor();
  591. if (this.isSelectionRangeChangeEvent(e)) {
  592. if (typeof anchor === 'undefined') {
  593. const currentFocus = this.list.getFocus()[0];
  594. anchor = currentFocus !== null && currentFocus !== void 0 ? currentFocus : focus;
  595. this.list.setAnchor(anchor);
  596. }
  597. const min = Math.min(anchor, focus);
  598. const max = Math.max(anchor, focus);
  599. const rangeSelection = range(min, max + 1);
  600. const selection = this.list.getSelection();
  601. const contiguousRange = getContiguousRangeContaining(disjunction(selection, [anchor]), anchor);
  602. if (contiguousRange.length === 0) {
  603. return;
  604. }
  605. const newSelection = disjunction(rangeSelection, relativeComplement(selection, contiguousRange));
  606. this.list.setSelection(newSelection, e.browserEvent);
  607. this.list.setFocus([focus], e.browserEvent);
  608. }
  609. else if (this.isSelectionSingleChangeEvent(e)) {
  610. const selection = this.list.getSelection();
  611. const newSelection = selection.filter(i => i !== focus);
  612. this.list.setFocus([focus]);
  613. this.list.setAnchor(focus);
  614. if (selection.length === newSelection.length) {
  615. this.list.setSelection([...newSelection, focus], e.browserEvent);
  616. }
  617. else {
  618. this.list.setSelection(newSelection, e.browserEvent);
  619. }
  620. }
  621. }
  622. dispose() {
  623. this.disposables.dispose();
  624. }
  625. }
  626. export class DefaultStyleController {
  627. constructor(styleElement, selectorSuffix) {
  628. this.styleElement = styleElement;
  629. this.selectorSuffix = selectorSuffix;
  630. }
  631. style(styles) {
  632. const suffix = this.selectorSuffix && `.${this.selectorSuffix}`;
  633. const content = [];
  634. if (styles.listBackground) {
  635. if (styles.listBackground.isOpaque()) {
  636. content.push(`.monaco-list${suffix} .monaco-list-rows { background: ${styles.listBackground}; }`);
  637. }
  638. else if (!platform.isMacintosh) { // subpixel AA doesn't exist in macOS
  639. console.warn(`List with id '${this.selectorSuffix}' was styled with a non-opaque background color. This will break sub-pixel antialiasing.`);
  640. }
  641. }
  642. if (styles.listFocusBackground) {
  643. content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`);
  644. content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused:hover { background-color: ${styles.listFocusBackground}; }`); // overwrite :hover style in this case!
  645. }
  646. if (styles.listFocusForeground) {
  647. content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { color: ${styles.listFocusForeground}; }`);
  648. }
  649. if (styles.listActiveSelectionBackground) {
  650. content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { background-color: ${styles.listActiveSelectionBackground}; }`);
  651. content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
  652. }
  653. if (styles.listActiveSelectionForeground) {
  654. content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { color: ${styles.listActiveSelectionForeground}; }`);
  655. }
  656. if (styles.listActiveSelectionIconForeground) {
  657. content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected .codicon { color: ${styles.listActiveSelectionIconForeground}; }`);
  658. }
  659. if (styles.listFocusAndSelectionOutline) {
  660. content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { outline-color: ${styles.listFocusAndSelectionOutline} !important; }`);
  661. }
  662. if (styles.listFocusAndSelectionBackground) {
  663. content.push(`
  664. .monaco-drag-image,
  665. .monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }
  666. `);
  667. }
  668. if (styles.listFocusAndSelectionForeground) {
  669. content.push(`
  670. .monaco-drag-image,
  671. .monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }
  672. `);
  673. }
  674. if (styles.listInactiveFocusForeground) {
  675. content.push(`.monaco-list${suffix} .monaco-list-row.focused { color: ${styles.listInactiveFocusForeground}; }`);
  676. content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { color: ${styles.listInactiveFocusForeground}; }`); // overwrite :hover style in this case!
  677. }
  678. if (styles.listInactiveSelectionIconForeground) {
  679. content.push(`.monaco-list${suffix} .monaco-list-row.focused .codicon { color: ${styles.listInactiveSelectionIconForeground}; }`);
  680. }
  681. if (styles.listInactiveFocusBackground) {
  682. content.push(`.monaco-list${suffix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
  683. content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
  684. }
  685. if (styles.listInactiveSelectionBackground) {
  686. content.push(`.monaco-list${suffix} .monaco-list-row.selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
  687. content.push(`.monaco-list${suffix} .monaco-list-row.selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
  688. }
  689. if (styles.listInactiveSelectionForeground) {
  690. content.push(`.monaco-list${suffix} .monaco-list-row.selected { color: ${styles.listInactiveSelectionForeground}; }`);
  691. }
  692. if (styles.listHoverBackground) {
  693. content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
  694. }
  695. if (styles.listHoverForeground) {
  696. content.push(`.monaco-list${suffix} .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`);
  697. }
  698. if (styles.listSelectionOutline) {
  699. content.push(`.monaco-list${suffix} .monaco-list-row.selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
  700. }
  701. if (styles.listFocusOutline) {
  702. content.push(`
  703. .monaco-drag-image,
  704. .monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }
  705. .monaco-workbench.context-menu-visible .monaco-list${suffix}.last-focused .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }
  706. `);
  707. }
  708. if (styles.listInactiveFocusOutline) {
  709. content.push(`.monaco-list${suffix} .monaco-list-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
  710. }
  711. if (styles.listHoverOutline) {
  712. content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
  713. }
  714. if (styles.listDropBackground) {
  715. content.push(`
  716. .monaco-list${suffix}.drop-target,
  717. .monaco-list${suffix} .monaco-list-rows.drop-target,
  718. .monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; }
  719. `);
  720. }
  721. if (styles.tableColumnsBorder) {
  722. content.push(`
  723. .monaco-table:hover > .monaco-split-view2,
  724. .monaco-table:hover > .monaco-split-view2 .monaco-sash.vertical::before {
  725. border-color: ${styles.tableColumnsBorder};
  726. }`);
  727. }
  728. if (styles.tableOddRowsBackgroundColor) {
  729. content.push(`
  730. .monaco-table .monaco-list-row[data-parity=odd]:not(.focused):not(.selected):not(:hover) .monaco-table-tr,
  731. .monaco-table .monaco-list:not(:focus) .monaco-list-row[data-parity=odd].focused:not(.selected):not(:hover) .monaco-table-tr,
  732. .monaco-table .monaco-list:not(.focused) .monaco-list-row[data-parity=odd].focused:not(.selected):not(:hover) .monaco-table-tr {
  733. background-color: ${styles.tableOddRowsBackgroundColor};
  734. }
  735. `);
  736. }
  737. this.styleElement.textContent = content.join('\n');
  738. }
  739. }
  740. const defaultStyles = {
  741. listFocusBackground: Color.fromHex('#7FB0D0'),
  742. listActiveSelectionBackground: Color.fromHex('#0E639C'),
  743. listActiveSelectionForeground: Color.fromHex('#FFFFFF'),
  744. listActiveSelectionIconForeground: Color.fromHex('#FFFFFF'),
  745. listFocusAndSelectionOutline: Color.fromHex('#90C2F9'),
  746. listFocusAndSelectionBackground: Color.fromHex('#094771'),
  747. listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'),
  748. listInactiveSelectionBackground: Color.fromHex('#3F3F46'),
  749. listInactiveSelectionIconForeground: Color.fromHex('#FFFFFF'),
  750. listHoverBackground: Color.fromHex('#2A2D2E'),
  751. listDropBackground: Color.fromHex('#383B3D'),
  752. treeIndentGuidesStroke: Color.fromHex('#a9a9a9'),
  753. tableColumnsBorder: Color.fromHex('#cccccc').transparent(0.2),
  754. tableOddRowsBackgroundColor: Color.fromHex('#cccccc').transparent(0.04)
  755. };
  756. const DefaultOptions = {
  757. keyboardSupport: true,
  758. mouseSupport: true,
  759. multipleSelectionSupport: true,
  760. dnd: {
  761. getDragURI() { return null; },
  762. onDragStart() { },
  763. onDragOver() { return false; },
  764. drop() { }
  765. }
  766. };
  767. // TODO@Joao: move these utils into a SortedArray class
  768. function getContiguousRangeContaining(range, value) {
  769. const index = range.indexOf(value);
  770. if (index === -1) {
  771. return [];
  772. }
  773. const result = [];
  774. let i = index - 1;
  775. while (i >= 0 && range[i] === value - (index - i)) {
  776. result.push(range[i--]);
  777. }
  778. result.reverse();
  779. i = index;
  780. while (i < range.length && range[i] === value + (i - index)) {
  781. result.push(range[i++]);
  782. }
  783. return result;
  784. }
  785. /**
  786. * Given two sorted collections of numbers, returns the intersection
  787. * between them (OR).
  788. */
  789. function disjunction(one, other) {
  790. const result = [];
  791. let i = 0, j = 0;
  792. while (i < one.length || j < other.length) {
  793. if (i >= one.length) {
  794. result.push(other[j++]);
  795. }
  796. else if (j >= other.length) {
  797. result.push(one[i++]);
  798. }
  799. else if (one[i] === other[j]) {
  800. result.push(one[i]);
  801. i++;
  802. j++;
  803. continue;
  804. }
  805. else if (one[i] < other[j]) {
  806. result.push(one[i++]);
  807. }
  808. else {
  809. result.push(other[j++]);
  810. }
  811. }
  812. return result;
  813. }
  814. /**
  815. * Given two sorted collections of numbers, returns the relative
  816. * complement between them (XOR).
  817. */
  818. function relativeComplement(one, other) {
  819. const result = [];
  820. let i = 0, j = 0;
  821. while (i < one.length || j < other.length) {
  822. if (i >= one.length) {
  823. result.push(other[j++]);
  824. }
  825. else if (j >= other.length) {
  826. result.push(one[i++]);
  827. }
  828. else if (one[i] === other[j]) {
  829. i++;
  830. j++;
  831. continue;
  832. }
  833. else if (one[i] < other[j]) {
  834. result.push(one[i++]);
  835. }
  836. else {
  837. j++;
  838. }
  839. }
  840. return result;
  841. }
  842. const numericSort = (a, b) => a - b;
  843. class PipelineRenderer {
  844. constructor(_templateId, renderers) {
  845. this._templateId = _templateId;
  846. this.renderers = renderers;
  847. }
  848. get templateId() {
  849. return this._templateId;
  850. }
  851. renderTemplate(container) {
  852. return this.renderers.map(r => r.renderTemplate(container));
  853. }
  854. renderElement(element, index, templateData, height) {
  855. let i = 0;
  856. for (const renderer of this.renderers) {
  857. renderer.renderElement(element, index, templateData[i++], height);
  858. }
  859. }
  860. disposeElement(element, index, templateData, height) {
  861. var _a;
  862. let i = 0;
  863. for (const renderer of this.renderers) {
  864. (_a = renderer.disposeElement) === null || _a === void 0 ? void 0 : _a.call(renderer, element, index, templateData[i], height);
  865. i += 1;
  866. }
  867. }
  868. disposeTemplate(templateData) {
  869. let i = 0;
  870. for (const renderer of this.renderers) {
  871. renderer.disposeTemplate(templateData[i++]);
  872. }
  873. }
  874. }
  875. class AccessibiltyRenderer {
  876. constructor(accessibilityProvider) {
  877. this.accessibilityProvider = accessibilityProvider;
  878. this.templateId = 'a18n';
  879. }
  880. renderTemplate(container) {
  881. return container;
  882. }
  883. renderElement(element, index, container) {
  884. const ariaLabel = this.accessibilityProvider.getAriaLabel(element);
  885. if (ariaLabel) {
  886. container.setAttribute('aria-label', ariaLabel);
  887. }
  888. else {
  889. container.removeAttribute('aria-label');
  890. }
  891. const ariaLevel = this.accessibilityProvider.getAriaLevel && this.accessibilityProvider.getAriaLevel(element);
  892. if (typeof ariaLevel === 'number') {
  893. container.setAttribute('aria-level', `${ariaLevel}`);
  894. }
  895. else {
  896. container.removeAttribute('aria-level');
  897. }
  898. }
  899. disposeTemplate(templateData) {
  900. // noop
  901. }
  902. }
  903. class ListViewDragAndDrop {
  904. constructor(list, dnd) {
  905. this.list = list;
  906. this.dnd = dnd;
  907. }
  908. getDragElements(element) {
  909. const selection = this.list.getSelectedElements();
  910. const elements = selection.indexOf(element) > -1 ? selection : [element];
  911. return elements;
  912. }
  913. getDragURI(element) {
  914. return this.dnd.getDragURI(element);
  915. }
  916. getDragLabel(elements, originalEvent) {
  917. if (this.dnd.getDragLabel) {
  918. return this.dnd.getDragLabel(elements, originalEvent);
  919. }
  920. return undefined;
  921. }
  922. onDragStart(data, originalEvent) {
  923. var _a, _b;
  924. (_b = (_a = this.dnd).onDragStart) === null || _b === void 0 ? void 0 : _b.call(_a, data, originalEvent);
  925. }
  926. onDragOver(data, targetElement, targetIndex, originalEvent) {
  927. return this.dnd.onDragOver(data, targetElement, targetIndex, originalEvent);
  928. }
  929. onDragLeave(data, targetElement, targetIndex, originalEvent) {
  930. var _a, _b;
  931. (_b = (_a = this.dnd).onDragLeave) === null || _b === void 0 ? void 0 : _b.call(_a, data, targetElement, targetIndex, originalEvent);
  932. }
  933. onDragEnd(originalEvent) {
  934. var _a, _b;
  935. (_b = (_a = this.dnd).onDragEnd) === null || _b === void 0 ? void 0 : _b.call(_a, originalEvent);
  936. }
  937. drop(data, targetElement, targetIndex, originalEvent) {
  938. this.dnd.drop(data, targetElement, targetIndex, originalEvent);
  939. }
  940. }
  941. /**
  942. * The {@link List} is a virtual scrolling widget, built on top of the {@link ListView}
  943. * widget.
  944. *
  945. * Features:
  946. * - Customizable keyboard and mouse support
  947. * - Element traits: focus, selection, achor
  948. * - Accessibility support
  949. * - Touch support
  950. * - Performant template-based rendering
  951. * - Horizontal scrolling
  952. * - Variable element height support
  953. * - Dynamic element height support
  954. * - Drag-and-drop support
  955. */
  956. export class List {
  957. constructor(user, container, virtualDelegate, renderers, _options = DefaultOptions) {
  958. var _a, _b, _c, _d;
  959. this.user = user;
  960. this._options = _options;
  961. this.focus = new Trait('focused');
  962. this.anchor = new Trait('anchor');
  963. this.eventBufferer = new EventBufferer();
  964. this._ariaLabel = '';
  965. this.disposables = new DisposableStore();
  966. this._onDidDispose = new Emitter();
  967. this.onDidDispose = this._onDidDispose.event;
  968. const role = this._options.accessibilityProvider && this._options.accessibilityProvider.getWidgetRole ? (_a = this._options.accessibilityProvider) === null || _a === void 0 ? void 0 : _a.getWidgetRole() : 'list';
  969. this.selection = new SelectionTrait(role !== 'listbox');
  970. mixin(_options, defaultStyles, false);
  971. const baseRenderers = [this.focus.renderer, this.selection.renderer];
  972. this.accessibilityProvider = _options.accessibilityProvider;
  973. if (this.accessibilityProvider) {
  974. baseRenderers.push(new AccessibiltyRenderer(this.accessibilityProvider));
  975. (_c = (_b = this.accessibilityProvider).onDidChangeActiveDescendant) === null || _c === void 0 ? void 0 : _c.call(_b, this.onDidChangeActiveDescendant, this, this.disposables);
  976. }
  977. renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r]));
  978. const viewOptions = Object.assign(Object.assign({}, _options), { dnd: _options.dnd && new ListViewDragAndDrop(this, _options.dnd) });
  979. this.view = new ListView(container, virtualDelegate, renderers, viewOptions);
  980. this.view.domNode.setAttribute('role', role);
  981. if (_options.styleController) {
  982. this.styleController = _options.styleController(this.view.domId);
  983. }
  984. else {
  985. const styleElement = createStyleSheet(this.view.domNode);
  986. this.styleController = new DefaultStyleController(styleElement, this.view.domId);
  987. }
  988. this.spliceable = new CombinedSpliceable([
  989. new TraitSpliceable(this.focus, this.view, _options.identityProvider),
  990. new TraitSpliceable(this.selection, this.view, _options.identityProvider),
  991. new TraitSpliceable(this.anchor, this.view, _options.identityProvider),
  992. this.view
  993. ]);
  994. this.disposables.add(this.focus);
  995. this.disposables.add(this.selection);
  996. this.disposables.add(this.anchor);
  997. this.disposables.add(this.view);
  998. this.disposables.add(this._onDidDispose);
  999. this.disposables.add(new DOMFocusController(this, this.view));
  1000. if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) {
  1001. this.keyboardController = new KeyboardController(this, this.view, _options);
  1002. this.disposables.add(this.keyboardController);
  1003. }
  1004. if (_options.keyboardNavigationLabelProvider) {
  1005. const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate;
  1006. this.typeNavigationController = new TypeNavigationController(this, this.view, _options.keyboardNavigationLabelProvider, (_d = _options.keyboardNavigationEventFilter) !== null && _d !== void 0 ? _d : (() => true), delegate);
  1007. this.disposables.add(this.typeNavigationController);
  1008. }
  1009. this.mouseController = this.createMouseController(_options);
  1010. this.disposables.add(this.mouseController);
  1011. this.onDidChangeFocus(this._onFocusChange, this, this.disposables);
  1012. this.onDidChangeSelection(this._onSelectionChange, this, this.disposables);
  1013. if (this.accessibilityProvider) {
  1014. this.ariaLabel = this.accessibilityProvider.getWidgetAriaLabel();
  1015. }
  1016. if (this._options.multipleSelectionSupport !== false) {
  1017. this.view.domNode.setAttribute('aria-multiselectable', 'true');
  1018. }
  1019. }
  1020. get onDidChangeFocus() {
  1021. return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e), this.disposables);
  1022. }
  1023. get onDidChangeSelection() {
  1024. return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e), this.disposables);
  1025. }
  1026. get domId() { return this.view.domId; }
  1027. get onMouseClick() { return this.view.onMouseClick; }
  1028. get onMouseDblClick() { return this.view.onMouseDblClick; }
  1029. get onMouseMiddleClick() { return this.view.onMouseMiddleClick; }
  1030. get onPointer() { return this.mouseController.onPointer; }
  1031. get onMouseDown() { return this.view.onMouseDown; }
  1032. get onMouseOver() { return this.view.onMouseOver; }
  1033. get onTouchStart() { return this.view.onTouchStart; }
  1034. get onTap() { return this.view.onTap; }
  1035. /**
  1036. * Possible context menu trigger events:
  1037. * - ContextMenu key
  1038. * - Shift F10
  1039. * - Ctrl Option Shift M (macOS with VoiceOver)
  1040. * - Mouse right click
  1041. */
  1042. get onContextMenu() {
  1043. let didJustPressContextMenuKey = false;
  1044. const fromKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event))
  1045. .map(e => new StandardKeyboardEvent(e))
  1046. .filter(e => didJustPressContextMenuKey = e.keyCode === 58 /* KeyCode.ContextMenu */ || (e.shiftKey && e.keyCode === 68 /* KeyCode.F10 */))
  1047. .map(stopEvent)
  1048. .filter(() => false)
  1049. .event;
  1050. const fromKeyUp = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event))
  1051. .forEach(() => didJustPressContextMenuKey = false)
  1052. .map(e => new StandardKeyboardEvent(e))
  1053. .filter(e => e.keyCode === 58 /* KeyCode.ContextMenu */ || (e.shiftKey && e.keyCode === 68 /* KeyCode.F10 */))
  1054. .map(stopEvent)
  1055. .map(({ browserEvent }) => {
  1056. const focus = this.getFocus();
  1057. const index = focus.length ? focus[0] : undefined;
  1058. const element = typeof index !== 'undefined' ? this.view.element(index) : undefined;
  1059. const anchor = typeof index !== 'undefined' ? this.view.domElement(index) : this.view.domNode;
  1060. return { index, element, anchor, browserEvent };
  1061. })
  1062. .event;
  1063. const fromMouse = this.disposables.add(Event.chain(this.view.onContextMenu))
  1064. .filter(_ => !didJustPressContextMenuKey)
  1065. .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.pageX + 1, y: browserEvent.pageY }, browserEvent }))
  1066. .event;
  1067. return Event.any(fromKeyDown, fromKeyUp, fromMouse);
  1068. }
  1069. get onKeyDown() { return this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event; }
  1070. get onDidFocus() { return Event.signal(this.disposables.add(new DomEmitter(this.view.domNode, 'focus', true)).event); }
  1071. createMouseController(options) {
  1072. return new MouseController(this);
  1073. }
  1074. updateOptions(optionsUpdate = {}) {
  1075. var _a, _b;
  1076. this._options = Object.assign(Object.assign({}, this._options), optionsUpdate);
  1077. (_a = this.typeNavigationController) === null || _a === void 0 ? void 0 : _a.updateOptions(this._options);
  1078. if (this._options.multipleSelectionController !== undefined) {
  1079. if (this._options.multipleSelectionSupport) {
  1080. this.view.domNode.setAttribute('aria-multiselectable', 'true');
  1081. }
  1082. else {
  1083. this.view.domNode.removeAttribute('aria-multiselectable');
  1084. }
  1085. }
  1086. this.mouseController.updateOptions(optionsUpdate);
  1087. (_b = this.keyboardController) === null || _b === void 0 ? void 0 : _b.updateOptions(optionsUpdate);
  1088. this.view.updateOptions(optionsUpdate);
  1089. }
  1090. get options() {
  1091. return this._options;
  1092. }
  1093. splice(start, deleteCount, elements = []) {
  1094. if (start < 0 || start > this.view.length) {
  1095. throw new ListError(this.user, `Invalid start index: ${start}`);
  1096. }
  1097. if (deleteCount < 0) {
  1098. throw new ListError(this.user, `Invalid delete count: ${deleteCount}`);
  1099. }
  1100. if (deleteCount === 0 && elements.length === 0) {
  1101. return;
  1102. }
  1103. this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements));
  1104. }
  1105. rerender() {
  1106. this.view.rerender();
  1107. }
  1108. element(index) {
  1109. return this.view.element(index);
  1110. }
  1111. get length() {
  1112. return this.view.length;
  1113. }
  1114. get contentHeight() {
  1115. return this.view.contentHeight;
  1116. }
  1117. get scrollTop() {
  1118. return this.view.getScrollTop();
  1119. }
  1120. set scrollTop(scrollTop) {
  1121. this.view.setScrollTop(scrollTop);
  1122. }
  1123. get ariaLabel() {
  1124. return this._ariaLabel;
  1125. }
  1126. set ariaLabel(value) {
  1127. this._ariaLabel = value;
  1128. this.view.domNode.setAttribute('aria-label', value);
  1129. }
  1130. domFocus() {
  1131. this.view.domNode.focus({ preventScroll: true });
  1132. }
  1133. layout(height, width) {
  1134. this.view.layout(height, width);
  1135. }
  1136. setSelection(indexes, browserEvent) {
  1137. for (const index of indexes) {
  1138. if (index < 0 || index >= this.length) {
  1139. throw new ListError(this.user, `Invalid index ${index}`);
  1140. }
  1141. }
  1142. this.selection.set(indexes, browserEvent);
  1143. }
  1144. getSelection() {
  1145. return this.selection.get();
  1146. }
  1147. getSelectedElements() {
  1148. return this.getSelection().map(i => this.view.element(i));
  1149. }
  1150. setAnchor(index) {
  1151. if (typeof index === 'undefined') {
  1152. this.anchor.set([]);
  1153. return;
  1154. }
  1155. if (index < 0 || index >= this.length) {
  1156. throw new ListError(this.user, `Invalid index ${index}`);
  1157. }
  1158. this.anchor.set([index]);
  1159. }
  1160. getAnchor() {
  1161. return firstOrDefault(this.anchor.get(), undefined);
  1162. }
  1163. getAnchorElement() {
  1164. const anchor = this.getAnchor();
  1165. return typeof anchor === 'undefined' ? undefined : this.element(anchor);
  1166. }
  1167. setFocus(indexes, browserEvent) {
  1168. for (const index of indexes) {
  1169. if (index < 0 || index >= this.length) {
  1170. throw new ListError(this.user, `Invalid index ${index}`);
  1171. }
  1172. }
  1173. this.focus.set(indexes, browserEvent);
  1174. }
  1175. focusNext(n = 1, loop = false, browserEvent, filter) {
  1176. if (this.length === 0) {
  1177. return;
  1178. }
  1179. const focus = this.focus.get();
  1180. const index = this.findNextIndex(focus.length > 0 ? focus[0] + n : 0, loop, filter);
  1181. if (index > -1) {
  1182. this.setFocus([index], browserEvent);
  1183. }
  1184. }
  1185. focusPrevious(n = 1, loop = false, browserEvent, filter) {
  1186. if (this.length === 0) {
  1187. return;
  1188. }
  1189. const focus = this.focus.get();
  1190. const index = this.findPreviousIndex(focus.length > 0 ? focus[0] - n : 0, loop, filter);
  1191. if (index > -1) {
  1192. this.setFocus([index], browserEvent);
  1193. }
  1194. }
  1195. focusNextPage(browserEvent, filter) {
  1196. return __awaiter(this, void 0, void 0, function* () {
  1197. let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight);
  1198. lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1;
  1199. const currentlyFocusedElementIndex = this.getFocus()[0];
  1200. if (currentlyFocusedElementIndex !== lastPageIndex && (currentlyFocusedElementIndex === undefined || lastPageIndex > currentlyFocusedElementIndex)) {
  1201. const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter);
  1202. if (lastGoodPageIndex > -1 && currentlyFocusedElementIndex !== lastGoodPageIndex) {
  1203. this.setFocus([lastGoodPageIndex], browserEvent);
  1204. }
  1205. else {
  1206. this.setFocus([lastPageIndex], browserEvent);
  1207. }
  1208. }
  1209. else {
  1210. const previousScrollTop = this.view.getScrollTop();
  1211. let nextpageScrollTop = previousScrollTop + this.view.renderHeight;
  1212. if (lastPageIndex > currentlyFocusedElementIndex) {
  1213. // scroll last page element to the top only if the last page element is below the focused element
  1214. nextpageScrollTop -= this.view.elementHeight(lastPageIndex);
  1215. }
  1216. this.view.setScrollTop(nextpageScrollTop);
  1217. if (this.view.getScrollTop() !== previousScrollTop) {
  1218. this.setFocus([]);
  1219. // Let the scroll event listener run
  1220. yield timeout(0);
  1221. yield this.focusNextPage(browserEvent, filter);
  1222. }
  1223. }
  1224. });
  1225. }
  1226. focusPreviousPage(browserEvent, filter) {
  1227. return __awaiter(this, void 0, void 0, function* () {
  1228. let firstPageIndex;
  1229. const scrollTop = this.view.getScrollTop();
  1230. if (scrollTop === 0) {
  1231. firstPageIndex = this.view.indexAt(scrollTop);
  1232. }
  1233. else {
  1234. firstPageIndex = this.view.indexAfter(scrollTop - 1);
  1235. }
  1236. const currentlyFocusedElementIndex = this.getFocus()[0];
  1237. if (currentlyFocusedElementIndex !== firstPageIndex && (currentlyFocusedElementIndex === undefined || currentlyFocusedElementIndex >= firstPageIndex)) {
  1238. const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter);
  1239. if (firstGoodPageIndex > -1 && currentlyFocusedElementIndex !== firstGoodPageIndex) {
  1240. this.setFocus([firstGoodPageIndex], browserEvent);
  1241. }
  1242. else {
  1243. this.setFocus([firstPageIndex], browserEvent);
  1244. }
  1245. }
  1246. else {
  1247. const previousScrollTop = scrollTop;
  1248. this.view.setScrollTop(scrollTop - this.view.renderHeight);
  1249. if (this.view.getScrollTop() !== previousScrollTop) {
  1250. this.setFocus([]);
  1251. // Let the scroll event listener run
  1252. yield timeout(0);
  1253. yield this.focusPreviousPage(browserEvent, filter);
  1254. }
  1255. }
  1256. });
  1257. }
  1258. focusLast(browserEvent, filter) {
  1259. if (this.length === 0) {
  1260. return;
  1261. }
  1262. const index = this.findPreviousIndex(this.length - 1, false, filter);
  1263. if (index > -1) {
  1264. this.setFocus([index], browserEvent);
  1265. }
  1266. }
  1267. focusFirst(browserEvent, filter) {
  1268. this.focusNth(0, browserEvent, filter);
  1269. }
  1270. focusNth(n, browserEvent, filter) {
  1271. if (this.length === 0) {
  1272. return;
  1273. }
  1274. const index = this.findNextIndex(n, false, filter);
  1275. if (index > -1) {
  1276. this.setFocus([index], browserEvent);
  1277. }
  1278. }
  1279. findNextIndex(index, loop = false, filter) {
  1280. for (let i = 0; i < this.length; i++) {
  1281. if (index >= this.length && !loop) {
  1282. return -1;
  1283. }
  1284. index = index % this.length;
  1285. if (!filter || filter(this.element(index))) {
  1286. return index;
  1287. }
  1288. index++;
  1289. }
  1290. return -1;
  1291. }
  1292. findPreviousIndex(index, loop = false, filter) {
  1293. for (let i = 0; i < this.length; i++) {
  1294. if (index < 0 && !loop) {
  1295. return -1;
  1296. }
  1297. index = (this.length + (index % this.length)) % this.length;
  1298. if (!filter || filter(this.element(index))) {
  1299. return index;
  1300. }
  1301. index--;
  1302. }
  1303. return -1;
  1304. }
  1305. getFocus() {
  1306. return this.focus.get();
  1307. }
  1308. getFocusedElements() {
  1309. return this.getFocus().map(i => this.view.element(i));
  1310. }
  1311. reveal(index, relativeTop) {
  1312. if (index < 0 || index >= this.length) {
  1313. throw new ListError(this.user, `Invalid index ${index}`);
  1314. }
  1315. const scrollTop = this.view.getScrollTop();
  1316. const elementTop = this.view.elementTop(index);
  1317. const elementHeight = this.view.elementHeight(index);
  1318. if (isNumber(relativeTop)) {
  1319. // y = mx + b
  1320. const m = elementHeight - this.view.renderHeight;
  1321. this.view.setScrollTop(m * clamp(relativeTop, 0, 1) + elementTop);
  1322. }
  1323. else {
  1324. const viewItemBottom = elementTop + elementHeight;
  1325. const scrollBottom = scrollTop + this.view.renderHeight;
  1326. if (elementTop < scrollTop && viewItemBottom >= scrollBottom) {
  1327. // The element is already overflowing the viewport, no-op
  1328. }
  1329. else if (elementTop < scrollTop || (viewItemBottom >= scrollBottom && elementHeight >= this.view.renderHeight)) {
  1330. this.view.setScrollTop(elementTop);
  1331. }
  1332. else if (viewItemBottom >= scrollBottom) {
  1333. this.view.setScrollTop(viewItemBottom - this.view.renderHeight);
  1334. }
  1335. }
  1336. }
  1337. getHTMLElement() {
  1338. return this.view.domNode;
  1339. }
  1340. getElementID(index) {
  1341. return this.view.getElementDomId(index);
  1342. }
  1343. style(styles) {
  1344. this.styleController.style(styles);
  1345. }
  1346. toListEvent({ indexes, browserEvent }) {
  1347. return { indexes, elements: indexes.map(i => this.view.element(i)), browserEvent };
  1348. }
  1349. _onFocusChange() {
  1350. const focus = this.focus.get();
  1351. this.view.domNode.classList.toggle('element-focused', focus.length > 0);
  1352. this.onDidChangeActiveDescendant();
  1353. }
  1354. onDidChangeActiveDescendant() {
  1355. var _a;
  1356. const focus = this.focus.get();
  1357. if (focus.length > 0) {
  1358. let id;
  1359. if ((_a = this.accessibilityProvider) === null || _a === void 0 ? void 0 : _a.getActiveDescendantId) {
  1360. id = this.accessibilityProvider.getActiveDescendantId(this.view.element(focus[0]));
  1361. }
  1362. this.view.domNode.setAttribute('aria-activedescendant', id || this.view.getElementDomId(focus[0]));
  1363. }
  1364. else {
  1365. this.view.domNode.removeAttribute('aria-activedescendant');
  1366. }
  1367. }
  1368. _onSelectionChange() {
  1369. const selection = this.selection.get();
  1370. this.view.domNode.classList.toggle('selection-none', selection.length === 0);
  1371. this.view.domNode.classList.toggle('selection-single', selection.length === 1);
  1372. this.view.domNode.classList.toggle('selection-multiple', selection.length > 1);
  1373. }
  1374. dispose() {
  1375. this._onDidDispose.fire();
  1376. this.disposables.dispose();
  1377. this._onDidDispose.dispose();
  1378. }
  1379. }
  1380. __decorate([
  1381. memoize
  1382. ], List.prototype, "onDidChangeFocus", null);
  1383. __decorate([
  1384. memoize
  1385. ], List.prototype, "onDidChangeSelection", null);
  1386. __decorate([
  1387. memoize
  1388. ], List.prototype, "onContextMenu", null);
  1389. __decorate([
  1390. memoize
  1391. ], List.prototype, "onKeyDown", null);
  1392. __decorate([
  1393. memoize
  1394. ], List.prototype, "onDidFocus", null);