Skip to content

Commit d6853ec

Browse files
committed
feat(toolbar)!: move the AJAX chip into a menu beside Extensions; the bar keeps the page request instead of following AJAX tags.
1 parent 2101333 commit d6853ec

22 files changed

Lines changed: 902 additions & 834 deletions

‎resources/assets/dist/js/toolbar.min.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎resources/src/toolbar/drawer.js‎

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { closest, storageKey, writeStorageItem } from "./state.js";
2-
import { isInsideExtensions, shouldCloseExtensionsMenu } from "./extensions.js";
32
import {
43
focusToolbarElement,
54
focusToolbarTrigger,
65
shouldCloseToolbarDrawer,
76
} from "./focus.js";
7+
import {
8+
isInsideToolbarMenu,
9+
shouldCloseToolbarMenu,
10+
toolbarMenu,
11+
toolbarMenus,
12+
} from "./menu.js";
813
import { shouldOpenToolbarDrawer } from "./panel.js";
914
import { toolbarDrawerHeight, toolbarDrawerHeightForKey } from "./position.js";
1015
import { renderDrawer } from "./render.js";
@@ -21,12 +26,13 @@ var minimumDrawerHeight = 120;
2126

2227
/**
2328
* Owns the interactive layers of the toolbar: the panel drawer, its resize
24-
* affordances and the Extensions menu.
29+
* affordances and the bar menus (AJAX and Extensions).
2530
*
26-
* `bind()` attaches the document-level listener that dismisses the menu, and
27-
* `dispose()` releases it together with whatever a drag in flight still holds.
28-
* Shadow-root wiring stays with the render cycle: `bindDelegatedEvents()` runs
29-
* once on the skeleton and `bindEvents()` re-binds the controls each render.
31+
* `bind()` attaches the document-level listener that dismisses the open menu,
32+
* and `dispose()` releases it together with whatever a drag in flight still
33+
* holds. Shadow-root wiring stays with the render cycle:
34+
* `bindDelegatedEvents()` runs once on the skeleton and `bindEvents()` re-binds
35+
* the controls each render.
3036
*
3137
* Usage example:
3238
*
@@ -54,12 +60,14 @@ export function createToolbarDrawer(toolbar) {
5460
}
5561

5662
/**
57-
* A pointer landing anywhere but the menu dismisses it. Registered on the
58-
* document by `bind()` and released by `dispose()`, so the registration
63+
* A pointer landing anywhere but the open menu dismisses it. Registered on
64+
* the document by `bind()` and released by `dispose()`, so the registration
5965
* follows the element lifecycle.
6066
*/
61-
function extensionsPointerDown(event) {
62-
if (!toolbar.extensionsOpen) {
67+
function menuPointerDown(event) {
68+
var menu = toolbarMenu(toolbar.openMenu);
69+
70+
if (!menu) {
6371
return;
6472
}
6573

@@ -71,14 +79,14 @@ export function createToolbarDrawer(toolbar) {
7179
typeof event.composedPath === "function" ? event.composedPath() : [];
7280
var target = path.length > 0 ? path[0] : event.target;
7381

74-
if (!isInsideExtensions(target, closest)) {
75-
controller.closeExtensions(false);
82+
if (!isInsideToolbarMenu(target, menu, closest)) {
83+
controller.closeMenu(false);
7684
}
7785
}
7886

7987
controller = {
8088
/** Document handler dismissing the menu, exposed for the lifecycle tests. */
81-
extensionsPointerDown: extensionsPointerDown,
89+
menuPointerDown: menuPointerDown,
8290
/**
8391
* Applies the stored drawer height once, then republishes the resize
8492
* handle's value range.
@@ -105,23 +113,25 @@ export function createToolbarDrawer(toolbar) {
105113

106114
controller.updateResizeHandleAccessibility();
107115
},
108-
/** Starts the lifecycle: the menu answers pointers landing outside it. */
116+
/** Starts the lifecycle: the open menu answers pointers landing outside it. */
109117
bind: function () {
110-
document.addEventListener("pointerdown", extensionsPointerDown, false);
118+
document.addEventListener("pointerdown", menuPointerDown, false);
111119
},
112120
/**
113121
* Binds the shadow-root delegation installed once on the skeleton: chips
114-
* open the drawer, the Extensions toggle flips the menu, and Escape closes
115-
* exactly one layer.
122+
* open the drawer, a menu chip flips its menu, and Escape closes exactly
123+
* one layer.
116124
*/
117125
bindDelegatedEvents: function () {
118126
var root = toolbar.shadowRoot;
119127

120128
root.addEventListener("click", function (event) {
121-
if (closest(event.target, ".extensions-toggle")) {
129+
var toggle = closest(event.target, ".menu-toggle");
130+
131+
if (toggle) {
122132
event.preventDefault();
123133
event.stopPropagation();
124-
controller.toggleExtensions();
134+
controller.toggleMenu(toggle.getAttribute("data-menu"));
125135

126136
return;
127137
}
@@ -143,10 +153,10 @@ export function createToolbarDrawer(toolbar) {
143153
* The menu answers first, so a single Escape never collapses both the
144154
* menu and the drawer behind it.
145155
*/
146-
if (shouldCloseExtensionsMenu(event, toolbar.extensionsOpen)) {
156+
if (shouldCloseToolbarMenu(event, toolbar.openMenu !== null)) {
147157
event.preventDefault();
148158
event.stopPropagation();
149-
controller.closeExtensions(true);
159+
controller.closeMenu(true);
150160

151161
return;
152162
}
@@ -208,20 +218,32 @@ export function createToolbarDrawer(toolbar) {
208218
var restoreFocusUrl = toolbar.restoreFocusUrl;
209219

210220
toolbar.drawerOpen = false;
211-
toolbar.extensionsOpen = false;
221+
toolbar.openMenu = null;
212222
toolbar.restoreFocusUrl = null;
213223
toolbar.render();
214224

215225
if (!focusToolbarTrigger(toolbar.shadowRoot, restoreFocusUrl)) {
216226
focusToolbarElement(toolbar.shadowRoot, ".toggle-toolbar");
217227
}
218228
},
219-
closeExtensions: function (focusToggle) {
220-
toolbar.extensionsOpen = false;
221-
controller.syncExtensions();
229+
/**
230+
* Closes the open menu, if any, without re-rendering the bar, and returns
231+
* focus to its chip when asked to.
232+
*/
233+
closeMenu: function (focusToggle) {
234+
var name = toolbar.openMenu;
235+
var menu = toolbarMenu(name);
236+
237+
toolbar.openMenu = null;
238+
239+
if (!menu) {
240+
return;
241+
}
242+
243+
controller.syncMenu(name);
222244

223245
if (focusToggle) {
224-
focusToolbarElement(toolbar.shadowRoot, ".extensions-toggle");
246+
focusToolbarElement(toolbar.shadowRoot, menu.toggle);
225247
}
226248
},
227249
/**
@@ -232,7 +254,7 @@ export function createToolbarDrawer(toolbar) {
232254
toolbar.resizing = false;
233255
document.removeEventListener("pointermove", onPointerMove, false);
234256
document.removeEventListener("pointerup", onPointerUp, false);
235-
document.removeEventListener("pointerdown", extensionsPointerDown, false);
257+
document.removeEventListener("pointerdown", menuPointerDown, false);
236258
},
237259
onPointerMove: function (event) {
238260
if (!toolbar.resizing) {
@@ -293,7 +315,7 @@ export function createToolbarDrawer(toolbar) {
293315

294316
toolbar.expanded = true;
295317
toolbar.drawerOpen = true;
296-
toolbar.extensionsOpen = false;
318+
toolbar.openMenu = null;
297319
toolbar.activeUrl = normalizedUrl;
298320
toolbar.restoreFocusUrl = normalizedUrl;
299321
writeStorageItem(storageKey, "1");
@@ -363,21 +385,21 @@ export function createToolbarDrawer(toolbar) {
363385

364386
toolbar.drawerPosition = position;
365387
},
366-
/** Mirrors the menu state onto the rendered wrapper and its toggle. */
367-
syncExtensions: function () {
368-
var wrapper = toolbar.shadowRoot.querySelector(".extensions");
388+
/** Mirrors the state of the named menu onto its wrapper and its chip. */
389+
syncMenu: function (name) {
390+
var menu = toolbarMenus[name];
391+
var wrapper = toolbar.shadowRoot.querySelector(menu.wrapper);
369392

370393
if (!wrapper) {
371394
return;
372395
}
373396

374-
wrapper.classList.toggle("is-open", toolbar.extensionsOpen);
397+
var open = toolbar.openMenu === name;
398+
399+
wrapper.classList.toggle("is-open", open);
375400
wrapper
376-
.querySelector(".extensions-toggle")
377-
?.setAttribute(
378-
"aria-expanded",
379-
toolbar.extensionsOpen ? "true" : "false",
380-
);
401+
.querySelector(menu.toggle)
402+
?.setAttribute("aria-expanded", open ? "true" : "false");
381403
},
382404
/** Collapses or expands the bar, persisting the choice. */
383405
toggleExpanded: function () {
@@ -390,20 +412,30 @@ export function createToolbarDrawer(toolbar) {
390412
focusToolbarElement(toolbar.shadowRoot, ".toggle-toolbar");
391413
},
392414
/**
393-
* Flips the menu without re-rendering the bar, so focus stays on the
394-
* toggle.
415+
* Flips the named menu without re-rendering the bar, so focus stays on
416+
* the chip. Opening a menu closes the other one first: only one is open at
417+
* a time.
395418
*/
396-
toggleExtensions: function () {
397-
toolbar.extensionsOpen = !toolbar.extensionsOpen;
398-
controller.syncExtensions();
419+
toggleMenu: function (name) {
420+
var previous = toolbar.openMenu;
421+
422+
if (previous === name) {
423+
controller.closeMenu(false);
399424

400-
if (!toolbar.extensionsOpen) {
401425
return;
402426
}
403427

404-
var menu = toolbar.shadowRoot.querySelector(".extensions-menu");
428+
toolbar.openMenu = name;
429+
430+
if (previous !== null) {
431+
controller.syncMenu(previous);
432+
}
433+
434+
controller.syncMenu(name);
435+
436+
var list = toolbar.shadowRoot.querySelector(toolbarMenus[name].menu);
405437

406-
menu?.querySelector("[data-debug-url], a, button")?.focus?.();
438+
list?.querySelector("[data-debug-url], a, button")?.focus?.();
407439
},
408440
/** Republishes the resize handle's value range after a height change. */
409441
updateResizeHandleAccessibility: function () {

0 commit comments

Comments
 (0)