@inboxsdk/core 0.2.29-alpha.2 → 0.2.29-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/inboxsdk.js +9 -8
- package/inboxsdk.js.map +1 -1
- package/package.json +1 -1
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.d.ts.map +1 -1
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.js +1 -2
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.d.ts +3 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.d.ts.map +1 -1
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.js +19 -12
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.d.ts +0 -4
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.d.ts.map +1 -1
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.js +8 -17
- package/src/platform-implementation-js/namespaces/app-menu.d.ts +19 -2
- package/src/platform-implementation-js/namespaces/app-menu.d.ts.map +1 -1
- package/src/platform-implementation-js/views/app-menu-item-view.d.ts.map +1 -1
- package/src/platform-implementation-js/views/app-menu-item-view.js +199 -151
- package/src/platform-implementation-js/views/collapsible-panel-view.d.ts +10 -8
- package/src/platform-implementation-js/views/collapsible-panel-view.d.ts.map +1 -1
- package/src/platform-implementation-js/views/collapsible-panel-view.js +57 -31
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import Kefir from 'kefir';
|
|
2
|
-
import kefirStopper from 'kefir-stopper';
|
|
3
2
|
import kefirBus from 'kefir-bus';
|
|
3
|
+
import cx from 'classnames';
|
|
4
4
|
import EventEmitter from '../lib/safe-event-emitter';
|
|
5
5
|
import { GmailAppMenuItemView, NATIVE_CLASS, INBOXSDK_CLASS, } from '../dom-driver/gmail/views/gmail-app-menu-item-view';
|
|
6
6
|
import { NATIVE_CLASS as PANEL_NATIVE_CLASS, CollapsiblePanelView, } from './collapsible-panel-view';
|
|
7
7
|
import { addCollapsiblePanel } from '../dom-driver/gmail/gmail-driver/add-collapsible-panel';
|
|
8
8
|
import GmailElementGetter from '../dom-driver/gmail/gmail-element-getter';
|
|
9
|
+
import defer from '../../common/defer';
|
|
9
10
|
var NativeAppMenuClassName;
|
|
10
11
|
(function (NativeAppMenuClassName) {
|
|
11
12
|
NativeAppMenuClassName["MAIL"] = "XJ";
|
|
@@ -33,12 +34,13 @@ const routeIDtoMenuItemClass = Object.fromEntries([
|
|
|
33
34
|
...SPACES_ROUTES.map((route) => [route, NativeAppMenuClassName.SPACES]),
|
|
34
35
|
...MEET_ROUTES.map((route) => [route, NativeAppMenuClassName.MEET]),
|
|
35
36
|
]);
|
|
36
|
-
const initialRoutingStateStopper = kefirStopper();
|
|
37
37
|
/**
|
|
38
38
|
* Contains both a native Gmail app menu item and a collapsible panel.
|
|
39
39
|
*/
|
|
40
40
|
export class AppMenuItemView extends EventEmitter {
|
|
41
41
|
static #menuItemChangeStream = Kefir.pool();
|
|
42
|
+
static #menuItemChangeBus = kefirBus();
|
|
43
|
+
static #menuItemAddedDeferred = defer();
|
|
42
44
|
static #routeViewDriverStream;
|
|
43
45
|
static #menuItemToPanelMap = new Map();
|
|
44
46
|
static #appMenuItemViews = new Set();
|
|
@@ -54,23 +56,18 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
54
56
|
const button = appMenuElement?.querySelector(`.${NATIVE_CLASS}.${ACTIVE}`);
|
|
55
57
|
return button;
|
|
56
58
|
}
|
|
59
|
+
static #isPanelLess() {
|
|
60
|
+
const activePanel = AppMenuItemView.#getActivePanel();
|
|
61
|
+
return !activePanel;
|
|
62
|
+
}
|
|
57
63
|
static #setUpRouteViewDriverStream(driver) {
|
|
58
64
|
if (AppMenuItemView.#routeViewDriverStream)
|
|
59
65
|
return;
|
|
60
66
|
AppMenuItemView.#routeViewDriverStream = driver.getRouteViewDriverStream();
|
|
61
|
-
|
|
62
|
-
AppMenuItemView.#
|
|
63
|
-
AppMenuItemView.#routeViewDriverStream
|
|
64
|
-
.takeUntilBy(initialRoutingStateStopper)
|
|
65
|
-
.onValue(async (routeView) => {
|
|
67
|
+
// Activate menu items on route changes. Used for deep-links, browser history navigation etc.
|
|
68
|
+
AppMenuItemView.#routeViewDriverStream.onValue(async (routeView) => {
|
|
66
69
|
const routeViewID = routeView.getRouteID();
|
|
67
70
|
const routeType = routeView.getRouteType();
|
|
68
|
-
const routeViewParams = routeView.getParams();
|
|
69
|
-
console.debug({
|
|
70
|
-
routeViewID,
|
|
71
|
-
routeType,
|
|
72
|
-
routeViewParams,
|
|
73
|
-
});
|
|
74
71
|
/* Handling custom app menu items */
|
|
75
72
|
const descriptors = Array.from(AppMenuItemView.#appMenuItemViews, async (appMenuItemView) => {
|
|
76
73
|
const gmailView = await appMenuItemView.#gmailViewPromise;
|
|
@@ -81,26 +78,37 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
81
78
|
return descriptor?.isRouteActive(routeView);
|
|
82
79
|
}) ?? [];
|
|
83
80
|
if (gmailView?.element) {
|
|
84
|
-
|
|
81
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
82
|
+
'route-activated',
|
|
83
|
+
gmailView.element,
|
|
84
|
+
]);
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
/* Handling native app menu items */
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
let menuItemClass = '';
|
|
89
|
+
if (routeViewID) {
|
|
90
|
+
menuItemClass = routeIDtoMenuItemClass[routeViewID];
|
|
91
|
+
}
|
|
92
|
+
if ((!routeViewID || !menuItemClass) && routeType !== 'CUSTOM') {
|
|
93
|
+
menuItemClass = NativeAppMenuClassName.MAIL;
|
|
94
|
+
}
|
|
95
|
+
const menuItemElement = menuItemClass &&
|
|
96
|
+
document.querySelector(`.${menuItemClass}`);
|
|
97
|
+
if (menuItemElement) {
|
|
98
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
99
|
+
'route-activated',
|
|
100
|
+
menuItemElement,
|
|
101
|
+
]);
|
|
95
102
|
}
|
|
96
103
|
});
|
|
97
104
|
}
|
|
98
105
|
static {
|
|
99
|
-
(async ()
|
|
106
|
+
(async function init() {
|
|
100
107
|
const gmailAppMenu = await GmailElementGetter.getAppMenuAsync();
|
|
101
108
|
if (!gmailAppMenu) {
|
|
102
109
|
return;
|
|
103
110
|
}
|
|
111
|
+
AppMenuItemView.#menuItemChangeStream.plug(AppMenuItemView.#menuItemChangeBus);
|
|
104
112
|
// Set up menu item panel -> menu collapsible panel element mappings
|
|
105
113
|
for (const [menuItemClass, panelClass] of nativeButtonToPanelMap) {
|
|
106
114
|
const menuItemElement = document.querySelector(`.${NATIVE_CLASS}.${menuItemClass}`);
|
|
@@ -111,97 +119,55 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
111
119
|
AppMenuItemView.#menuItemToPanelMap.set(menuItemElement, panelElement ?? undefined);
|
|
112
120
|
}
|
|
113
121
|
}
|
|
114
|
-
// Set up event listeners we use to control menu UI
|
|
115
|
-
const stream = Kefir.stream((emitter) => {
|
|
116
|
-
// We intercept some events emitted on Gmail (native) menu items to gain full control of the menu (menu items and panels).
|
|
117
|
-
// Thus, some native event handlers won't be invoked so we replicate the Gmail's behavior by ourselves.
|
|
118
|
-
const appMenuElement = GmailElementGetter.getAppMenuContainer();
|
|
119
|
-
for (const nativeMenuItem of appMenuElement?.querySelectorAll(`.${NATIVE_CLASS}:not(.${INBOXSDK_CLASS})`) ?? []) {
|
|
120
|
-
// Gmail uses this listener for panel hover state activation. It's prevented from propagation and we handle UI updates
|
|
121
|
-
nativeMenuItem.addEventListener('mouseenter', (e) => {
|
|
122
|
-
e.stopImmediatePropagation();
|
|
123
|
-
emitter.emit(['mouseenter', nativeMenuItem, e]);
|
|
124
|
-
}, true);
|
|
125
|
-
// Gmail uses this listener for panel hover state deactivation. It's prevented from propagation and we handle UI updates
|
|
126
|
-
nativeMenuItem.addEventListener('mouseleave', (e) => {
|
|
127
|
-
e.stopImmediatePropagation();
|
|
128
|
-
emitter.emit(['mouseleave', nativeMenuItem, e]);
|
|
129
|
-
}, true);
|
|
130
|
-
// Click event isn't prevented from propagation to let Gmail handle routing. The styling is handled by us
|
|
131
|
-
nativeMenuItem.addEventListener('click', () => {
|
|
132
|
-
emitter.emit(['click', nativeMenuItem]);
|
|
133
|
-
});
|
|
134
|
-
// Handle moving cursor out of panel. We use it to enhance the styling since it won't be handled by Gmail
|
|
135
|
-
const panel = AppMenuItemView.#menuItemToPanelMap.get(nativeMenuItem);
|
|
136
|
-
panel?.addEventListener('mouseleave', (e) => {
|
|
137
|
-
emitter.emit(['mouseleave', nativeMenuItem, e]);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
AppMenuItemView.#menuItemChangeStream.plug(stream);
|
|
142
122
|
})();
|
|
143
123
|
}
|
|
124
|
+
static {
|
|
125
|
+
AppMenuItemView.#menuItemAddedDeferred.promise.then(() => {
|
|
126
|
+
// Set up event listeners we use to control menu UI
|
|
127
|
+
// We intercept some events emitted on Gmail (native) menu items to gain full control of the menu (menu items and panels).
|
|
128
|
+
// Thus, some native event handlers won't be invoked so we replicate the Gmail's behavior by ourselves.
|
|
129
|
+
const appMenuElement = GmailElementGetter.getAppMenuContainer();
|
|
130
|
+
for (const nativeMenuItem of appMenuElement?.querySelectorAll(`.${NATIVE_CLASS}:not(.${INBOXSDK_CLASS})`) ?? []) {
|
|
131
|
+
// Gmail uses this listener for panel hover state activation. It's prevented from propagation and we handle UI updates
|
|
132
|
+
const buttonElement = nativeMenuItem.querySelector('.V6.CL');
|
|
133
|
+
buttonElement?.addEventListener('mouseenter', (e) => {
|
|
134
|
+
e.stopImmediatePropagation();
|
|
135
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
136
|
+
'mouseenter',
|
|
137
|
+
nativeMenuItem,
|
|
138
|
+
e,
|
|
139
|
+
]);
|
|
140
|
+
}, true);
|
|
141
|
+
// Gmail uses this listener for panel hover state deactivation. It's prevented from propagation and we handle UI updates
|
|
142
|
+
nativeMenuItem.addEventListener('mouseleave', (e) => {
|
|
143
|
+
e.stopImmediatePropagation();
|
|
144
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
145
|
+
'mouseleave',
|
|
146
|
+
nativeMenuItem,
|
|
147
|
+
e,
|
|
148
|
+
]);
|
|
149
|
+
}, true);
|
|
150
|
+
// Click event isn't prevented from propagation to let Gmail handle routing. The styling is handled by us
|
|
151
|
+
nativeMenuItem.addEventListener('click', () => {
|
|
152
|
+
AppMenuItemView.#menuItemChangeBus.emit(['click', nativeMenuItem]);
|
|
153
|
+
});
|
|
154
|
+
// Handle moving cursor out of panel. We use it to enhance the styling since it won't be handled by Gmail
|
|
155
|
+
const panel = AppMenuItemView.#menuItemToPanelMap.get(nativeMenuItem);
|
|
156
|
+
panel?.addEventListener('mouseleave', (e) => {
|
|
157
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
158
|
+
'mouseleave',
|
|
159
|
+
nativeMenuItem,
|
|
160
|
+
e,
|
|
161
|
+
]);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
144
166
|
static {
|
|
145
167
|
AppMenuItemView.#menuItemChangeStream.onValue(async (v) => {
|
|
146
168
|
const [type, menuItem, mouseEvent] = v;
|
|
147
169
|
const { ACTIVE, HOVER } = GmailAppMenuItemView.elementCss;
|
|
148
|
-
|
|
149
|
-
const panel = AppMenuItemView.#menuItemToPanelMap.get(menuItem);
|
|
150
|
-
if (mouseEvent.relatedTarget instanceof Node &&
|
|
151
|
-
(menuItem.contains(mouseEvent.relatedTarget) ||
|
|
152
|
-
panel?.contains(mouseEvent.relatedTarget)))
|
|
153
|
-
return;
|
|
154
|
-
const activeMenuItem = AppMenuItemView.#getActiveMenuItem();
|
|
155
|
-
const activePanel = AppMenuItemView.#getActivePanel();
|
|
156
|
-
if (activeMenuItem === menuItem && activePanel === panel)
|
|
157
|
-
return;
|
|
158
|
-
// deactivate active panel
|
|
159
|
-
if (activePanel) {
|
|
160
|
-
activePanel.style.removeProperty('height');
|
|
161
|
-
activePanel.classList.remove(CollapsiblePanelView.elementCss.ACTIVE);
|
|
162
|
-
activePanel.classList.remove(CollapsiblePanelView.elementCss.HOVER);
|
|
163
|
-
}
|
|
164
|
-
// activate new panel
|
|
165
|
-
if (panel) {
|
|
166
|
-
const height = window.innerHeight - panel.getBoundingClientRect().top;
|
|
167
|
-
panel.style.cssText = `height: ${height}px;`;
|
|
168
|
-
panel.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
169
|
-
panel.classList.add(CollapsiblePanelView.elementCss.HOVER);
|
|
170
|
-
// hover menu item
|
|
171
|
-
menuItem.classList.add(HOVER);
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
// panel doesn't exist (i.e. Meet) so activate active menu item's panel instead (as Gmail currently does -- FEB 2023)
|
|
175
|
-
const panel = activeMenuItem &&
|
|
176
|
-
AppMenuItemView.#menuItemToPanelMap.get(activeMenuItem);
|
|
177
|
-
panel?.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (type === 'mouseleave') {
|
|
181
|
-
const activeMenuItem = AppMenuItemView.#getActiveMenuItem();
|
|
182
|
-
const activePanel = AppMenuItemView.#getActivePanel();
|
|
183
|
-
const activeMenuItemPanel = activeMenuItem &&
|
|
184
|
-
AppMenuItemView.#menuItemToPanelMap.get(activeMenuItem);
|
|
185
|
-
if (mouseEvent.relatedTarget instanceof Node &&
|
|
186
|
-
(menuItem.contains(mouseEvent.relatedTarget) ||
|
|
187
|
-
activePanel?.contains(mouseEvent.relatedTarget)))
|
|
188
|
-
return;
|
|
189
|
-
if (activePanel === activeMenuItemPanel)
|
|
190
|
-
return;
|
|
191
|
-
// unhover menu item
|
|
192
|
-
menuItem.classList.remove(HOVER);
|
|
193
|
-
// deactivate active panel
|
|
194
|
-
if (activePanel) {
|
|
195
|
-
activePanel.style.removeProperty('height');
|
|
196
|
-
activePanel.classList.remove(CollapsiblePanelView.elementCss.ACTIVE);
|
|
197
|
-
activePanel.classList.remove(CollapsiblePanelView.elementCss.HOVER);
|
|
198
|
-
}
|
|
199
|
-
// activate active menu item panel
|
|
200
|
-
if (activeMenuItemPanel) {
|
|
201
|
-
activeMenuItemPanel.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (type === 'click') {
|
|
170
|
+
function handleActivate() {
|
|
205
171
|
const appMenuElement = GmailElementGetter.getAppMenuContainer();
|
|
206
172
|
// deactivate active panel
|
|
207
173
|
const activePanel = AppMenuItemView.#getActivePanel();
|
|
@@ -223,6 +189,92 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
223
189
|
panel.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
224
190
|
}
|
|
225
191
|
}
|
|
192
|
+
switch (type) {
|
|
193
|
+
case 'mouseenter': {
|
|
194
|
+
const panel = AppMenuItemView.#menuItemToPanelMap.get(menuItem);
|
|
195
|
+
const activeMenuItem = AppMenuItemView.#getActiveMenuItem();
|
|
196
|
+
const activePanel = AppMenuItemView.#getActivePanel();
|
|
197
|
+
const burgerMenuOpen = GmailElementGetter.isAppBurgerMenuOpen();
|
|
198
|
+
// hover-styled panel is displayed for collapsed burger menu
|
|
199
|
+
if (activeMenuItem === menuItem &&
|
|
200
|
+
activePanel === panel &&
|
|
201
|
+
burgerMenuOpen)
|
|
202
|
+
return;
|
|
203
|
+
// deactivate active panel
|
|
204
|
+
if (activePanel) {
|
|
205
|
+
activePanel.style.removeProperty('height');
|
|
206
|
+
activePanel.classList.remove(CollapsiblePanelView.elementCss.ACTIVE);
|
|
207
|
+
activePanel.classList.remove(CollapsiblePanelView.elementCss.HOVER);
|
|
208
|
+
}
|
|
209
|
+
// activate new panel
|
|
210
|
+
if (panel) {
|
|
211
|
+
const height = window.innerHeight - panel.getBoundingClientRect().top;
|
|
212
|
+
panel.style.cssText = `height: ${height}px;`;
|
|
213
|
+
panel.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
214
|
+
panel.classList.add(CollapsiblePanelView.elementCss.HOVER);
|
|
215
|
+
// hover menu item
|
|
216
|
+
menuItem.classList.add(HOVER);
|
|
217
|
+
// handle burger menu collapsed
|
|
218
|
+
if (!burgerMenuOpen) {
|
|
219
|
+
for (const panel of document.querySelectorAll(CollapsiblePanelView.elementSelectors.NATIVE) ?? []) {
|
|
220
|
+
panel.classList.add(CollapsiblePanelView.elementCss.COLLAPSED_HOVER);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
// panel doesn't exist (i.e. Meet) so activate active menu item's panel instead (as Gmail currently does -- FEB 2023)
|
|
226
|
+
const panel = activeMenuItem &&
|
|
227
|
+
AppMenuItemView.#menuItemToPanelMap.get(activeMenuItem);
|
|
228
|
+
panel?.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'mouseleave': {
|
|
233
|
+
const activeMenuItem = AppMenuItemView.#getActiveMenuItem();
|
|
234
|
+
const activePanel = AppMenuItemView.#getActivePanel();
|
|
235
|
+
const activeMenuItemPanel = activeMenuItem &&
|
|
236
|
+
AppMenuItemView.#menuItemToPanelMap.get(activeMenuItem);
|
|
237
|
+
if (mouseEvent.relatedTarget instanceof Node &&
|
|
238
|
+
(menuItem.contains(mouseEvent.relatedTarget) ||
|
|
239
|
+
activePanel?.contains(mouseEvent.relatedTarget)))
|
|
240
|
+
return;
|
|
241
|
+
const burgerMenuOpen = GmailElementGetter.isAppBurgerMenuOpen();
|
|
242
|
+
if (activePanel === activeMenuItemPanel && burgerMenuOpen)
|
|
243
|
+
return;
|
|
244
|
+
// unhover menu item
|
|
245
|
+
menuItem.classList.remove(HOVER);
|
|
246
|
+
// handle burger menu collapsed
|
|
247
|
+
for (const panel of document.querySelectorAll(CollapsiblePanelView.elementSelectors.NATIVE) ?? []) {
|
|
248
|
+
panel.classList.remove(CollapsiblePanelView.elementCss.COLLAPSED_HOVER);
|
|
249
|
+
}
|
|
250
|
+
// deactivate active panel
|
|
251
|
+
if (activePanel) {
|
|
252
|
+
activePanel.style.removeProperty('height');
|
|
253
|
+
activePanel.classList.remove(CollapsiblePanelView.elementCss.ACTIVE);
|
|
254
|
+
activePanel.classList.remove(CollapsiblePanelView.elementCss.HOVER);
|
|
255
|
+
}
|
|
256
|
+
// activate active menu item panel
|
|
257
|
+
if (activeMenuItemPanel) {
|
|
258
|
+
activeMenuItemPanel.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case 'click': {
|
|
263
|
+
handleActivate();
|
|
264
|
+
// handle panel-less mode
|
|
265
|
+
const isPanelLess = AppMenuItemView.#isPanelLess();
|
|
266
|
+
for (const panel of document.querySelectorAll(CollapsiblePanelView.elementSelectors.NATIVE) ?? []) {
|
|
267
|
+
panel.classList.toggle(CollapsiblePanelView.elementCss.PANEL_LESS, isPanelLess);
|
|
268
|
+
}
|
|
269
|
+
GmailElementGetter.getAppMenu()?.classList.toggle('aTO', isPanelLess);
|
|
270
|
+
GmailElementGetter.getAppHeader()?.classList.toggle('aTO', isPanelLess);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'route-activated': {
|
|
274
|
+
handleActivate();
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
226
278
|
});
|
|
227
279
|
}
|
|
228
280
|
get menuItemDescriptor() {
|
|
@@ -232,8 +284,6 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
232
284
|
#destroyed = false;
|
|
233
285
|
#gmailViewPromise;
|
|
234
286
|
#collapsiblePanel;
|
|
235
|
-
#appMenuItemStream;
|
|
236
|
-
#collapsiblePanelStream;
|
|
237
287
|
#menuItemDescriptor;
|
|
238
288
|
constructor(driver, menuItemDescriptor) {
|
|
239
289
|
super();
|
|
@@ -249,41 +299,54 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
249
299
|
if (gmailElement) {
|
|
250
300
|
AppMenuItemView.#menuItemToPanelMap.set(gmailElement, undefined);
|
|
251
301
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
302
|
+
gmailView.on('destroy', () => {
|
|
303
|
+
this.remove();
|
|
304
|
+
});
|
|
305
|
+
gmailView.on('click', () => {
|
|
306
|
+
AppMenuItemView.#menuItemChangeBus.emit(['click', gmailElement]);
|
|
307
|
+
this.emit('click');
|
|
308
|
+
});
|
|
309
|
+
gmailView.on('hover', (e) => {
|
|
310
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
311
|
+
'mouseenter',
|
|
312
|
+
gmailElement,
|
|
313
|
+
e,
|
|
314
|
+
]);
|
|
315
|
+
this.emit('hover');
|
|
316
|
+
});
|
|
317
|
+
gmailView.on('blur', (e) => {
|
|
318
|
+
AppMenuItemView.#menuItemChangeBus.emit([
|
|
319
|
+
'mouseleave',
|
|
320
|
+
gmailElement,
|
|
321
|
+
e,
|
|
322
|
+
]);
|
|
323
|
+
this.emit('blur');
|
|
269
324
|
});
|
|
270
|
-
AppMenuItemView.#menuItemChangeStream.plug(this.#appMenuItemStream);
|
|
271
|
-
AppMenuItemView.#setUpRouteViewDriverStream(driver);
|
|
272
|
-
AppMenuItemView.#appMenuItemViews.add(this);
|
|
273
325
|
});
|
|
326
|
+
AppMenuItemView.#setUpRouteViewDriverStream(driver);
|
|
327
|
+
AppMenuItemView.#menuItemAddedDeferred.resolve(undefined);
|
|
328
|
+
AppMenuItemView.#appMenuItemViews.add(this);
|
|
274
329
|
}
|
|
275
330
|
async addCollapsiblePanel(panelDescriptor) {
|
|
276
|
-
const
|
|
331
|
+
const descriptor = {
|
|
332
|
+
...panelDescriptor,
|
|
333
|
+
className: cx(panelDescriptor.className, {
|
|
334
|
+
[CollapsiblePanelView.elementCss.COLLAPSED]: !GmailElementGetter.isAppBurgerMenuOpen(),
|
|
335
|
+
[CollapsiblePanelView.elementCss.PANEL_LESS]: AppMenuItemView.#isPanelLess(),
|
|
336
|
+
}),
|
|
337
|
+
};
|
|
338
|
+
const collapsiblePanel = await addCollapsiblePanel(this.#driver, descriptor);
|
|
277
339
|
if (collapsiblePanel) {
|
|
278
340
|
this.#gmailViewPromise.then((gmailView) => {
|
|
279
341
|
const gmailElement = gmailView.element;
|
|
280
342
|
if (gmailElement) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
343
|
+
collapsiblePanel.on('blur', (e) => {
|
|
344
|
+
AppMenuItemView.#menuItemChangeBus.value([
|
|
345
|
+
'mouseleave',
|
|
346
|
+
gmailElement,
|
|
347
|
+
e,
|
|
348
|
+
]);
|
|
285
349
|
});
|
|
286
|
-
AppMenuItemView.#menuItemChangeStream.plug(this.#collapsiblePanelStream);
|
|
287
350
|
}
|
|
288
351
|
if (gmailElement && collapsiblePanel) {
|
|
289
352
|
AppMenuItemView.#menuItemToPanelMap.set(gmailElement, collapsiblePanel.element);
|
|
@@ -296,6 +359,7 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
296
359
|
async update(menuItemDescriptor) {
|
|
297
360
|
const gmailView = await this.#gmailViewPromise;
|
|
298
361
|
gmailView.setMenuItemDescriptor(menuItemDescriptor);
|
|
362
|
+
this.#menuItemDescriptor = menuItemDescriptor;
|
|
299
363
|
}
|
|
300
364
|
remove() {
|
|
301
365
|
if (this.#destroyed)
|
|
@@ -308,22 +372,6 @@ export class AppMenuItemView extends EventEmitter {
|
|
|
308
372
|
AppMenuItemView.#menuItemToPanelMap.delete(gmailView.element);
|
|
309
373
|
gmailView.remove();
|
|
310
374
|
});
|
|
311
|
-
this.#appMenuItemStream &&
|
|
312
|
-
AppMenuItemView.#menuItemChangeStream.unplug(this.#appMenuItemStream);
|
|
313
|
-
this.#collapsiblePanelStream &&
|
|
314
|
-
AppMenuItemView.#menuItemChangeStream.unplug(this.#collapsiblePanelStream);
|
|
315
375
|
AppMenuItemView.#appMenuItemViews.delete(this);
|
|
316
|
-
initialRoutingStateStopper.destroy();
|
|
317
|
-
}
|
|
318
|
-
async #activate() {
|
|
319
|
-
const gmailView = await this.#gmailViewPromise;
|
|
320
|
-
const appMenuElement = GmailElementGetter.getAppMenuContainer();
|
|
321
|
-
const { ACTIVE } = GmailAppMenuItemView.elementCss;
|
|
322
|
-
// Note this affects both the active app menu item and the collapsible panel.
|
|
323
|
-
for (const el of appMenuElement?.querySelectorAll(`.${ACTIVE}`) ?? []) {
|
|
324
|
-
el.classList.remove(ACTIVE);
|
|
325
|
-
}
|
|
326
|
-
gmailView.activate();
|
|
327
|
-
this.#collapsiblePanel?.activate();
|
|
328
376
|
}
|
|
329
377
|
}
|
|
@@ -19,19 +19,21 @@ export declare class CollapsiblePanelView extends CollapsiblePanelView_base {
|
|
|
19
19
|
};
|
|
20
20
|
static elementCss: {
|
|
21
21
|
readonly ACTIVE: "apV";
|
|
22
|
-
readonly COLLAPSED: "
|
|
22
|
+
readonly COLLAPSED: "aBA";
|
|
23
23
|
/** A hover popover has both ACTIVE _and_ HOVER */
|
|
24
24
|
readonly HOVER: "aJu";
|
|
25
|
+
readonly COLLAPSED_HOVER: "bym";
|
|
26
|
+
readonly PANEL_LESS: "a3W";
|
|
25
27
|
};
|
|
26
|
-
get
|
|
28
|
+
get loading(): boolean;
|
|
29
|
+
setLoading(loading: boolean): void;
|
|
30
|
+
get panelDescriptor(): AppMenuItemPanelDescriptor;
|
|
27
31
|
set panelDescriptor(panelDescriptor: AppMenuItemPanelDescriptor);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*/
|
|
32
|
-
activate(): void;
|
|
32
|
+
get element(): HTMLElement;
|
|
33
|
+
get scrollablePaneElement(): Element | null;
|
|
34
|
+
constructor(driver: GmailDriver, panelDescriptor: AppMenuItemPanelDescriptor);
|
|
33
35
|
remove(): void;
|
|
34
|
-
addNavItem(navItemDescriptor: NavItemDescriptor | Kefir.Observable<NavItemDescriptor, any>): NavItemView
|
|
36
|
+
addNavItem(navItemDescriptor: NavItemDescriptor | Kefir.Observable<NavItemDescriptor, any>): NavItemView;
|
|
35
37
|
}
|
|
36
38
|
export {};
|
|
37
39
|
//# sourceMappingURL=collapsible-panel-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible-panel-view.d.ts","sourceRoot":"","sources":["../../../../../src/platform-implementation-js/views/collapsible-panel-view.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAIpE,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,eAAO,MAAM,YAAY,OAAiB,CAAC;AAC3C,eAAO,MAAM,cAAc,8BAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"collapsible-panel-view.d.ts","sourceRoot":"","sources":["../../../../../src/platform-implementation-js/views/collapsible-panel-view.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAIpE,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,eAAO,MAAM,YAAY,OAAiB,CAAC;AAC3C,eAAO,MAAM,cAAc,8BAAwC,CAAC;AAapE,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;CAC/B,CAAC;mDAEmE,aAAa,aAAa,CAAC;AAAhG,qBAAa,oBAAqB,SAAQ,yBAAuD;;IAC/F,MAAM,CAAC,gBAAgB;;;MAGZ;IACX,MAAM,CAAC,UAAU;;;QAGf,kDAAkD;;;;MAIzC;IASX,IAAI,OAAO,YAEV;IAED,UAAU,CAAC,OAAO,EAAE,OAAO;IAS3B,IAAI,eAAe,IAIkB,0BAA0B,CAF9D;IAED,IAAI,eAAe,CAAC,eAAe,EAAE,0BAA0B,EAG9D;IAED,IAAI,OAAO,gBAEV;IAED,IAAI,qBAAqB,mBAExB;gBAGC,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,0BAA0B;IAY7C,MAAM;IAON,UAAU,CACR,iBAAiB,EACb,iBAAiB,GACjB,KAAK,CAAC,UAAU,CAAC,iBAAiB,EAAE,GAAG,CAAC;CAsL/C"}
|
|
@@ -11,6 +11,10 @@ export const INBOXSDK_CLASS = 'inboxsdk__collapsiblePanel';
|
|
|
11
11
|
const ELEMENT_CLASS = `${NATIVE_CLASS} ${INBOXSDK_CLASS} oy8Mbf`;
|
|
12
12
|
const PRIMARY_BUTTON_ELEMENT_CLASS = 'T-I T-I-KE L3';
|
|
13
13
|
const PRIMARY_BUTTON_ELEMENT_SELECTOR = '.T-I.T-I-KE.L3';
|
|
14
|
+
const scrollablePanelClass = 'at9';
|
|
15
|
+
const scrollablePanelSelector = `.${scrollablePanelClass}`;
|
|
16
|
+
const loadingElementClass = 'inboxsdk__collapsiblePanel_loading_container';
|
|
17
|
+
const loadingElementSelector = `.${loadingElementClass}`;
|
|
14
18
|
const NAV_MENU_CONTAINER_ELEMENT_SELECTOR = '.at9 .n3 .TK';
|
|
15
19
|
export class CollapsiblePanelView extends EventEmitter {
|
|
16
20
|
static elementSelectors = {
|
|
@@ -19,9 +23,11 @@ export class CollapsiblePanelView extends EventEmitter {
|
|
|
19
23
|
};
|
|
20
24
|
static elementCss = {
|
|
21
25
|
ACTIVE: 'apV',
|
|
22
|
-
COLLAPSED: '
|
|
26
|
+
COLLAPSED: 'aBA',
|
|
23
27
|
/** A hover popover has both ACTIVE _and_ HOVER */
|
|
24
28
|
HOVER: 'aJu',
|
|
29
|
+
COLLAPSED_HOVER: 'bym',
|
|
30
|
+
PANEL_LESS: 'a3W',
|
|
25
31
|
};
|
|
26
32
|
#panelDescriptor;
|
|
27
33
|
#element;
|
|
@@ -29,42 +35,50 @@ export class CollapsiblePanelView extends EventEmitter {
|
|
|
29
35
|
#id = Math.random().toFixed(3);
|
|
30
36
|
#ARIA_LABELLED_BY_ID = Math.random().toFixed(3);
|
|
31
37
|
#driver;
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
#loading;
|
|
39
|
+
get loading() {
|
|
40
|
+
return this.#loading;
|
|
41
|
+
}
|
|
42
|
+
setLoading(loading) {
|
|
43
|
+
const changed = this.#loading !== loading;
|
|
44
|
+
this.#loading = loading;
|
|
45
|
+
if (changed) {
|
|
46
|
+
this.#update();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
get panelDescriptor() {
|
|
50
|
+
return this.#panelDescriptor;
|
|
34
51
|
}
|
|
35
52
|
set panelDescriptor(panelDescriptor) {
|
|
36
53
|
this.#panelDescriptor = panelDescriptor;
|
|
37
54
|
this.#update();
|
|
38
55
|
}
|
|
39
|
-
|
|
56
|
+
get element() {
|
|
57
|
+
return this.#element;
|
|
58
|
+
}
|
|
59
|
+
get scrollablePaneElement() {
|
|
60
|
+
return this.element.querySelector(scrollablePanelSelector);
|
|
61
|
+
}
|
|
62
|
+
constructor(driver, panelDescriptor) {
|
|
40
63
|
super();
|
|
41
64
|
this.#driver = driver;
|
|
65
|
+
this.#panelDescriptor = panelDescriptor;
|
|
66
|
+
this.#loading = panelDescriptor.loadingIcon != null;
|
|
42
67
|
this.#element = this.#setupElement();
|
|
43
68
|
this.#element.addEventListener('mouseleave', (e) => {
|
|
44
69
|
this.emit('blur', e);
|
|
45
70
|
});
|
|
46
71
|
}
|
|
47
|
-
/**
|
|
48
|
-
* @internal
|
|
49
|
-
*/
|
|
50
|
-
activate() {
|
|
51
|
-
const element = this.element;
|
|
52
|
-
if (!element)
|
|
53
|
-
return;
|
|
54
|
-
element?.classList.remove(CollapsiblePanelView.elementCss.HOVER);
|
|
55
|
-
element.classList.add(CollapsiblePanelView.elementCss.ACTIVE);
|
|
56
|
-
}
|
|
57
72
|
remove() {
|
|
58
73
|
if (this.#destroyed)
|
|
59
74
|
return;
|
|
60
75
|
this.#destroyed = true;
|
|
61
|
-
this.element
|
|
76
|
+
this.element.remove();
|
|
62
77
|
this.emit('destroy');
|
|
63
78
|
}
|
|
64
79
|
addNavItem(navItemDescriptor) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return;
|
|
80
|
+
this.setLoading(false);
|
|
81
|
+
const { element } = this;
|
|
68
82
|
const navMenuContainerElement = querySelector(element, NAV_MENU_CONTAINER_ELEMENT_SELECTOR);
|
|
69
83
|
const appId = `collapsible-panel-${this.#id}`;
|
|
70
84
|
const navItemDescriptorPropertyStream = kefirCast(Kefir, navItemDescriptor).toProperty();
|
|
@@ -73,9 +87,11 @@ export class CollapsiblePanelView extends EventEmitter {
|
|
|
73
87
|
return navItemView;
|
|
74
88
|
}
|
|
75
89
|
#setupElement() {
|
|
76
|
-
const {
|
|
90
|
+
const { panelDescriptor } = this;
|
|
91
|
+
const { loadingIcon } = panelDescriptor;
|
|
92
|
+
const { iconUrl: themedIcons, className, name = '', } = panelDescriptor?.primaryButton ?? {};
|
|
77
93
|
const element = document.createElement('div');
|
|
78
|
-
element.className = ELEMENT_CLASS;
|
|
94
|
+
element.className = cx(ELEMENT_CLASS, this.panelDescriptor.className);
|
|
79
95
|
const primaryButtonClass = cx(PRIMARY_BUTTON_ELEMENT_CLASS, className);
|
|
80
96
|
let iconUrl;
|
|
81
97
|
if (themedIcons) {
|
|
@@ -95,7 +111,8 @@ export class CollapsiblePanelView extends EventEmitter {
|
|
|
95
111
|
</div>
|
|
96
112
|
</div>
|
|
97
113
|
</div>
|
|
98
|
-
<div class="
|
|
114
|
+
<div class="${scrollablePanelClass}">
|
|
115
|
+
<div class="${loadingElementClass}">${loadingIcon ? { __html: loadingIcon } : ''}</div>
|
|
99
116
|
<div class="Ls77Lb aZ6">
|
|
100
117
|
<div class="pp" style="user-select: none;">
|
|
101
118
|
<div>
|
|
@@ -127,25 +144,21 @@ export class CollapsiblePanelView extends EventEmitter {
|
|
|
127
144
|
return element;
|
|
128
145
|
}
|
|
129
146
|
#onPrimaryButtonClick = (e) => {
|
|
130
|
-
this
|
|
147
|
+
this.panelDescriptor.primaryButton?.onClick?.(e);
|
|
131
148
|
};
|
|
132
149
|
#update() {
|
|
133
150
|
const element = this.element;
|
|
134
151
|
if (!element)
|
|
135
152
|
return;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
const isCollapsed = element.classList.contains(COLLAPSED);
|
|
139
|
-
element.className = cx(ELEMENT_CLASS, this.#panelDescriptor?.className, {
|
|
140
|
-
[ACTIVE]: isActive,
|
|
141
|
-
[COLLAPSED]: isCollapsed,
|
|
142
|
-
});
|
|
153
|
+
const existingClassNames = Object.values(CollapsiblePanelView.elementCss).filter((className) => element.classList.contains(className));
|
|
154
|
+
element.className = cx(ELEMENT_CLASS, this.panelDescriptor.className, ...existingClassNames);
|
|
143
155
|
this.#updateName(element);
|
|
144
156
|
this.#updateIcon(element);
|
|
157
|
+
this.#updateScrollablePanelLoading(element);
|
|
145
158
|
}
|
|
146
159
|
#updateIcon(element) {
|
|
147
160
|
const iconContainerEl = querySelector(element, PRIMARY_BUTTON_ELEMENT_SELECTOR);
|
|
148
|
-
const { iconUrl, className } = this
|
|
161
|
+
const { iconUrl, className } = this.panelDescriptor.primaryButton ?? {};
|
|
149
162
|
if (iconUrl) {
|
|
150
163
|
const backgroundImage = `url(${GmailElementGetter.isDarkTheme()
|
|
151
164
|
? iconUrl.darkTheme
|
|
@@ -162,6 +175,19 @@ export class CollapsiblePanelView extends EventEmitter {
|
|
|
162
175
|
if (!buttonTextEl) {
|
|
163
176
|
return;
|
|
164
177
|
}
|
|
165
|
-
buttonTextEl.textContent = this
|
|
178
|
+
buttonTextEl.textContent = this.panelDescriptor.primaryButton?.name ?? '';
|
|
179
|
+
}
|
|
180
|
+
#updateScrollablePanelLoading(element) {
|
|
181
|
+
const scrollablePanelLoadingEl = element.querySelector(loadingElementSelector);
|
|
182
|
+
if (!scrollablePanelLoadingEl) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const panelLoadingClass = `${loadingElementClass}--active`;
|
|
186
|
+
if (this.loading) {
|
|
187
|
+
scrollablePanelLoadingEl.classList.add(panelLoadingClass);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
scrollablePanelLoadingEl.classList.remove(panelLoadingClass);
|
|
191
|
+
}
|
|
166
192
|
}
|
|
167
193
|
}
|