@inboxsdk/core 0.2.29-alpha.4 → 0.2.29-alpha.6

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.
Files changed (23) hide show
  1. package/inboxsdk.js +35 -35
  2. package/inboxsdk.js.map +1 -1
  3. package/package.json +2 -1
  4. package/src/inboxsdk.d.ts +3 -0
  5. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.d.ts.map +1 -1
  6. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.js +3 -4
  7. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.d.ts +1 -1
  8. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.d.ts.map +1 -1
  9. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.js +13 -2
  10. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.d.ts +5 -1
  11. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.d.ts.map +1 -1
  12. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.js +32 -20
  13. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.d.ts.map +1 -1
  14. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.js +3 -0
  15. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.d.ts +4 -12
  16. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.d.ts.map +1 -1
  17. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.js +28 -17
  18. package/src/platform-implementation-js/namespaces/app-menu.d.ts +11 -4
  19. package/src/platform-implementation-js/namespaces/app-menu.d.ts.map +1 -1
  20. package/src/platform-implementation-js/views/app-menu-item-view.d.ts.map +1 -1
  21. package/src/platform-implementation-js/views/app-menu-item-view.js +25 -4
  22. package/src/platform-implementation-js/views/collapsible-panel-view.d.ts.map +1 -1
  23. package/src/platform-implementation-js/views/collapsible-panel-view.js +68 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inboxsdk/core",
3
- "version": "0.2.29-alpha.4",
3
+ "version": "0.2.29-alpha.6",
4
4
  "description": "Library for building browser extensions for Gmail",
5
5
  "main": "inboxsdk.js",
6
6
  "author": "Streak",
@@ -31,6 +31,7 @@
31
31
  "@types/kefir": "^3.8.6",
32
32
  "@types/node": "*",
33
33
  "asap": "^2.0.3",
34
+ "fast-deep-equal": "^3.1.3",
34
35
  "kefir-cast": "^3.3.0",
35
36
  "sha.js": "^2.4.0",
36
37
  "tag-tree": "^1.0.0",
package/src/inboxsdk.d.ts CHANGED
@@ -106,6 +106,9 @@ export interface ButterBar {
106
106
  // export interface DropdownButtonAccessoryDescriptor {}
107
107
 
108
108
  export { AppMenu };
109
+ export type { AppMenuItemView } from './platform-implementation-js/views/app-menu-item-view';
110
+ export type { AppMenuItemPanelDescriptor } from './platform-implementation-js/namespaces/app-menu';
111
+ export type { CollapsiblePanelView } from './platform-implementation-js/views/collapsible-panel-view';
109
112
 
110
113
  export interface NativeNavItemView extends EventEmitter {
111
114
  addNavItem(descriptor: NavItemDescriptor): NavItemView;
@@ -1 +1 @@
1
- {"version":3,"file":"add-app-menu-item.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAI1C,wBAAsB,cAAc,CAClC,MAAM,EAAE,WAAW,EACnB,kBAAkB,EAAE,qBAAqB,6CAuB1C"}
1
+ {"version":3,"file":"add-app-menu-item.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAI1C,wBAAsB,cAAc,CAClC,MAAM,EAAE,WAAW,EACnB,kBAAkB,EAAE,qBAAqB,6CAyB1C"}
@@ -3,12 +3,11 @@ import GmailElementGetter from '../gmail-element-getter';
3
3
  import Logger from '../../../lib/logger';
4
4
  export async function addAppMenuItem(driver, menuItemDescriptor) {
5
5
  const appMenuInjectionContainer = await GmailElementGetter.getAppMenuAsync();
6
- const gmailAppMenuItemView = new GmailAppMenuItemView(driver);
7
- gmailAppMenuItemView.setMenuItemDescriptor(menuItemDescriptor);
6
+ const gmailAppMenuItemView = new GmailAppMenuItemView(driver, menuItemDescriptor);
8
7
  try {
9
- if (!appMenuInjectionContainer || !gmailAppMenuItemView.element)
8
+ if (!appMenuInjectionContainer)
10
9
  return;
11
- const siblingElement = menuItemDescriptor.insertIndex
10
+ const siblingElement = Number.isInteger(menuItemDescriptor.insertIndex)
12
11
  ? appMenuInjectionContainer.childNodes[menuItemDescriptor.insertIndex] ??
13
12
  null
14
13
  : null;
@@ -1,5 +1,5 @@
1
1
  import { AppMenuItemPanelDescriptor } from '../../../namespaces/app-menu';
2
2
  import { CollapsiblePanelView } from '../../../views/collapsible-panel-view';
3
3
  import GmailDriver from '../gmail-driver';
4
- export declare function addCollapsiblePanel(driver: GmailDriver, panelDescriptor: AppMenuItemPanelDescriptor): Promise<CollapsiblePanelView | undefined>;
4
+ export declare function addCollapsiblePanel(driver: GmailDriver, panelDescriptor: AppMenuItemPanelDescriptor, insertIndex?: number): Promise<CollapsiblePanelView | undefined>;
5
5
  //# sourceMappingURL=add-collapsible-panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"add-collapsible-panel.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAI1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,0BAA0B,6CAkB5C"}
1
+ {"version":3,"file":"add-collapsible-panel.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAI1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,0BAA0B,EAC3C,WAAW,CAAC,EAAE,MAAM,6CAmCrB"}
@@ -3,7 +3,7 @@ import GmailElementGetter from '../gmail-element-getter';
3
3
  import waitFor from '../../../lib/wait-for';
4
4
  import Logger from '../../../lib/logger';
5
5
  import { CollapsiblePanelView } from '../../../views/collapsible-panel-view';
6
- export async function addCollapsiblePanel(driver, panelDescriptor) {
6
+ export async function addCollapsiblePanel(driver, panelDescriptor, insertIndex) {
7
7
  const injectionContainer = await waitForAppMenuParentReady();
8
8
  const collapsiblePanelView = new CollapsiblePanelView(driver, panelDescriptor);
9
9
  if (!injectionContainer || !collapsiblePanelView.element)
@@ -11,7 +11,18 @@ export async function addCollapsiblePanel(driver, panelDescriptor) {
11
11
  const appMenu = await GmailElementGetter.getAppMenuAsync();
12
12
  if (!appMenu)
13
13
  return;
14
- appMenu.insertAdjacentElement('afterend', collapsiblePanelView.element);
14
+ const panelNodes = injectionContainer.querySelectorAll(CollapsiblePanelView.elementSelectors.NATIVE) ?? [];
15
+ const siblingElement = Number.isInteger(insertIndex)
16
+ ? panelNodes[insertIndex] ?? null
17
+ : null;
18
+ if (siblingElement) {
19
+ siblingElement.insertAdjacentElement('beforebegin', collapsiblePanelView.element);
20
+ }
21
+ else {
22
+ [...panelNodes]
23
+ .at(-1)
24
+ ?.insertAdjacentElement('afterend', collapsiblePanelView.element);
25
+ }
15
26
  return collapsiblePanelView;
16
27
  }
17
28
  const waitForAppMenuParentReady = once(async () => {
@@ -1,2 +1,6 @@
1
- export default function trackGmailStyles(): void;
1
+ export declare const stylesStream: import("kefir-bus").Bus<{
2
+ type: 'theme';
3
+ isDarkMode: boolean;
4
+ }, unknown>;
5
+ export default function trackGmailStyles(): Promise<void>;
2
6
  //# sourceMappingURL=track-gmail-styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"track-gmail-styles.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.ts"],"names":[],"mappings":"AAsDA,MAAM,CAAC,OAAO,UAAU,gBAAgB,SAwDvC"}
1
+ {"version":3,"file":"track-gmail-styles.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.ts"],"names":[],"mappings":"AAyDA,eAAO,MAAM,YAAY;UACf,OAAO;gBAAc,OAAO;WAEnC,CAAC;AAEJ,wBAA8B,gBAAgB,kBAmE7C"}
@@ -1,3 +1,4 @@
1
+ import kefirBus from 'kefir-bus';
1
2
  import Logger from '../../../lib/logger';
2
3
  import waitFor from '../../../lib/wait-for';
3
4
  import GmailElementGetter from '../gmail-element-getter';
@@ -21,10 +22,11 @@ function getDensity() {
21
22
  return 'compact';
22
23
  }
23
24
  }
25
+ const navItemSelector = '.aio';
24
26
  function isDarkTheme() {
25
27
  // get the color of the left-nav-menu entries to determine whether Gmail is
26
28
  // in dark theme mode.
27
- const navItem = document.querySelector('.aio');
29
+ const navItem = document.querySelector(navItemSelector);
28
30
  if (!navItem) {
29
31
  Logger.error(new Error('Failed to find nav item'));
30
32
  return false;
@@ -45,7 +47,8 @@ function isDarkTheme() {
45
47
  }
46
48
  return r > 128;
47
49
  }
48
- export default function trackGmailStyles() {
50
+ export const stylesStream = kefirBus();
51
+ export default async function trackGmailStyles() {
49
52
  if (document.head.hasAttribute('data-inboxsdk-gmail-style-tracker') ||
50
53
  GmailElementGetter.isStandalone()) {
51
54
  return;
@@ -71,25 +74,34 @@ export default function trackGmailStyles() {
71
74
  else {
72
75
  document.body.classList.remove('inboxsdk__gmail_dark_theme');
73
76
  }
77
+ stylesStream.emit({
78
+ type: 'theme',
79
+ isDarkMode: newDarkTheme,
80
+ });
74
81
  }
75
82
  }
76
- waitFor(() => document.querySelector('.TO .TN') && document.querySelector('.aio'))
77
- .then(() => {
78
- // Gmail changes an inline <style> sheet when the display density changes.
79
- // Watch for changes to all <style> elements.
80
- const observer = new MutationObserver(checkStyles);
81
- const options = {
82
- characterData: true,
83
- childList: true,
84
- };
85
- Array.from(document.styleSheets).forEach((sheet) => {
86
- if (sheet.ownerNode.tagName == 'STYLE') {
87
- observer.observe(sheet.ownerNode, options);
88
- }
89
- });
90
- checkStyles();
91
- })
92
- .catch((err) => {
83
+ try {
84
+ await waitFor(() => document.querySelector('.TO .TN') &&
85
+ document.querySelector(navItemSelector));
86
+ }
87
+ catch (err) {
93
88
  Logger.error(err);
94
- });
89
+ return;
90
+ }
91
+ // Gmail changes an inline <style> sheet when the display density changes.
92
+ // Watch for changes to all <style> elements.
93
+ const observer = new MutationObserver(checkStyles);
94
+ const options = {
95
+ characterData: true,
96
+ childList: true,
97
+ };
98
+ for (const sheet of document.styleSheets) {
99
+ if (!(sheet.ownerNode instanceof Element)) {
100
+ continue;
101
+ }
102
+ if (sheet.ownerNode.tagName == 'STYLE') {
103
+ observer.observe(sheet.ownerNode, options);
104
+ }
105
+ }
106
+ checkStyles();
95
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gmail-element-getter.d.ts","sourceRoot":"","sources":["../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAA+B,EAC7B,mBAAmB,EACpB,MAAM,yCAAyC,CAAC;AAmBjD,QAAA,MAAM,kBAAkB;yBACD,WAAW,GAAG,IAAI;uCAcJ,WAAW,GAAG,IAAI;kDAKP,WAAW,GAAG,IAAI;+CAKrB,WAAW,GAAG,IAAI;wBAIzC,WAAW,GAAG,IAAI;iCAOT,WAAW,GAAG,IAAI;gCAInB,WAAW,GAAG,SAAS,GAAG,IAAI;2CA8BnB,WAAW,GAAG,IAAI;iDAIZ,MAAM,UAAU,CAC3D,mBAAmB,EACnB,KAAK,CACN;uBAgBkB,WAAW,GAAG,IAAI;kCAIP,WAAW,GAAG,IAAI;+BAOrB,WAAW,GAAG,IAAI;0CAIP,WAAW,GAAG,IAAI;+BAI7B,WAAW,GAAG,IAAI;+CAQrC,GAAG,KACR,MAAM,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC;qBAItB,WAAW,GAAG,IAAI;+BAOR,OAAO;;;;;;uDAgFiB,WAAW,GAAG,IAAI;mDAItB,WAAW,GAAG,IAAI;0BAQ3C,WAAW,EAAE;0BAIb,WAAW,GAAG,IAAI;sBAItB,gBAAgB,GAAG,IAAI;qCAMR,WAAW,GAAG,IAAI;kCAIrB,WAAW,GAAG,IAAI;2BAIzB,WAAW,GAAG,IAAI;iCAWZ,WAAW,GAAG,IAAI;yBAI1B,WAAW;8BAIN,WAAW,GAAG,IAAI;sBAM1B,OAAO;mBAaV,OAAO;qBAIL,OAAO;oBAIR,OAAO;iCAIM,OAAO;gCAOR,OAAO;;qCASJ,WAAW,GAAG,IAAI;;gCAKrB,QAAQ,IAAI,CAAC;CAG1C,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"gmail-element-getter.d.ts","sourceRoot":"","sources":["../../../../../../src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAA+B,EAC7B,mBAAmB,EACpB,MAAM,yCAAyC,CAAC;AAmBjD,QAAA,MAAM,kBAAkB;yBACD,WAAW,GAAG,IAAI;uCAcJ,WAAW,GAAG,IAAI;kDAKP,WAAW,GAAG,IAAI;+CAKrB,WAAW,GAAG,IAAI;wBAIzC,WAAW,GAAG,IAAI;iCAOT,WAAW,GAAG,IAAI;gCAInB,WAAW,GAAG,SAAS,GAAG,IAAI;2CA8BnB,WAAW,GAAG,IAAI;iDAIZ,MAAM,UAAU,CAC3D,mBAAmB,EACnB,KAAK,CACN;uBAgBkB,WAAW,GAAG,IAAI;kCAIP,WAAW,GAAG,IAAI;+BAUrB,WAAW,GAAG,IAAI;0CAIP,WAAW,GAAG,IAAI;+BAI7B,WAAW,GAAG,IAAI;+CAQrC,GAAG,KACR,MAAM,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC;qBAItB,WAAW,GAAG,IAAI;+BAOR,OAAO;;;;;;uDAgFiB,WAAW,GAAG,IAAI;mDAItB,WAAW,GAAG,IAAI;0BAQ3C,WAAW,EAAE;0BAIb,WAAW,GAAG,IAAI;sBAItB,gBAAgB,GAAG,IAAI;qCAMR,WAAW,GAAG,IAAI;kCAIrB,WAAW,GAAG,IAAI;2BAIzB,WAAW,GAAG,IAAI;iCAWZ,WAAW,GAAG,IAAI;yBAI1B,WAAW;8BAIN,WAAW,GAAG,IAAI;sBAM1B,OAAO;mBAaV,OAAO;qBAIL,OAAO;oBAIR,OAAO;iCAIM,OAAO;gCAOR,OAAO;;qCASJ,WAAW,GAAG,IAAI;;gCAKrB,QAAQ,IAAI,CAAC;CAG1C,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -89,6 +89,9 @@ const GmailElementGetter = {
89
89
  return document.querySelector('.aeN .aj5.J-KU-Jg');
90
90
  },
91
91
  getLeftNavContainerElement() {
92
+ if (this.getAppMenu()) {
93
+ return this.getAppMenuContainer()?.querySelector('.aqn.aIH') ?? null;
94
+ }
92
95
  if (isIntegratedViewGmail()) {
93
96
  return document.querySelector('div[role=navigation] + div.aqn');
94
97
  }
@@ -17,19 +17,11 @@ export declare class GmailAppMenuItemView extends GmailAppMenuItemView_base {
17
17
  readonly HOVER: "aIk";
18
18
  readonly ACTIVE: "apV";
19
19
  };
20
- get element(): HTMLElement | undefined;
21
- setMenuItemDescriptor(newMenuItemDescriptor: AppMenuItemDescriptor): void;
22
- get menuItemDescriptor(): AppMenuItemDescriptor | undefined;
23
- constructor(driver: GmailDriver);
20
+ get element(): HTMLElement;
21
+ set menuItemDescriptor(newMenuItemDescriptor: AppMenuItemDescriptor);
22
+ get menuItemDescriptor(): AppMenuItemDescriptor;
23
+ constructor(driver: GmailDriver, menuItemDescriptor: AppMenuItemDescriptor);
24
24
  remove(): void;
25
- /**
26
- * @internal
27
- */
28
- blur(): void;
29
- /**
30
- * @internal
31
- */
32
- hover(): void;
33
25
  }
34
26
  export {};
35
27
  //# sourceMappingURL=gmail-app-menu-item-view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gmail-app-menu-item-view.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,OAAO,iBAAiB,MAAM,eAAe,CAAC;AAK9C,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,YAAY,MAAgB,CAAC;AAC1C,eAAO,MAAM,cAAc,yBAAmC,CAAC;AAC/D,eAAO,MAAM,aAAa,4BAA+C,CAAC;mDAOL,kBAAkB,aAAa,CAAC;AAArG,qBAAa,oBAAqB,SAAQ,yBAA4D;;IACpG,MAAM,CAAC,UAAU;;;MAGN;IAOX,IAAI,OAAO,4BAEV;IAED,qBAAqB,CAAC,qBAAqB,EAAE,qBAAqB;IAKlE,IAAI,kBAAkB,sCAErB;gBAEW,MAAM,EAAE,WAAW;IAc/B,MAAM;IAON;;OAEG;IACH,IAAI;IAIJ;;OAEG;IACH,KAAK;CA4FN"}
1
+ {"version":3,"file":"gmail-app-menu-item-view.d.ts","sourceRoot":"","sources":["../../../../../../../src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,OAAO,iBAAiB,MAAM,eAAe,CAAC;AAO9C,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,YAAY,MAAgB,CAAC;AAC1C,eAAO,MAAM,cAAc,yBAAmC,CAAC;AAC/D,eAAO,MAAM,aAAa,4BAA+C,CAAC;mDAOL,kBAAkB,aAAa,CAAC;AAArG,qBAAa,oBAAqB,SAAQ,yBAA4D;;IACpG,MAAM,CAAC,UAAU;;;MAGN;IAOX,IAAI,OAAO,gBAEV;IAED,IAAI,kBAAkB,CAAC,qBAAqB,uBAAA,EAG3C;IAED,IAAI,kBAAkB,0BAErB;gBAEW,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,qBAAqB;IAwB1E,MAAM;CA6GP"}
@@ -2,6 +2,8 @@ import querySelector from '../../../lib/dom/querySelectorOrFail';
2
2
  import { EventEmitter } from 'events';
3
3
  import cx from 'classnames';
4
4
  import GmailElementGetter from '../gmail-element-getter';
5
+ import { stylesStream } from '../gmail-driver/track-gmail-styles';
6
+ import isEqual from 'fast-deep-equal';
5
7
  export const NATIVE_CLASS = 'Xa';
6
8
  export const INBOXSDK_CLASS = 'inboxsdk__appMenuItem';
7
9
  export const ELEMENT_CLASS = `${NATIVE_CLASS} ${INBOXSDK_CLASS}`;
@@ -21,17 +23,26 @@ export class GmailAppMenuItemView extends EventEmitter {
21
23
  get element() {
22
24
  return this.#element;
23
25
  }
24
- setMenuItemDescriptor(newMenuItemDescriptor) {
26
+ set menuItemDescriptor(newMenuItemDescriptor) {
25
27
  this.#menuItemDescriptor = newMenuItemDescriptor;
26
28
  this.#update();
27
29
  }
28
30
  get menuItemDescriptor() {
29
31
  return this.#menuItemDescriptor;
30
32
  }
31
- constructor(driver) {
33
+ constructor(driver, menuItemDescriptor) {
32
34
  super();
33
35
  this.#element = this.#setupElement();
34
36
  this.#driver = driver;
37
+ this.#menuItemDescriptor = menuItemDescriptor;
38
+ this.#update();
39
+ stylesStream
40
+ .skipDuplicates((a, b) => isEqual(a, b))
41
+ .onValue(({ type }) => {
42
+ if (type === 'theme') {
43
+ this.#update();
44
+ }
45
+ });
35
46
  driver.getStopper().onValue(() => this.remove());
36
47
  this.#element.addEventListener('click', (e) => this.#onClick(e));
37
48
  this.#element.addEventListener('mouseleave', this.#onBlur);
@@ -44,21 +55,6 @@ export class GmailAppMenuItemView extends EventEmitter {
44
55
  this.element?.remove();
45
56
  this.emit('destroy');
46
57
  }
47
- /**
48
- * @internal
49
- */
50
- blur() {
51
- this.element?.classList.remove(GmailAppMenuItemView.elementCss.HOVER);
52
- }
53
- /**
54
- * @internal
55
- */
56
- hover() {
57
- if (this.element?.classList.contains(GmailAppMenuItemView.elementCss.ACTIVE)) {
58
- return;
59
- }
60
- this.element?.classList.add(GmailAppMenuItemView.elementCss.HOVER);
61
- }
62
58
  #setupElement() {
63
59
  const element = document.createElement('div');
64
60
  element.className = ELEMENT_CLASS;
@@ -82,6 +78,7 @@ export class GmailAppMenuItemView extends EventEmitter {
82
78
  const headingElement = querySelector(element, HEADING_ELEMENT_SELECTOR);
83
79
  headingElement.textContent = this.#menuItemDescriptor?.name ?? '';
84
80
  }
81
+ static #preloadedIcons = new Set();
85
82
  #updateIcon(element) {
86
83
  const iconContainerEl = querySelector(element, ICON_ELEMENT_SELECTOR);
87
84
  const { iconUrl, iconClassName } = this.#menuItemDescriptor ?? {};
@@ -89,8 +86,22 @@ export class GmailAppMenuItemView extends EventEmitter {
89
86
  const rawTheme = GmailElementGetter.isDarkTheme()
90
87
  ? iconUrl.darkTheme
91
88
  : iconUrl.lightTheme;
89
+ if (!rawTheme)
90
+ return;
92
91
  const theme = typeof rawTheme === 'string' ? rawTheme : rawTheme.default;
93
92
  const activeImg = typeof rawTheme === 'string' ? rawTheme : rawTheme.active;
93
+ // alleviate FOUC when switching between active and default icons
94
+ // https://stackoverflow.com/a/68521953/1924257
95
+ for (const url of [theme, activeImg]) {
96
+ if (GmailAppMenuItemView.#preloadedIcons.has(url))
97
+ continue;
98
+ GmailAppMenuItemView.#preloadedIcons.add(url);
99
+ var link = document.createElement('link');
100
+ link.rel = 'preload';
101
+ link.as = 'image';
102
+ link.href = url;
103
+ document.head.appendChild(link);
104
+ }
94
105
  iconContainerEl.style.setProperty('--background-image', `url(${theme})`);
95
106
  iconContainerEl.style.setProperty('--background-image--active', `url(${activeImg})`);
96
107
  }
@@ -8,8 +8,8 @@ type ThemedIcon = string | {
8
8
  export type AppMenuItemDescriptor = {
9
9
  name: string;
10
10
  iconUrl?: {
11
- darkTheme: ThemedIcon;
12
- lightTheme: ThemedIcon;
11
+ darkTheme?: ThemedIcon;
12
+ lightTheme?: ThemedIcon;
13
13
  };
14
14
  className?: string;
15
15
  iconClassName?: string;
@@ -28,6 +28,13 @@ export type AppMenuItemDescriptor = {
28
28
  */
29
29
  isRouteActive: (routeView: RouteView) => boolean;
30
30
  };
31
+ /**
32
+ * In dark themes, Gmail has different colored primary buttons depending on hover or active state
33
+ */
34
+ type PanelPrimaryButtonThemedIcon = string | {
35
+ panelHovered: string;
36
+ panelDefault: string;
37
+ };
31
38
  export type AppMenuItemPanelDescriptor = {
32
39
  className?: string;
33
40
  /** In the form of HTML as a string.
@@ -38,8 +45,8 @@ export type AppMenuItemPanelDescriptor = {
38
45
  name: string;
39
46
  onClick: (e: MouseEvent) => void;
40
47
  iconUrl?: {
41
- darkTheme: string;
42
- lightTheme: string;
48
+ darkTheme?: PanelPrimaryButtonThemedIcon;
49
+ lightTheme?: PanelPrimaryButtonThemedIcon;
43
50
  };
44
51
  className?: string;
45
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"app-menu.d.ts","sourceRoot":"","sources":["../../../../../src/platform-implementation-js/namespaces/app-menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,KAAK,UAAU,GACX,MAAM,GACN;IACE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACR,SAAS,EAAE,UAAU,CAAC;QACtB,UAAU,EAAE,UAAU,CAAC;KACxB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,EAAE,CAAC;IACjB;;;;OAIG;IACH,aAAa,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;0EAEsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;QACjC,OAAO,CAAC,EAAE;YACR,SAAS,EAAE,MAAM,CAAC;YAClB,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC;QACF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,OAAO;;gBAGd,MAAM,EAAE,WAAW;IAI/B,WAAW,CAAC,kBAAkB,EAAE,qBAAqB,GAAG,eAAe;IAKjE,OAAO;CAId"}
1
+ {"version":3,"file":"app-menu.d.ts","sourceRoot":"","sources":["../../../../../src/platform-implementation-js/namespaces/app-menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,KAAK,UAAU,GACX,MAAM,GACN;IACE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,UAAU,CAAC;QACvB,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,EAAE,CAAC;IACjB;;;;OAIG;IACH,aAAa,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF;;GAEG;AACH,KAAK,4BAA4B,GAC7B,MAAM,GACN;IACE,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;0EAEsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;QACjC,OAAO,CAAC,EAAE;YACR,SAAS,CAAC,EAAE,4BAA4B,CAAC;YACzC,UAAU,CAAC,EAAE,4BAA4B,CAAC;SAC3C,CAAC;QACF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,OAAO;;gBAGd,MAAM,EAAE,WAAW;IAI/B,WAAW,CAAC,kBAAkB,EAAE,qBAAqB,GAAG,eAAe;IAKjE,OAAO;CAId"}
@@ -1 +1 @@
1
- {"version":3,"file":"app-menu-item-view.d.ts","sourceRoot":"","sources":["../../../../../src/platform-implementation-js/views/app-menu-item-view.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AAWzC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,oBAAoB,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAK3D,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;8CA0C8D,aAAa,aAAa,CAAC;AAH3F;;GAEG;AACH,qBAAa,eAAgB,SAAQ,oBAAuD;;IAqV1F,IAAI,kBAAkB,0BAErB;gBAQW,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,qBAAqB;IAsDpE,mBAAmB,CAAC,eAAe,EAAE,0BAA0B;IA2C/D,MAAM,CAAC,kBAAkB,EAAE,qBAAqB;IAMtD,MAAM;CAYP"}
1
+ {"version":3,"file":"app-menu-item-view.d.ts","sourceRoot":"","sources":["../../../../../src/platform-implementation-js/views/app-menu-item-view.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AAWzC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,oBAAoB,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAK3D,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;8CA0C8D,aAAa,aAAa,CAAC;AAH3F;;GAEG;AACH,qBAAa,eAAgB,SAAQ,oBAAuD;;IA8W1F,IAAI,kBAAkB,0BAErB;gBAQW,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,qBAAqB;IAsDpE,mBAAmB,CAAC,eAAe,EAAE,0BAA0B;IA2C/D,MAAM,CAAC,kBAAkB,EAAE,qBAAqB;IAMtD,MAAM;CAaP"}
@@ -60,6 +60,26 @@ export class AppMenuItemView extends EventEmitter {
60
60
  const activePanel = AppMenuItemView.#getActivePanel();
61
61
  return !activePanel;
62
62
  }
63
+ static #adjustTooltipNub() {
64
+ // adjust all menu items' collapsible panel's tooltip nub position
65
+ const appMenuElement = GmailElementGetter.getAppMenu();
66
+ for (const menuItem of appMenuElement?.querySelectorAll(`.${NATIVE_CLASS}`) ?? []) {
67
+ const panel = AppMenuItemView.#menuItemToPanelMap.get(menuItem);
68
+ if (!panel)
69
+ continue;
70
+ const nubElement = panel?.querySelector('* > .aTV');
71
+ const iconElement = menuItem?.querySelector('.V6');
72
+ const iconElementBox = iconElement?.getBoundingClientRect();
73
+ const offsetTop = (iconElement?.offsetTop ?? 0) + (iconElementBox?.height ?? 0) * 0.5;
74
+ const nubSide = 17;
75
+ const nubDiagonal = nubSide * Math.sqrt(2);
76
+ const offsetNub = nubDiagonal / 4;
77
+ const offset = offsetTop - offsetNub;
78
+ if (nubElement && offset) {
79
+ nubElement.style.top = `${offset}px`;
80
+ }
81
+ }
82
+ }
63
83
  static #setUpRouteViewDriverStream(driver) {
64
84
  if (AppMenuItemView.#routeViewDriverStream)
65
85
  return;
@@ -298,6 +318,7 @@ export class AppMenuItemView extends EventEmitter {
298
318
  const gmailElement = gmailView.element;
299
319
  if (gmailElement) {
300
320
  AppMenuItemView.#menuItemToPanelMap.set(gmailElement, undefined);
321
+ AppMenuItemView.#adjustTooltipNub();
301
322
  }
302
323
  gmailView.on('destroy', () => {
303
324
  this.remove();
@@ -335,7 +356,7 @@ export class AppMenuItemView extends EventEmitter {
335
356
  [CollapsiblePanelView.elementCss.PANEL_LESS]: AppMenuItemView.#isPanelLess(),
336
357
  }),
337
358
  };
338
- const collapsiblePanel = await addCollapsiblePanel(this.#driver, descriptor);
359
+ const collapsiblePanel = await addCollapsiblePanel(this.#driver, descriptor, this.menuItemDescriptor.insertIndex);
339
360
  if (collapsiblePanel) {
340
361
  this.#gmailViewPromise.then((gmailView) => {
341
362
  const gmailElement = gmailView.element;
@@ -347,9 +368,8 @@ export class AppMenuItemView extends EventEmitter {
347
368
  e,
348
369
  ]);
349
370
  });
350
- }
351
- if (gmailElement && collapsiblePanel) {
352
371
  AppMenuItemView.#menuItemToPanelMap.set(gmailElement, collapsiblePanel.element);
372
+ AppMenuItemView.#adjustTooltipNub();
353
373
  }
354
374
  });
355
375
  }
@@ -358,7 +378,7 @@ export class AppMenuItemView extends EventEmitter {
358
378
  }
359
379
  async update(menuItemDescriptor) {
360
380
  const gmailView = await this.#gmailViewPromise;
361
- gmailView.setMenuItemDescriptor(menuItemDescriptor);
381
+ gmailView.menuItemDescriptor = menuItemDescriptor;
362
382
  this.#menuItemDescriptor = menuItemDescriptor;
363
383
  }
364
384
  remove() {
@@ -371,6 +391,7 @@ export class AppMenuItemView extends EventEmitter {
371
391
  gmailView.element &&
372
392
  AppMenuItemView.#menuItemToPanelMap.delete(gmailView.element);
373
393
  gmailView.remove();
394
+ AppMenuItemView.#adjustTooltipNub();
374
395
  });
375
396
  AppMenuItemView.#appMenuItemViews.delete(this);
376
397
  }
@@ -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;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"}
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;AAI1C,eAAO,MAAM,YAAY,OAAiB,CAAC;AAC3C,eAAO,MAAM,cAAc,8BAAwC,CAAC;AAcpE,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,+BAElB;IAED,IAAI,eAAe,CAAC,eAAe,4BAAA,EAGlC;IAED,IAAI,OAAO,gBAEV;IAED,IAAI,qBAAqB,mBAExB;gBAGC,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,0BAA0B;IAmB7C,MAAM;IAON,UAAU,CACR,iBAAiB,EACb,iBAAiB,GACjB,KAAK,CAAC,UAAU,CAAC,iBAAiB,EAAE,GAAG,CAAC;CA+N/C"}
@@ -6,6 +6,8 @@ import autoHtml from 'auto-html';
6
6
  import querySelector from '../lib/dom/querySelectorOrFail';
7
7
  import GmailElementGetter from '../dom-driver/gmail/gmail-element-getter';
8
8
  import NavItemView from './nav-item-view';
9
+ import { stylesStream } from '../dom-driver/gmail/gmail-driver/track-gmail-styles';
10
+ import isEqual from 'fast-deep-equal';
9
11
  export const NATIVE_CLASS = 'aqn';
10
12
  export const INBOXSDK_CLASS = 'inboxsdk__collapsiblePanel';
11
13
  const ELEMENT_CLASS = `${NATIVE_CLASS} ${INBOXSDK_CLASS} oy8Mbf`;
@@ -14,6 +16,7 @@ const PRIMARY_BUTTON_ELEMENT_SELECTOR = '.T-I.T-I-KE.L3';
14
16
  const scrollablePanelClass = 'at9';
15
17
  const scrollablePanelSelector = `.${scrollablePanelClass}`;
16
18
  const loadingElementClass = 'inboxsdk__collapsiblePanel_loading_container';
19
+ const panelLoadingClass = `${loadingElementClass}--active`;
17
20
  const loadingElementSelector = `.${loadingElementClass}`;
18
21
  const NAV_MENU_CONTAINER_ELEMENT_SELECTOR = '.at9 .n3 .TK';
19
22
  export class CollapsiblePanelView extends EventEmitter {
@@ -68,6 +71,13 @@ export class CollapsiblePanelView extends EventEmitter {
68
71
  this.#element.addEventListener('mouseleave', (e) => {
69
72
  this.emit('blur', e);
70
73
  });
74
+ stylesStream
75
+ .skipDuplicates((a, b) => isEqual(a, b))
76
+ .onValue(({ type }) => {
77
+ if (type === 'theme') {
78
+ this.#update();
79
+ }
80
+ });
71
81
  }
72
82
  remove() {
73
83
  if (this.#destroyed)
@@ -86,22 +96,41 @@ export class CollapsiblePanelView extends EventEmitter {
86
96
  const navItemView = new NavItemView(appId, this.#driver, navItemDescriptorPropertyStream, gmailNavItemView);
87
97
  return navItemView;
88
98
  }
99
+ #extractPrimaryButtonTheme() {
100
+ const { iconUrl } = this.panelDescriptor.primaryButton ?? {};
101
+ if (!iconUrl) {
102
+ return;
103
+ }
104
+ const theme = GmailElementGetter.isDarkTheme()
105
+ ? iconUrl.darkTheme
106
+ : iconUrl.lightTheme;
107
+ if (!theme) {
108
+ return;
109
+ }
110
+ if (typeof theme === 'string') {
111
+ return {
112
+ panelHovered: theme,
113
+ panelDefault: theme,
114
+ };
115
+ }
116
+ else {
117
+ return {
118
+ panelDefault: theme.panelDefault,
119
+ panelHovered: theme.panelHovered,
120
+ };
121
+ }
122
+ }
89
123
  #setupElement() {
90
124
  const { panelDescriptor } = this;
91
125
  const { loadingIcon } = panelDescriptor;
92
- const { iconUrl: themedIcons, className, name = '', } = panelDescriptor?.primaryButton ?? {};
126
+ const { className, name = '' } = panelDescriptor?.primaryButton ?? {};
93
127
  const element = document.createElement('div');
94
128
  element.className = cx(ELEMENT_CLASS, this.panelDescriptor.className);
95
129
  const primaryButtonClass = cx(PRIMARY_BUTTON_ELEMENT_CLASS, className);
96
- let iconUrl;
97
- if (themedIcons) {
98
- if (GmailElementGetter.isDarkTheme()) {
99
- iconUrl = themedIcons.darkTheme;
100
- }
101
- else {
102
- iconUrl = themedIcons.lightTheme;
103
- }
104
- }
130
+ const loadingElClass = cx(loadingElementClass, {
131
+ [panelLoadingClass]: this.#loading,
132
+ });
133
+ const { panelDefault: defaultIconUrl, panelHovered: hoverPanelIconUrl } = this.#extractPrimaryButtonTheme() ?? {};
105
134
  element.innerHTML = autoHtml `
106
135
  <div class="aBO">
107
136
  <div class="aic">
@@ -112,7 +141,7 @@ export class CollapsiblePanelView extends EventEmitter {
112
141
  </div>
113
142
  </div>
114
143
  <div class="${scrollablePanelClass}">
115
- <div class="${loadingElementClass}">${loadingIcon ? { __html: loadingIcon } : ''}</div>
144
+ <div class="${loadingElClass}">${loadingIcon ? { __html: loadingIcon } : ''}</div>
116
145
  <div class="Ls77Lb aZ6">
117
146
  <div class="pp" style="user-select: none;">
118
147
  <div>
@@ -136,10 +165,11 @@ export class CollapsiblePanelView extends EventEmitter {
136
165
  </div>
137
166
  </div>
138
167
  </div>
139
- <div class="aTV" style="top: 27px;"></div>
168
+ <div class="aTV"></div>
140
169
  `.trim();
141
170
  const primaryBtnEl = querySelector(element, PRIMARY_BUTTON_ELEMENT_SELECTOR);
142
- primaryBtnEl.style.setProperty('--background-image', iconUrl ? `url(${iconUrl})` : 'unset');
171
+ primaryBtnEl.style.setProperty('--background-image', defaultIconUrl ? `url(${defaultIconUrl})` : 'unset');
172
+ primaryBtnEl.style.setProperty('--background-image--hover', hoverPanelIconUrl ? `url(${hoverPanelIconUrl})` : 'unset');
143
173
  primaryBtnEl.addEventListener('click', this.#onPrimaryButtonClick);
144
174
  return element;
145
175
  }
@@ -156,18 +186,35 @@ export class CollapsiblePanelView extends EventEmitter {
156
186
  this.#updateIcon(element);
157
187
  this.#updateScrollablePanelLoading(element);
158
188
  }
189
+ static #preloadedIcons = new Set();
159
190
  #updateIcon(element) {
160
191
  const iconContainerEl = querySelector(element, PRIMARY_BUTTON_ELEMENT_SELECTOR);
161
- const { iconUrl, className } = this.panelDescriptor.primaryButton ?? {};
162
- if (iconUrl) {
163
- const backgroundImage = `url(${GmailElementGetter.isDarkTheme()
164
- ? iconUrl.darkTheme
165
- : iconUrl.lightTheme})`;
166
- iconContainerEl.style.setProperty('--background-image', backgroundImage);
192
+ const { panelDefault, panelHovered } = this.#extractPrimaryButtonTheme() ?? {};
193
+ // alleviate FOUC when switching between active and default icons
194
+ // https://stackoverflow.com/a/68521953/1924257
195
+ for (const url of [panelHovered, panelDefault]) {
196
+ if (!url || CollapsiblePanelView.#preloadedIcons.has(url)) {
197
+ continue;
198
+ }
199
+ CollapsiblePanelView.#preloadedIcons.add(url);
200
+ var link = document.createElement('link');
201
+ link.rel = 'preload';
202
+ link.as = 'image';
203
+ link.href = url;
204
+ document.head.appendChild(link);
167
205
  }
168
- else {
169
- iconContainerEl.style.setProperty('--background-image', 'unset');
206
+ for (const [key, value] of Object.entries({
207
+ '--background-image--hover': panelHovered,
208
+ '--background-image': panelDefault,
209
+ })) {
210
+ if (value) {
211
+ iconContainerEl.style.setProperty(key, `url(${value})`);
212
+ }
213
+ else {
214
+ iconContainerEl.style.removeProperty(key);
215
+ }
170
216
  }
217
+ const { className } = this.panelDescriptor.primaryButton ?? {};
171
218
  iconContainerEl.className = cx(PRIMARY_BUTTON_ELEMENT_CLASS, className);
172
219
  }
173
220
  #updateName(element) {
@@ -182,7 +229,6 @@ export class CollapsiblePanelView extends EventEmitter {
182
229
  if (!scrollablePanelLoadingEl) {
183
230
  return;
184
231
  }
185
- const panelLoadingClass = `${loadingElementClass}--active`;
186
232
  if (this.loading) {
187
233
  scrollablePanelLoadingEl.classList.add(panelLoadingClass);
188
234
  }