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 +52 -29 3.1.2 → trunk View file →
@@ -25,12 +25,22 @@
25 25 const QUICK_EDIT_ON_CLASS = 'extendify-quick-edit-on';
26 26 const AGENT_BTN_HOST_ID = 'wp-admin-bar-extendify-agent-btn';
27 27 const AGENT_SIDEBAR_ID = 'extendify-agent-sidebar';
28 28
29 +function watch(target, options, callback) {
30 + const observer = new MutationObserver(callback);
31 + observer.observe(target, options);
32 + return observer;
33 +}
34 +
29 35 function findExtendifyAgentButton() {
30 36 const host = document.getElementById(AGENT_BTN_HOST_ID);
31 37 if (!host) return null;
32 - 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;
33 43 }
34 44
35 45 function clickExtendifyAgent() {
36 46 const btn = findExtendifyAgentButton();
@@ -60,33 +70,45 @@
60 70 * the Zustand store so we don't have to share a chunk with the
61 71 * agent bundle.
62 72 */
63 73 function watchAgentSidebar(toolbar, aiBtn) {
64 - let attached = false;
65 - const observer = new MutationObserver(() => {
66 - const sidebar = document.getElementById(AGENT_SIDEBAR_ID);
67 - if (!sidebar || attached) return;
68 - attached = true;
69 - const update = () => {
70 - const open = !sidebar.hasAttribute('inert');
71 - toolbar.classList.toggle('ext-tb-agent-open', open);
72 - if (!aiBtn) return;
73 - aiBtn.inert = open;
74 - if (open) {
75 - aiBtn.setAttribute('tabindex', '-1');
76 - aiBtn.setAttribute('aria-hidden', 'true');
77 - } else {
78 - aiBtn.removeAttribute('tabindex');
79 - 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;
80 101 }
81 - };
82 - new MutationObserver(update).observe(sidebar, {
83 - attributes: true,
84 - attributeFilter: ['inert'],
85 - });
86 - update();
87 - });
88 - 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 + );
89 111 if (document.getElementById(AGENT_SIDEBAR_ID)) observer.takeRecords();
90 112 }
91 113
92 114 /**
@@ -99,12 +121,13 @@
99 121 const on = document.documentElement.classList.contains(QUICK_EDIT_ON_CLASS);
100 122 btn.setAttribute('aria-checked', on ? 'true' : 'false');
101 123 };
102 124 sync();
103 - new MutationObserver(sync).observe(document.documentElement, {
104 - attributes: true,
105 - attributeFilter: ['class'],
106 - });
125 + watch(
126 + document.documentElement,
127 + { attributes: true, attributeFilter: ['class'] },
128 + sync,
129 + );
107 130 }
108 131
109 132 function init() {
110 133 // Don't wire the toolbar inside another tool's iframe (Customizer preview,