[go: nahoru, domu]

blob: 64a479cfaa65cbea173a99e61a2b01af48a42e58 [file] [log] [blame]
Tim van der Lippe0830b3d2019-10-03 13:20:071// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Paul Lewis17e384e2020-01-08 15:46:515import * as Common from '../common/common.js';
Wolfgang Beyera03d3df2020-05-04 13:59:316import * as Host from '../host/host.js';
Vidal Guillermo Diazleal Ortega9b0bc5b2021-02-22 22:12:047import * as i18n from '../i18n/i18n.js';
Paul Lewis2d7d65c2020-03-16 17:26:308
Wolfgang Beyera03d3df2020-05-04 13:59:319import * as ARIAUtils from './ARIAUtils.js';
Paul Lewis9950e182019-12-16 16:06:0710import {ContextMenu} from './ContextMenu.js'; // eslint-disable-line no-unused-vars
11import {Icon} from './Icon.js';
12import {Events as TabbedPaneEvents, TabbedPane} from './TabbedPane.js';
Andres Olivares6c7c4a62020-11-20 22:23:4113import {ItemsProvider, Toolbar, ToolbarItem, ToolbarMenuButton} from './Toolbar.js'; // eslint-disable-line no-unused-vars
Sigurd Schneider23c52972020-10-13 09:31:1414import {createTextChild} from './UIUtils.js';
Simon Zünd2947ce52020-11-12 11:50:5115import {ProvidedView, TabbedViewLocation, View, ViewLocation, ViewLocationResolver} from './View.js'; // eslint-disable-line no-unused-vars
Andres Olivares786a9e72021-01-14 13:38:5416import {getRegisteredLocationResolvers, getRegisteredViewExtensions, registerLocationResolver, registerViewExtension, ViewLocationCategoryValues, ViewLocationValues, ViewPersistence, ViewRegistration} from './ViewRegistration.js';
Paul Lewis9950e182019-12-16 16:06:0717import {VBox, Widget} from './Widget.js'; // eslint-disable-line no-unused-vars
18
Simon Zündf6afbca2021-03-02 06:11:1419const UIStrings = {
Vidal Guillermo Diazleal Ortega9b0bc5b2021-02-22 22:12:0420 /**
21 *@description Aria label for the tab panel view container
22 *@example {Sensors} PH1
23 */
24 sPanel: '{PH1} panel',
25};
26const str_ = i18n.i18n.registerUIStrings('ui/ViewManager.js', UIStrings);
27const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Andres Olivares71a1ce42020-11-03 16:07:3028/**
29 * @implements {View}
30 */
Andres Olivares7b9af182020-12-02 16:09:2831export class PreRegisteredView {
Andres Olivares71a1ce42020-11-03 16:07:3032 /**
33 * @param {!ViewRegistration} viewRegistration
34 */
35 constructor(viewRegistration) {
36 /** @type {!ViewRegistration} */
37 this._viewRegistration = viewRegistration;
38 this._widgetRequested = false;
39 }
40
41 /**
42 * @override
43 */
44 title() {
Andres Olivares4ce36a52021-01-18 18:35:0545 return this._viewRegistration.title();
Andres Olivares71a1ce42020-11-03 16:07:3046 }
47
Andres Olivares2747dbf2020-12-16 15:39:3448 commandPrompt() {
Andres Olivaresf1389022021-01-25 17:09:3549 return this._viewRegistration.commandPrompt();
Andres Olivares2747dbf2020-12-16 15:39:3450 }
Andres Olivares71a1ce42020-11-03 16:07:3051 /**
52 * @override
53 */
54 isCloseable() {
55 return this._viewRegistration.persistence === ViewPersistence.CLOSEABLE;
56 }
57
58 /**
59 * @override
60 */
61 isTransient() {
62 return this._viewRegistration.persistence === ViewPersistence.TRANSIENT;
63 }
64
65 /**
66 * @override
67 */
68 viewId() {
69 return this._viewRegistration.id;
70 }
71
72 location() {
73 return this._viewRegistration.location;
74 }
75
76 order() {
77 return this._viewRegistration.order;
78 }
79
80 settings() {
81 return this._viewRegistration.settings;
82 }
83
84 tags() {
Andres Olivares344120f2020-12-07 17:43:2885 if (this._viewRegistration.tags) {
86 // Get localized keys and separate by null character to prevent fuzzy matching from matching across them.
Andres Olivares4ce36a52021-01-18 18:35:0587 return this._viewRegistration.tags.map(tag => tag()).join('\0');
Andres Olivares344120f2020-12-07 17:43:2888 }
89 return undefined;
Andres Olivares71a1ce42020-11-03 16:07:3090 }
91
Andres Olivaresbaa14da2020-11-18 00:01:5392 persistence() {
93 return this._viewRegistration.persistence;
94 }
95
Andres Olivares71a1ce42020-11-03 16:07:3096 /**
97 * @override
98 */
99 async toolbarItems() {
Andres Olivares6c7c4a62020-11-20 22:23:41100 if (this._viewRegistration.hasToolbar) {
101 return this.widget().then(widget => /** @type {!ItemsProvider} */ (/** @type {*} */ (widget)).toolbarItems());
102 }
Andres Olivares71a1ce42020-11-03 16:07:30103 return [];
104 }
105
106 /**
107 * @override
108 */
109 async widget() {
110 this._widgetRequested = true;
111 return this._viewRegistration.loadView();
112 }
113
114 /**
115 * @override
116 */
117 async disposeView() {
118 if (!this._widgetRequested) {
119 return;
120 }
121
122 const widget = await this.widget();
Jan Schefflerff2592f2020-11-11 11:37:46123 await widget.ownerViewDisposed();
Andres Olivares71a1ce42020-11-03 16:07:30124 }
Andres Olivares6c7c4a62020-11-20 22:23:41125
126 experiment() {
127 return this._viewRegistration.experiment;
128 }
129
130 condition() {
131 return this._viewRegistration.condition;
132 }
Andres Olivares71a1ce42020-11-03 16:07:30133}
134
135/**
Paul Lewis75c7d0d2020-03-19 12:17:26136 * @type {!ViewManager}
137 */
138let viewManagerInstance;
139
Paul Lewis9950e182019-12-16 16:06:07140export class ViewManager {
Paul Lewis75c7d0d2020-03-19 12:17:26141 /**
142 * @private
143 */
Tim van der Lippe0830b3d2019-10-03 13:20:07144 constructor() {
Paul Lewis9950e182019-12-16 16:06:07145 /** @type {!Map<string, !View>} */
Tim van der Lippe0830b3d2019-10-03 13:20:07146 this._views = new Map();
147 /** @type {!Map<string, string>} */
148 this._locationNameByViewId = new Map();
149
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43150 // Read override setting for location
151 this._locationOverrideSetting = Common.Settings.Settings.instance().createSetting('viewsLocationOverride', {});
152 const preferredExtensionLocations = this._locationOverrideSetting.get();
153
Andres Olivaresce24d2a2021-01-25 16:57:23154 // Views may define their initial ordering within a location. When the user has not reordered, we use the
Andres Olivares71a1ce42020-11-03 16:07:30155 // default ordering as defined by the views themselves.
Andres Olivares71a1ce42020-11-03 16:07:30156
Andres Olivaresafb00412021-01-26 14:45:33157 /** @type {!Map<string, !Array<!PreRegisteredView>>} */
Andres Olivaresce24d2a2021-01-25 16:57:23158 const viewsByLocation = new Map();
Andres Olivaresafb00412021-01-26 14:45:33159 for (const view of getRegisteredViewExtensions()) {
160 const location = view.location() || 'none';
Andres Olivaresce24d2a2021-01-25 16:57:23161 const views = viewsByLocation.get(location) || [];
162 views.push(view);
163 viewsByLocation.set(location, views);
164 }
165
Andres Olivaresafb00412021-01-26 14:45:33166 /** @type {!Array<!PreRegisteredView>} */
Andres Olivaresce24d2a2021-01-25 16:57:23167 let sortedViewExtensions = [];
168 for (const views of viewsByLocation.values()) {
169 views.sort((firstView, secondView) => {
Andres Olivaresafb00412021-01-26 14:45:33170 const firstViewOrder = firstView.order();
171 const secondViewOrder = secondView.order();
Andres Olivaresce24d2a2021-01-25 16:57:23172 if (firstViewOrder && secondViewOrder) {
173 return firstViewOrder - secondViewOrder;
174 }
175 return 0;
176 });
177 sortedViewExtensions = sortedViewExtensions.concat(views);
178 }
179
Andres Olivaresafb00412021-01-26 14:45:33180 for (const view of sortedViewExtensions) {
181 const viewId = view.viewId();
182 const location = view.location();
Andres Olivaresafef3762020-12-30 20:13:01183 if (this._views.has(viewId)) {
184 throw new Error(`Duplicate view id '${viewId}'`);
185 }
Andres Olivares71a1ce42020-11-03 16:07:30186 this._views.set(viewId, view);
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43187 // Use the preferred user location if available
Andres Olivares71a1ce42020-11-03 16:07:30188 const locationName = preferredExtensionLocations[viewId] || location;
189 this._locationNameByViewId.set(viewId, locationName);
Tim van der Lippe0830b3d2019-10-03 13:20:07190 }
191 }
192
193 /**
Paul Lewis75c7d0d2020-03-19 12:17:26194 * @param {{forceNew: ?boolean}} opts
195 */
196 static instance(opts = {forceNew: null}) {
197 const {forceNew} = opts;
198 if (!viewManagerInstance || forceNew) {
199 viewManagerInstance = new ViewManager();
200 }
201
202 return viewManagerInstance;
203 }
204
205 /**
Paul Lewis9950e182019-12-16 16:06:07206 * @param {!Array<!ToolbarItem>} toolbarItems
Jack Lyncha29e3002019-11-04 19:59:20207 * @return {?Element}
Tim van der Lippe0830b3d2019-10-03 13:20:07208 */
Jack Lyncha29e3002019-11-04 19:59:20209 static _createToolbar(toolbarItems) {
Tim van der Lippe0830b3d2019-10-03 13:20:07210 if (!toolbarItems.length) {
Jack Lyncha29e3002019-11-04 19:59:20211 return null;
Tim van der Lippe0830b3d2019-10-03 13:20:07212 }
Paul Lewis9950e182019-12-16 16:06:07213 const toolbar = new Toolbar('');
Tim van der Lippe0830b3d2019-10-03 13:20:07214 for (const item of toolbarItems) {
215 toolbar.appendToolbarItem(item);
216 }
Jack Lyncha29e3002019-11-04 19:59:20217 return toolbar.element;
Tim van der Lippe0830b3d2019-10-03 13:20:07218 }
219
220 /**
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43221 * @param {string} viewId
222 * @returns {string}
223 */
224 locationNameForViewId(viewId) {
Simon Zünd01cef982020-11-13 08:29:16225 const locationName = this._locationNameByViewId.get(viewId);
226 if (!locationName) {
227 throw new Error(`No location name for view with id ${viewId}`);
228 }
229 return locationName;
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43230 }
231
232 /**
233 * Moves a view to a new location
234 * @param {string} viewId
235 * @param {string} locationName
chait pinnamaneni6bc1c122020-10-30 17:30:52236 * @param {{shouldSelectTab: (boolean), overrideSaving: (boolean)}=} options - Optional parameters for selecting tab and override saving
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43237 */
chait pinnamaneni6bc1c122020-10-30 17:30:52238 moveView(viewId, locationName, options) {
239 const defaultOptions = {shouldSelectTab: true, overrideSaving: false};
240 const {shouldSelectTab, overrideSaving} = options || defaultOptions;
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43241 if (!viewId || !locationName) {
242 return;
243 }
244
245 const view = this.view(viewId);
246 if (!view) {
247 return;
248 }
249
chait pinnamaneni6bc1c122020-10-30 17:30:52250 if (!overrideSaving) {
251 // Update the inner map of locations
252 this._locationNameByViewId.set(viewId, locationName);
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43253
chait pinnamaneni6bc1c122020-10-30 17:30:52254 // Update the settings of location overwrites
255 const locations = this._locationOverrideSetting.get();
256 locations[viewId] = locationName;
257 this._locationOverrideSetting.set(locations);
258 }
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43259
260 // Find new location and show view there
261 this.resolveLocation(locationName).then(location => {
262 if (!location) {
263 throw new Error('Move view: Could not resolve location for view: ' + viewId);
264 }
265 location._reveal();
chait pinnamaneni6bc1c122020-10-30 17:30:52266 return location.showView(view, undefined, /* userGesture*/ true, /* omitFocus*/ false, shouldSelectTab);
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43267 });
268 }
269
270 /**
Paul Lewis9950e182019-12-16 16:06:07271 * @param {!View} view
Tim van der Lippeee6e3e92020-05-13 13:01:37272 * @return {!Promise<void>}
Tim van der Lippe0830b3d2019-10-03 13:20:07273 */
274 revealView(view) {
Simon Zünd47a9a992020-11-13 07:54:13275 const location = locationForView.get(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07276 if (!location) {
277 return Promise.resolve();
278 }
279 location._reveal();
280 return location.showView(view);
281 }
282
283 /**
chait pinnamaneni6bc1c122020-10-30 17:30:52284 * Show view in location
285 * @param {string} viewId
286 * @param {string} locationName
287 * @param {boolean=} shouldSelectTab
288 */
289 showViewInLocation(viewId, locationName, shouldSelectTab = true) {
290 this.moveView(viewId, locationName, {
291 shouldSelectTab,
292 overrideSaving: true,
293 });
294 }
295
296 /**
Tim van der Lippe0830b3d2019-10-03 13:20:07297 * @param {string} viewId
Andres Olivares786a9e72021-01-14 13:38:54298 * @return {View}
Tim van der Lippe0830b3d2019-10-03 13:20:07299 */
300 view(viewId) {
Simon Zünd01cef982020-11-13 08:29:16301 const view = this._views.get(viewId);
302 if (!view) {
303 throw new Error(`No view with id ${viewId} found!`);
304 }
305 return view;
Tim van der Lippe0830b3d2019-10-03 13:20:07306 }
307
308 /**
309 * @param {string} viewId
Paul Lewis9950e182019-12-16 16:06:07310 * @return {?Widget}
Tim van der Lippe0830b3d2019-10-03 13:20:07311 */
312 materializedWidget(viewId) {
313 const view = this.view(viewId);
Simon Zünd2947ce52020-11-12 11:50:51314 if (!view) {
315 return null;
316 }
317 return widgetForView.get(view) || null;
Tim van der Lippe0830b3d2019-10-03 13:20:07318 }
319
320 /**
321 * @param {string} viewId
322 * @param {boolean=} userGesture
323 * @param {boolean=} omitFocus
Tim van der Lippeee6e3e92020-05-13 13:01:37324 * @return {!Promise<void>}
Tim van der Lippe0830b3d2019-10-03 13:20:07325 */
326 showView(viewId, userGesture, omitFocus) {
327 const view = this._views.get(viewId);
328 if (!view) {
329 console.error('Could not find view for id: \'' + viewId + '\' ' + new Error().stack);
330 return Promise.resolve();
331 }
332
333 const locationName = this._locationNameByViewId.get(viewId);
334
Simon Zünd47a9a992020-11-13 07:54:13335 const location = locationForView.get(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07336 if (location) {
337 location._reveal();
338 return location.showView(view, undefined, userGesture, omitFocus);
339 }
340
341 return this.resolveLocation(locationName).then(location => {
342 if (!location) {
343 throw new Error('Could not resolve location for view: ' + viewId);
344 }
345 location._reveal();
346 return location.showView(view, undefined, userGesture, omitFocus);
347 });
348 }
349
350 /**
351 * @param {string=} location
352 * @return {!Promise<?_Location>}
353 */
Simon Zünd01cef982020-11-13 08:29:16354 async resolveLocation(location) {
Tim van der Lippe0830b3d2019-10-03 13:20:07355 if (!location) {
356 return /** @type {!Promise<?_Location>} */ (Promise.resolve(null));
357 }
Andres Olivares786a9e72021-01-14 13:38:54358 const registeredResolvers = getRegisteredLocationResolvers().filter(resolver => resolver.name === location);
Tim van der Lippe0830b3d2019-10-03 13:20:07359
Andres Olivares1a4728f2021-02-09 23:06:26360 if (registeredResolvers.length > 1) {
Andres Olivares786a9e72021-01-14 13:38:54361 throw new Error('Duplicate resolver for location: ' + location);
Tim van der Lippe0830b3d2019-10-03 13:20:07362 }
Andres Olivares786a9e72021-01-14 13:38:54363 if (registeredResolvers.length) {
364 const resolver = /** @type {!ViewLocationResolver} */ (await registeredResolvers[0].loadResolver());
365 return /** @type {?_Location} */ (resolver.resolveLocation(location));
366 }
367 throw new Error('Unresolved location: ' + location);
Tim van der Lippe0830b3d2019-10-03 13:20:07368 }
369
370 /**
Tim van der Lippe403a88d2020-05-13 11:51:32371 * @param {function():void=} revealCallback
Tim van der Lippe0830b3d2019-10-03 13:20:07372 * @param {string=} location
373 * @param {boolean=} restoreSelection
374 * @param {boolean=} allowReorder
375 * @param {?string=} defaultTab
Paul Lewis9950e182019-12-16 16:06:07376 * @return {!TabbedViewLocation}
Tim van der Lippe0830b3d2019-10-03 13:20:07377 */
378 createTabbedLocation(revealCallback, location, restoreSelection, allowReorder, defaultTab) {
Paul Lewis9950e182019-12-16 16:06:07379 return new _TabbedLocation(this, revealCallback, location, restoreSelection, allowReorder, defaultTab);
Tim van der Lippe0830b3d2019-10-03 13:20:07380 }
381
382 /**
Tim van der Lippe403a88d2020-05-13 11:51:32383 * @param {function():void=} revealCallback
Tim van der Lippe0830b3d2019-10-03 13:20:07384 * @param {string=} location
Paul Lewis9950e182019-12-16 16:06:07385 * @return {!ViewLocation}
Tim van der Lippe0830b3d2019-10-03 13:20:07386 */
387 createStackLocation(revealCallback, location) {
388 return new _StackLocation(this, revealCallback, location);
389 }
390
391 /**
392 * @param {string} location
393 * @return {boolean}
394 */
395 hasViewsForLocation(location) {
Tim van der Lippeba0e6452021-01-07 13:46:34396 return Boolean(this._viewsForLocation(location).length);
Tim van der Lippe0830b3d2019-10-03 13:20:07397 }
398
399 /**
400 * @param {string} location
Paul Lewis9950e182019-12-16 16:06:07401 * @return {!Array<!View>}
Tim van der Lippe0830b3d2019-10-03 13:20:07402 */
403 _viewsForLocation(location) {
404 const result = [];
Simon Zünd01cef982020-11-13 08:29:16405 for (const [id, view] of this._views.entries()) {
Simon Zünd25c0c4f2020-11-30 12:38:11406 if (this._locationNameByViewId.get(id) === location) {
Simon Zünd01cef982020-11-13 08:29:16407 result.push(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07408 }
409 }
410 return result;
411 }
412}
413
Simon Zünd2947ce52020-11-12 11:50:51414/** @type {!WeakMap<!View, !Widget>} */
415const widgetForView = new WeakMap();
Tim van der Lippe0830b3d2019-10-03 13:20:07416
Paul Lewis9950e182019-12-16 16:06:07417export class ContainerWidget extends VBox {
Tim van der Lippe0830b3d2019-10-03 13:20:07418 /**
Paul Lewis9950e182019-12-16 16:06:07419 * @param {!View} view
Tim van der Lippe0830b3d2019-10-03 13:20:07420 */
421 constructor(view) {
422 super();
423 this.element.classList.add('flex-auto', 'view-container', 'overflow-auto');
424 this._view = view;
425 this.element.tabIndex = -1;
Tim van der Lippeaa76aa22020-02-14 14:38:24426 ARIAUtils.markAsTabpanel(this.element);
Vidal Guillermo Diazleal Ortega9b0bc5b2021-02-22 22:12:04427 ARIAUtils.setAccessibleName(this.element, i18nString(UIStrings.sPanel, {PH1: view.title()}));
Tim van der Lippe0830b3d2019-10-03 13:20:07428 this.setDefaultFocusedElement(this.element);
429 }
430
431 /**
Tim van der Lippeee6e3e92020-05-13 13:01:37432 * @return {!Promise<*>}
Tim van der Lippe0830b3d2019-10-03 13:20:07433 */
434 _materialize() {
435 if (this._materializePromise) {
436 return this._materializePromise;
437 }
438 const promises = [];
439 // TODO(crbug.com/1006759): Transform to async-await
Jack Lyncha29e3002019-11-04 19:59:20440 promises.push(this._view.toolbarItems().then(toolbarItems => {
Paul Lewis9950e182019-12-16 16:06:07441 const toolbarElement = ViewManager._createToolbar(toolbarItems);
Jack Lyncha29e3002019-11-04 19:59:20442 if (toolbarElement) {
443 this.element.insertBefore(toolbarElement, this.element.firstChild);
444 }
445 }));
Tim van der Lippe0830b3d2019-10-03 13:20:07446 promises.push(this._view.widget().then(widget => {
447 // Move focus from |this| to loaded |widget| if any.
448 const shouldFocus = this.element.hasFocus();
449 this.setDefaultFocusedElement(null);
Simon Zünd2947ce52020-11-12 11:50:51450 widgetForView.set(this._view, widget);
Tim van der Lippe0830b3d2019-10-03 13:20:07451 widget.show(this.element);
452 if (shouldFocus) {
453 widget.focus();
454 }
455 }));
456 this._materializePromise = Promise.all(promises);
457 return this._materializePromise;
458 }
459
460 /**
461 * @override
462 */
463 wasShown() {
464 this._materialize().then(() => {
Simon Zünd2947ce52020-11-12 11:50:51465 const widget = widgetForView.get(this._view);
466 if (widget) {
467 widget.show(this.element);
468 this._wasShownForTest();
469 }
Tim van der Lippe0830b3d2019-10-03 13:20:07470 });
471 }
472
473 _wasShownForTest() {
474 // This method is sniffed in tests.
475 }
476}
477
Paul Lewis9950e182019-12-16 16:06:07478export class _ExpandableContainerWidget extends VBox {
Tim van der Lippe0830b3d2019-10-03 13:20:07479 /**
Paul Lewis9950e182019-12-16 16:06:07480 * @param {!View} view
Tim van der Lippe0830b3d2019-10-03 13:20:07481 */
482 constructor(view) {
483 super(true);
484 this.element.classList.add('flex-none');
Jack Franklin71519f82020-11-03 12:08:59485 this.registerRequiredCSS('ui/viewContainers.css', {enableLegacyPatching: true});
Tim van der Lippe0830b3d2019-10-03 13:20:07486
Tim van der Lippef49e2322020-05-01 15:03:09487 this._titleElement = document.createElement('div');
488 this._titleElement.classList.add('expandable-view-title');
Tim van der Lippeaa76aa22020-02-14 14:38:24489 ARIAUtils.markAsButton(this._titleElement);
Paul Lewis9950e182019-12-16 16:06:07490 this._titleExpandIcon = Icon.create('smallicon-triangle-right', 'title-expand-icon');
Tim van der Lippe0830b3d2019-10-03 13:20:07491 this._titleElement.appendChild(this._titleExpandIcon);
Jack Lynch202351c2019-10-17 18:31:15492 const titleText = view.title();
Sigurd Schneider23c52972020-10-13 09:31:14493 createTextChild(this._titleElement, titleText);
Tim van der Lippeaa76aa22020-02-14 14:38:24494 ARIAUtils.setAccessibleName(this._titleElement, titleText);
495 ARIAUtils.setExpanded(this._titleElement, false);
Tim van der Lippe0830b3d2019-10-03 13:20:07496 this._titleElement.tabIndex = 0;
Jack Lynchf53ecf52019-11-04 19:57:08497 self.onInvokeElement(this._titleElement, this._toggleExpanded.bind(this));
Tim van der Lippe0830b3d2019-10-03 13:20:07498 this._titleElement.addEventListener('keydown', this._onTitleKeyDown.bind(this), false);
499 this.contentElement.insertBefore(this._titleElement, this.contentElement.firstChild);
500
Tim van der Lippeaa76aa22020-02-14 14:38:24501 ARIAUtils.setControls(this._titleElement, this.contentElement.createChild('slot'));
Tim van der Lippe0830b3d2019-10-03 13:20:07502 this._view = view;
Simon Zünd47a9a992020-11-13 07:54:13503 expandableContainerForView.set(view, this);
Tim van der Lippe0830b3d2019-10-03 13:20:07504 }
505
506 /**
Jack Lynch0a882882020-01-13 16:42:18507 * @override
508 */
509 wasShown() {
Simon Zünd01cef982020-11-13 08:29:16510 if (this._widget && this._materializePromise) {
Jack Lynch808557b2020-05-27 00:26:00511 this._materializePromise.then(() => {
Simon Zünd01cef982020-11-13 08:29:16512 if (this._titleElement.classList.contains('expanded') && this._widget) {
Jack Lynch808557b2020-05-27 00:26:00513 this._widget.show(this.element);
514 }
515 });
Jack Lynch0a882882020-01-13 16:42:18516 }
517 }
518
519 /**
Tim van der Lippeee6e3e92020-05-13 13:01:37520 * @return {!Promise<*>}
Tim van der Lippe0830b3d2019-10-03 13:20:07521 */
522 _materialize() {
523 if (this._materializePromise) {
524 return this._materializePromise;
525 }
526 // TODO(crbug.com/1006759): Transform to async-await
527 const promises = [];
Jack Lyncha29e3002019-11-04 19:59:20528 promises.push(this._view.toolbarItems().then(toolbarItems => {
Paul Lewis9950e182019-12-16 16:06:07529 const toolbarElement = ViewManager._createToolbar(toolbarItems);
Jack Lyncha29e3002019-11-04 19:59:20530 if (toolbarElement) {
531 this._titleElement.appendChild(toolbarElement);
532 }
533 }));
Tim van der Lippe0830b3d2019-10-03 13:20:07534 promises.push(this._view.widget().then(widget => {
535 this._widget = widget;
Simon Zünd2947ce52020-11-12 11:50:51536 widgetForView.set(this._view, widget);
Tim van der Lippe0830b3d2019-10-03 13:20:07537 widget.show(this.element);
538 }));
539 this._materializePromise = Promise.all(promises);
540 return this._materializePromise;
541 }
542
543 /**
Tim van der Lippeee6e3e92020-05-13 13:01:37544 * @return {!Promise<*>}
Tim van der Lippe0830b3d2019-10-03 13:20:07545 */
546 _expand() {
547 if (this._titleElement.classList.contains('expanded')) {
548 return this._materialize();
549 }
550 this._titleElement.classList.add('expanded');
Tim van der Lippeaa76aa22020-02-14 14:38:24551 ARIAUtils.setExpanded(this._titleElement, true);
Tim van der Lippe0830b3d2019-10-03 13:20:07552 this._titleExpandIcon.setIconType('smallicon-triangle-down');
Simon Zünd01cef982020-11-13 08:29:16553 return this._materialize().then(() => {
554 if (this._widget) {
555 this._widget.show(this.element);
556 }
557 });
Tim van der Lippe0830b3d2019-10-03 13:20:07558 }
559
560 _collapse() {
561 if (!this._titleElement.classList.contains('expanded')) {
562 return;
563 }
564 this._titleElement.classList.remove('expanded');
Tim van der Lippeaa76aa22020-02-14 14:38:24565 ARIAUtils.setExpanded(this._titleElement, false);
Tim van der Lippe0830b3d2019-10-03 13:20:07566 this._titleExpandIcon.setIconType('smallicon-triangle-right');
Simon Zünd01cef982020-11-13 08:29:16567 this._materialize().then(() => {
568 if (this._widget) {
569 this._widget.detach();
570 }
571 });
Tim van der Lippe0830b3d2019-10-03 13:20:07572 }
573
Jack Lynchf53ecf52019-11-04 19:57:08574 /**
575 * @param {!Event} event
576 */
577 _toggleExpanded(event) {
578 if (event.type === 'keydown' && event.target !== this._titleElement) {
579 return;
580 }
Tim van der Lippe0830b3d2019-10-03 13:20:07581 if (this._titleElement.classList.contains('expanded')) {
582 this._collapse();
583 } else {
584 this._expand();
585 }
586 }
587
588 /**
589 * @param {!Event} event
590 */
591 _onTitleKeyDown(event) {
Jack Lynchf53ecf52019-11-04 19:57:08592 if (event.target !== this._titleElement) {
593 return;
594 }
Simon Zünd01cef982020-11-13 08:29:16595 const keyEvent = /** @type {!KeyboardEvent} */ (event);
596 if (keyEvent.key === 'ArrowLeft') {
Tim van der Lippe0830b3d2019-10-03 13:20:07597 this._collapse();
Simon Zünd01cef982020-11-13 08:29:16598 } else if (keyEvent.key === 'ArrowRight') {
Tim van der Lippe0830b3d2019-10-03 13:20:07599 if (!this._titleElement.classList.contains('expanded')) {
600 this._expand();
601 } else if (this._widget) {
602 this._widget.focus();
603 }
604 }
605 }
606}
607
Simon Zünd47a9a992020-11-13 07:54:13608/** @type {!WeakMap<!View, !_ExpandableContainerWidget>} */
609const expandableContainerForView = new WeakMap();
Tim van der Lippe0830b3d2019-10-03 13:20:07610
Tim van der Lippec96ccd92019-11-29 16:23:54611class _Location {
Tim van der Lippe0830b3d2019-10-03 13:20:07612 /**
Paul Lewis9950e182019-12-16 16:06:07613 * @param {!ViewManager} manager
614 * @param {!Widget} widget
Tim van der Lippe403a88d2020-05-13 11:51:32615 * @param {function():void=} revealCallback
Tim van der Lippe0830b3d2019-10-03 13:20:07616 */
617 constructor(manager, widget, revealCallback) {
618 this._manager = manager;
619 this._revealCallback = revealCallback;
620 this._widget = widget;
621 }
622
623 /**
Paul Lewis9950e182019-12-16 16:06:07624 * @return {!Widget}
Tim van der Lippe0830b3d2019-10-03 13:20:07625 */
626 widget() {
627 return this._widget;
628 }
629
630 _reveal() {
631 if (this._revealCallback) {
632 this._revealCallback();
633 }
634 }
Simon Zünd01cef982020-11-13 08:29:16635
636 /**
637 * @param {!View} view
638 * @param {?View=} insertBefore
639 * @param {boolean=} userGesture
640 * @param {boolean=} omitFocus
641 * @param {boolean=} shouldSelectTab
642 * @return {!Promise<void>}
643 */
644 showView(view, insertBefore, userGesture, omitFocus, shouldSelectTab) {
645 throw new Error('not implemented');
646 }
647
648 /**
649 * @param {!View} view
650 */
651 removeView(view) {
652 throw new Error('not implemented');
653 }
Tim van der Lippe0830b3d2019-10-03 13:20:07654}
655
Simon Zünd47a9a992020-11-13 07:54:13656/** @type {!WeakMap<!View, !_Location>} */
657const locationForView = new WeakMap();
Tim van der Lippe0830b3d2019-10-03 13:20:07658
659/**
Paul Lewis9950e182019-12-16 16:06:07660 * @implements {TabbedViewLocation}
Tim van der Lippe0830b3d2019-10-03 13:20:07661 */
662export class _TabbedLocation extends _Location {
663 /**
Paul Lewis9950e182019-12-16 16:06:07664 * @param {!ViewManager} manager
Tim van der Lippe403a88d2020-05-13 11:51:32665 * @param {function():void=} revealCallback
Tim van der Lippe0830b3d2019-10-03 13:20:07666 * @param {string=} location
667 * @param {boolean=} restoreSelection
668 * @param {boolean=} allowReorder
669 * @param {?string=} defaultTab
670 */
671 constructor(manager, revealCallback, location, restoreSelection, allowReorder, defaultTab) {
Paul Lewis9950e182019-12-16 16:06:07672 const tabbedPane = new TabbedPane();
Tim van der Lippe0830b3d2019-10-03 13:20:07673 if (allowReorder) {
674 tabbedPane.setAllowTabReorder(true);
675 }
676
677 super(manager, tabbedPane, revealCallback);
678 this._tabbedPane = tabbedPane;
679 this._allowReorder = allowReorder;
680
Paul Lewis9950e182019-12-16 16:06:07681 this._tabbedPane.addEventListener(TabbedPaneEvents.TabSelected, this._tabSelected, this);
682 this._tabbedPane.addEventListener(TabbedPaneEvents.TabClosed, this._tabClosed, this);
Jose Leal Chapa1c7e7fd2020-06-08 22:36:43683
Jose Leal Chapa50e7bb52020-06-19 17:03:27684 this._closeableTabSetting = Common.Settings.Settings.instance().createSetting('closeableTabs', {});
685 // As we give tabs the capability to be closed we also need to add them to the setting so they are still open
686 // until the user decide to close them
687 this._setOrUpdateCloseableTabsSetting();
688
Paul Lewis2d7d65c2020-03-16 17:26:30689 this._tabOrderSetting = Common.Settings.Settings.instance().createSetting(location + '-tabOrder', {});
Paul Lewis9950e182019-12-16 16:06:07690 this._tabbedPane.addEventListener(TabbedPaneEvents.TabOrderChanged, this._persistTabOrder, this);
Tim van der Lippe0830b3d2019-10-03 13:20:07691 if (restoreSelection) {
Paul Lewis2d7d65c2020-03-16 17:26:30692 this._lastSelectedTabSetting = Common.Settings.Settings.instance().createSetting(location + '-selectedTab', '');
Tim van der Lippe0830b3d2019-10-03 13:20:07693 }
694 this._defaultTab = defaultTab;
695
Paul Lewis9950e182019-12-16 16:06:07696 /** @type {!Map.<string, !View>} */
Tim van der Lippe0830b3d2019-10-03 13:20:07697 this._views = new Map();
698
699 if (location) {
700 this.appendApplicableItems(location);
701 }
702 }
703
Jose Leal Chapa50e7bb52020-06-19 17:03:27704 _setOrUpdateCloseableTabsSetting() {
705 // Update the setting value, we respect the closed state decided by the user
706 // and append the new tabs with value of true so they are shown open
707 const defaultOptionsForTabs = {'security': true};
708 const tabs = this._closeableTabSetting.get();
709 const newClosable = Object.assign(defaultOptionsForTabs, tabs);
710 this._closeableTabSetting.set(newClosable);
711 }
712
Tim van der Lippe0830b3d2019-10-03 13:20:07713 /**
714 * @override
Paul Lewis9950e182019-12-16 16:06:07715 * @return {!Widget}
Tim van der Lippe0830b3d2019-10-03 13:20:07716 */
717 widget() {
718 return this._tabbedPane;
719 }
720
721 /**
722 * @override
Paul Lewis9950e182019-12-16 16:06:07723 * @return {!TabbedPane}
Tim van der Lippe0830b3d2019-10-03 13:20:07724 */
725 tabbedPane() {
726 return this._tabbedPane;
727 }
728
729 /**
730 * @override
Paul Lewis9950e182019-12-16 16:06:07731 * @return {!ToolbarMenuButton}
Tim van der Lippe0830b3d2019-10-03 13:20:07732 */
733 enableMoreTabsButton() {
Paul Lewis9950e182019-12-16 16:06:07734 const moreTabsButton = new ToolbarMenuButton(this._appendTabsToMenu.bind(this));
Tim van der Lippe0830b3d2019-10-03 13:20:07735 this._tabbedPane.leftToolbar().appendToolbarItem(moreTabsButton);
736 this._tabbedPane.disableOverflowMenu();
737 return moreTabsButton;
738 }
739
740 /**
741 * @override
742 * @param {string} locationName
743 */
744 appendApplicableItems(locationName) {
745 const views = this._manager._viewsForLocation(locationName);
746 if (this._allowReorder) {
747 let i = 0;
748 const persistedOrders = this._tabOrderSetting.get();
749 const orders = new Map();
750 for (const view of views) {
Paul Lewis9950e182019-12-16 16:06:07751 orders.set(view.viewId(), persistedOrders[view.viewId()] || (++i) * _TabbedLocation.orderStep);
Tim van der Lippe0830b3d2019-10-03 13:20:07752 }
753 views.sort((a, b) => orders.get(a.viewId()) - orders.get(b.viewId()));
754 }
755
756 for (const view of views) {
757 const id = view.viewId();
758 this._views.set(id, view);
Simon Zünd47a9a992020-11-13 07:54:13759 locationForView.set(view, this);
Tim van der Lippe0830b3d2019-10-03 13:20:07760 if (view.isTransient()) {
761 continue;
762 }
763 if (!view.isCloseable()) {
764 this._appendTab(view);
765 } else if (this._closeableTabSetting.get()[id]) {
766 this._appendTab(view);
767 }
768 }
Jose Leal Chapa1f3176b2020-06-15 20:47:31769
770 // If a default tab was provided we open or select it
771 if (this._defaultTab) {
772 if (this._tabbedPane.hasTab(this._defaultTab)) {
773 // If the tabbed pane already has the tab we just have to select it
774 this._tabbedPane.selectTab(this._defaultTab);
775 } else {
776 // If the tab is not present already it can be because:
777 // it doesn't correspond to this tabbed location
778 // or because it is closed
779 const view = Array.from(this._views.values()).find(view => view.viewId() === this._defaultTab);
780 if (view) {
781 // _defaultTab is indeed part of the views for this tabbed location
782 this.showView(view);
783 }
784 }
Tim van der Lippe0830b3d2019-10-03 13:20:07785 } else if (this._lastSelectedTabSetting && this._tabbedPane.hasTab(this._lastSelectedTabSetting.get())) {
786 this._tabbedPane.selectTab(this._lastSelectedTabSetting.get());
787 }
788 }
789
790 /**
Paul Lewis9950e182019-12-16 16:06:07791 * @param {!ContextMenu} contextMenu
Tim van der Lippe0830b3d2019-10-03 13:20:07792 */
793 _appendTabsToMenu(contextMenu) {
794 const views = Array.from(this._views.values());
795 views.sort((viewa, viewb) => viewa.title().localeCompare(viewb.title()));
796 for (const view of views) {
Andres Olivaresa388aa22020-11-18 01:21:26797 const title = view.title();
Wolfgang Beyera03d3df2020-05-04 13:59:31798
799 if (view.viewId() === 'issues-pane') {
800 contextMenu.defaultSection().appendItem(title, () => {
801 Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.HamburgerMenu);
802 this.showView(view, undefined, true);
803 });
804 continue;
805 }
806
Tim van der Lippe0830b3d2019-10-03 13:20:07807 contextMenu.defaultSection().appendItem(title, this.showView.bind(this, view, undefined, true));
808 }
809 }
810
811 /**
Paul Lewis9950e182019-12-16 16:06:07812 * @param {!View} view
Tim van der Lippe0830b3d2019-10-03 13:20:07813 * @param {number=} index
814 */
815 _appendTab(view, index) {
816 this._tabbedPane.appendTab(
Paul Lewis9950e182019-12-16 16:06:07817 view.viewId(), view.title(), new ContainerWidget(view), undefined, false,
Tim van der Lippe0830b3d2019-10-03 13:20:07818 view.isCloseable() || view.isTransient(), index);
819 }
820
821 /**
822 * @override
Paul Lewis9950e182019-12-16 16:06:07823 * @param {!View} view
Tim van der Lippeaa76aa22020-02-14 14:38:24824 * @param {?View=} insertBefore
Tim van der Lippe0830b3d2019-10-03 13:20:07825 */
826 appendView(view, insertBefore) {
827 if (this._tabbedPane.hasTab(view.viewId())) {
828 return;
829 }
Simon Zünd47a9a992020-11-13 07:54:13830 const oldLocation = locationForView.get(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07831 if (oldLocation && oldLocation !== this) {
832 oldLocation.removeView(view);
833 }
Simon Zünd47a9a992020-11-13 07:54:13834 locationForView.set(view, this);
Tim van der Lippe0830b3d2019-10-03 13:20:07835 this._manager._views.set(view.viewId(), view);
836 this._views.set(view.viewId(), view);
837 let index = undefined;
838 const tabIds = this._tabbedPane.tabIds();
839 if (this._allowReorder) {
840 const orderSetting = this._tabOrderSetting.get();
841 const order = orderSetting[view.viewId()];
842 for (let i = 0; order && i < tabIds.length; ++i) {
843 if (orderSetting[tabIds[i]] && orderSetting[tabIds[i]] > order) {
844 index = i;
845 break;
846 }
847 }
848 } else if (insertBefore) {
849 for (let i = 0; i < tabIds.length; ++i) {
850 if (tabIds[i] === insertBefore.viewId()) {
851 index = i;
852 break;
853 }
854 }
855 }
856 this._appendTab(view, index);
857
858 if (view.isCloseable()) {
859 const tabs = this._closeableTabSetting.get();
860 const tabId = view.viewId();
861 if (!tabs[tabId]) {
862 tabs[tabId] = true;
863 this._closeableTabSetting.set(tabs);
864 }
865 }
866 this._persistTabOrder();
867 }
868
869 /**
870 * @override
Paul Lewis9950e182019-12-16 16:06:07871 * @param {!View} view
Tim van der Lippeaa76aa22020-02-14 14:38:24872 * @param {?View=} insertBefore
Tim van der Lippe0830b3d2019-10-03 13:20:07873 * @param {boolean=} userGesture
874 * @param {boolean=} omitFocus
chait pinnamaneni6bc1c122020-10-30 17:30:52875 * @param {boolean=} shouldSelectTab
Simon Zünd01cef982020-11-13 08:29:16876 * @return {!Promise<void>}
Tim van der Lippe0830b3d2019-10-03 13:20:07877 */
Simon Zünd01cef982020-11-13 08:29:16878 async showView(view, insertBefore, userGesture, omitFocus, shouldSelectTab = true) {
Tim van der Lippe0830b3d2019-10-03 13:20:07879 this.appendView(view, insertBefore);
chait pinnamaneni6bc1c122020-10-30 17:30:52880 if (shouldSelectTab) {
881 this._tabbedPane.selectTab(view.viewId(), userGesture);
882 }
Tim van der Lippe0830b3d2019-10-03 13:20:07883 if (!omitFocus) {
884 this._tabbedPane.focus();
885 }
Paul Lewis9950e182019-12-16 16:06:07886 const widget = /** @type {!ContainerWidget} */ (this._tabbedPane.tabView(view.viewId()));
Simon Zünd01cef982020-11-13 08:29:16887 await widget._materialize();
Tim van der Lippe0830b3d2019-10-03 13:20:07888 }
889
890 /**
Paul Lewis9950e182019-12-16 16:06:07891 * @param {!View} view
Tim van der Lippe0830b3d2019-10-03 13:20:07892 * @override
893 */
894 removeView(view) {
895 if (!this._tabbedPane.hasTab(view.viewId())) {
896 return;
897 }
898
Simon Zünd47a9a992020-11-13 07:54:13899 locationForView.delete(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07900 this._manager._views.delete(view.viewId());
901 this._tabbedPane.closeTab(view.viewId());
902 this._views.delete(view.viewId());
903 }
904
905 /**
Tim van der Lippec02a97c2020-02-14 14:39:27906 * @param {!Common.EventTarget.EventTargetEvent} event
Tim van der Lippe0830b3d2019-10-03 13:20:07907 */
908 _tabSelected(event) {
909 const tabId = /** @type {string} */ (event.data.tabId);
910 if (this._lastSelectedTabSetting && event.data['isUserGesture']) {
911 this._lastSelectedTabSetting.set(tabId);
912 }
913 }
914
915 /**
Tim van der Lippec02a97c2020-02-14 14:39:27916 * @param {!Common.EventTarget.EventTargetEvent} event
Tim van der Lippe0830b3d2019-10-03 13:20:07917 */
918 _tabClosed(event) {
919 const id = /** @type {string} */ (event.data['tabId']);
920 const tabs = this._closeableTabSetting.get();
921 if (tabs[id]) {
Jose Leal Chapa50e7bb52020-06-19 17:03:27922 tabs[id] = false;
Tim van der Lippe0830b3d2019-10-03 13:20:07923 this._closeableTabSetting.set(tabs);
924 }
Simon Zünd01cef982020-11-13 08:29:16925 const view = this._views.get(id);
926 if (view) {
927 view.disposeView();
928 }
Tim van der Lippe0830b3d2019-10-03 13:20:07929 }
930
931 _persistTabOrder() {
932 const tabIds = this._tabbedPane.tabIds();
Simon Zünd01cef982020-11-13 08:29:16933 /** @type {!Object<string, number>} */
Tim van der Lippe0830b3d2019-10-03 13:20:07934 const tabOrders = {};
935 for (let i = 0; i < tabIds.length; i++) {
Paul Lewis9950e182019-12-16 16:06:07936 tabOrders[tabIds[i]] = (i + 1) * _TabbedLocation.orderStep;
Tim van der Lippe0830b3d2019-10-03 13:20:07937 }
938
939 const oldTabOrder = this._tabOrderSetting.get();
940 const oldTabArray = Object.keys(oldTabOrder);
941 oldTabArray.sort((a, b) => oldTabOrder[a] - oldTabOrder[b]);
942 let lastOrder = 0;
943 for (const key of oldTabArray) {
944 if (key in tabOrders) {
945 lastOrder = tabOrders[key];
946 continue;
947 }
948 tabOrders[key] = ++lastOrder;
949 }
950 this._tabOrderSetting.set(tabOrders);
951 }
952}
953
954_TabbedLocation.orderStep = 10; // Keep in sync with descriptors.
955
956/**
Paul Lewis9950e182019-12-16 16:06:07957 * @implements {ViewLocation}
Tim van der Lippe0830b3d2019-10-03 13:20:07958 */
Tim van der Lippec96ccd92019-11-29 16:23:54959class _StackLocation extends _Location {
Tim van der Lippe0830b3d2019-10-03 13:20:07960 /**
Paul Lewis9950e182019-12-16 16:06:07961 * @param {!ViewManager} manager
Tim van der Lippe403a88d2020-05-13 11:51:32962 * @param {function():void=} revealCallback
Tim van der Lippe0830b3d2019-10-03 13:20:07963 * @param {string=} location
964 */
965 constructor(manager, revealCallback, location) {
Paul Lewis9950e182019-12-16 16:06:07966 const vbox = new VBox();
Tim van der Lippe0830b3d2019-10-03 13:20:07967 super(manager, vbox, revealCallback);
968 this._vbox = vbox;
969
Paul Lewis9950e182019-12-16 16:06:07970 /** @type {!Map<string, !_ExpandableContainerWidget>} */
Tim van der Lippe0830b3d2019-10-03 13:20:07971 this._expandableContainers = new Map();
972
973 if (location) {
974 this.appendApplicableItems(location);
975 }
976 }
977
978 /**
979 * @override
Paul Lewis9950e182019-12-16 16:06:07980 * @param {!View} view
Tim van der Lippeaa76aa22020-02-14 14:38:24981 * @param {?View=} insertBefore
Tim van der Lippe0830b3d2019-10-03 13:20:07982 */
983 appendView(view, insertBefore) {
Simon Zünd47a9a992020-11-13 07:54:13984 const oldLocation = locationForView.get(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07985 if (oldLocation && oldLocation !== this) {
986 oldLocation.removeView(view);
987 }
988
989 let container = this._expandableContainers.get(view.viewId());
990 if (!container) {
Simon Zünd47a9a992020-11-13 07:54:13991 locationForView.set(view, this);
Tim van der Lippe0830b3d2019-10-03 13:20:07992 this._manager._views.set(view.viewId(), view);
Paul Lewis9950e182019-12-16 16:06:07993 container = new _ExpandableContainerWidget(view);
Tim van der Lippe0830b3d2019-10-03 13:20:07994 let beforeElement = null;
995 if (insertBefore) {
Simon Zünd47a9a992020-11-13 07:54:13996 const beforeContainer = expandableContainerForView.get(insertBefore);
Tim van der Lippe0830b3d2019-10-03 13:20:07997 beforeElement = beforeContainer ? beforeContainer.element : null;
998 }
999 container.show(this._vbox.contentElement, beforeElement);
1000 this._expandableContainers.set(view.viewId(), container);
1001 }
1002 }
1003
1004 /**
1005 * @override
Paul Lewis9950e182019-12-16 16:06:071006 * @param {!View} view
Tim van der Lippeaa76aa22020-02-14 14:38:241007 * @param {?View=} insertBefore
Simon Zünd01cef982020-11-13 08:29:161008 * @return {!Promise<void>}
Tim van der Lippe0830b3d2019-10-03 13:20:071009 */
Simon Zünd01cef982020-11-13 08:29:161010 async showView(view, insertBefore) {
Tim van der Lippe0830b3d2019-10-03 13:20:071011 this.appendView(view, insertBefore);
1012 const container = this._expandableContainers.get(view.viewId());
Simon Zünd01cef982020-11-13 08:29:161013 if (container) {
1014 await container._expand();
1015 }
Tim van der Lippe0830b3d2019-10-03 13:20:071016 }
1017
1018 /**
Paul Lewis9950e182019-12-16 16:06:071019 * @param {!View} view
Tim van der Lippe0830b3d2019-10-03 13:20:071020 * @override
1021 */
1022 removeView(view) {
1023 const container = this._expandableContainers.get(view.viewId());
1024 if (!container) {
1025 return;
1026 }
1027
1028 container.detach();
1029 this._expandableContainers.delete(view.viewId());
Simon Zünd47a9a992020-11-13 07:54:131030 locationForView.delete(view);
Tim van der Lippe0830b3d2019-10-03 13:20:071031 this._manager._views.delete(view.viewId());
1032 }
1033
1034 /**
1035 * @override
1036 * @param {string} locationName
1037 */
1038 appendApplicableItems(locationName) {
1039 for (const view of this._manager._viewsForLocation(locationName)) {
1040 this.appendView(view);
1041 }
1042 }
1043}
Andres Olivares7b9af182020-12-02 16:09:281044
Andres Olivaresce24d2a2021-01-25 16:57:231045/**
1046 * @typedef {{viewId: string, view: (!ProvidedView|!PreRegisteredView), location: (string|null)}}
1047 */
1048// @ts-ignore typedef
1049export let ViewRegistry;
1050
Andres Olivares786a9e72021-01-14 13:38:541051export {
1052 ViewRegistration,
1053 ViewPersistence,
1054 getRegisteredViewExtensions,
1055 registerViewExtension,
1056 ViewLocationValues,
1057 getRegisteredLocationResolvers,
1058 registerLocationResolver,
1059 ViewLocationCategoryValues
1060};