PluginProbe
Extendify / trunk
Extendify vtrunk
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | src/Toolbar/toolbar.js +64 -29 3.1.1 → trunk View file →
@@ -16,8 +16,9 @@
16 16 * so the rounded-frame layout matches what the Agent does to
17 17 * the core admin bar.
18 18 */
19 19 import { isEmbedded } from '@shared/lib/embedded-guard';
20 +import { track } from '@shared/lib/track';
20 21 import { __ } from '@wordpress/i18n';
21 22 import './toolbar.css';
22 23
23 24 const QUICK_EDIT_TOGGLE_EVENT = 'extendify-quick-edit:toggle';
@@ -24,12 +25,22 @@
24 25 const QUICK_EDIT_ON_CLASS = 'extendify-quick-edit-on';
25 26 const AGENT_BTN_HOST_ID = 'wp-admin-bar-extendify-agent-btn';
26 27 const AGENT_SIDEBAR_ID = 'extendify-agent-sidebar';
27 28
29 +function watch(target, options, callback) {
30 + const observer = new MutationObserver(callback);
31 + observer.observe(target, options);
32 + return observer;
33 +}
34 +
28 35 function findExtendifyAgentButton() {
29 36 const host = document.getElementById(AGENT_BTN_HOST_ID);
30 37 if (!host) return null;
31 - return host.querySelector('button') || host;
38 + const mounted = host.querySelector('button:not([disabled])');
39 + if (mounted) return mounted;
40 + // The stand-in never leaves the slot, so a disabled button means not-yet-mounted.
41 + if (host.querySelector('button')) return null;
42 + return host;
32 43 }
33 44
34 45 function clickExtendifyAgent() {
35 46 const btn = findExtendifyAgentButton();
@@ -59,33 +70,45 @@
59 70 * the Zustand store so we don't have to share a chunk with the
60 71 * agent bundle.
61 72 */
62 73 function watchAgentSidebar(toolbar, aiBtn) {
63 - let attached = false;
64 - const observer = new MutationObserver(() => {
65 - const sidebar = document.getElementById(AGENT_SIDEBAR_ID);
66 - if (!sidebar || attached) return;
67 - attached = true;
68 - const update = () => {
69 - const open = !sidebar.hasAttribute('inert');
70 - toolbar.classList.toggle('ext-tb-agent-open', open);
71 - if (!aiBtn) return;
72 - aiBtn.inert = open;
73 - if (open) {
74 - aiBtn.setAttribute('tabindex', '-1');
75 - aiBtn.setAttribute('aria-hidden', 'true');
76 - } else {
77 - aiBtn.removeAttribute('tabindex');
78 - aiBtn.removeAttribute('aria-hidden');
74 + const setOpen = (open) => {
75 + toolbar.classList.toggle('ext-tb-agent-open', open);
76 + if (!aiBtn) return;
77 + aiBtn.inert = open;
78 + if (open) {
79 + aiBtn.setAttribute('tabindex', '-1');
80 + aiBtn.setAttribute('aria-hidden', 'true');
81 + } else {
82 + aiBtn.removeAttribute('tabindex');
83 + aiBtn.removeAttribute('aria-hidden');
84 + }
85 + };
86 + // A resize past the mobile breakpoint replaces this node, so never latch on one.
87 + let watched = null;
88 + let inertObserver = null;
89 + const observer = watch(
90 + document.body,
91 + { childList: true, subtree: true },
92 + () => {
93 + const sidebar = document.getElementById(AGENT_SIDEBAR_ID);
94 + if (sidebar === watched) return;
95 + watched = sidebar;
96 + inertObserver?.disconnect();
97 + inertObserver = null;
98 + if (!sidebar) {
99 + setOpen(false);
100 + return;
79 101 }
80 - };
81 - new MutationObserver(update).observe(sidebar, {
82 - attributes: true,
83 - attributeFilter: ['inert'],
84 - });
85 - update();
86 - });
87 - observer.observe(document.body, { childList: true, subtree: true });
102 + const update = () => setOpen(!sidebar.hasAttribute('inert'));
103 + inertObserver = watch(
104 + sidebar,
105 + { attributes: true, attributeFilter: ['inert'] },
106 + update,
107 + );
108 + update();
109 + },
110 + );
88 111 if (document.getElementById(AGENT_SIDEBAR_ID)) observer.takeRecords();
89 112 }
90 113
91 114 /**
@@ -98,12 +121,13 @@
98 121 const on = document.documentElement.classList.contains(QUICK_EDIT_ON_CLASS);
99 122 btn.setAttribute('aria-checked', on ? 'true' : 'false');
100 123 };
101 124 sync();
102 - new MutationObserver(sync).observe(document.documentElement, {
103 - attributes: true,
104 - attributeFilter: ['class'],
105 - });
125 + watch(
126 + document.documentElement,
127 + { attributes: true, attributeFilter: ['class'] },
128 + sync,
129 + );
106 130 }
107 131
108 132 function init() {
109 133 // Don't wire the toolbar inside another tool's iframe (Customizer preview,
@@ -144,8 +168,19 @@
144 168 e.preventDefault();
145 169 window.dispatchEvent(new CustomEvent(QUICK_EDIT_TOGGLE_EVENT));
146 170 });
147 171 }
172 +
173 + toolbar
174 + .querySelector('.ext-tb-edit')
175 + ?.addEventListener('click', () =>
176 + track('toolbar_link_clicked', { target: 'block_editor' }),
177 + );
178 + toolbar
179 + .querySelector('.ext-tb-admin-link')
180 + ?.addEventListener('click', () =>
181 + track('toolbar_link_clicked', { target: 'wp_admin' }),
182 + );
148 183
149 184 watchAgentSidebar(toolbar, aiBtn);
150 185 }
151 186