@oicl/openbridge-webcomponents 2.0.0-next.168 → 2.0.0-next.169
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/bundle/openbridge-webcomponents.bundle.js +3352 -2948
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +465 -3
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.d.ts.map +1 -1
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.js +18 -1
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.js.map +1 -1
- package/dist/components/alert-menu/alert-menu.css.js +21 -1
- package/dist/components/alert-menu/alert-menu.css.js.map +1 -1
- package/dist/components/alert-menu/alert-menu.d.ts +14 -0
- package/dist/components/alert-menu/alert-menu.d.ts.map +1 -1
- package/dist/components/alert-menu/alert-menu.js +10 -0
- package/dist/components/alert-menu/alert-menu.js.map +1 -1
- package/dist/components/app-menu/app-menu.css.js +23 -1
- package/dist/components/app-menu/app-menu.css.js.map +1 -1
- package/dist/components/app-menu/app-menu.d.ts +14 -0
- package/dist/components/app-menu/app-menu.d.ts.map +1 -1
- package/dist/components/app-menu/app-menu.js +17 -1
- package/dist/components/app-menu/app-menu.js.map +1 -1
- package/dist/components/brilliance-menu/brilliance-menu.css.js +21 -1
- package/dist/components/brilliance-menu/brilliance-menu.css.js.map +1 -1
- package/dist/components/brilliance-menu/brilliance-menu.d.ts +14 -0
- package/dist/components/brilliance-menu/brilliance-menu.d.ts.map +1 -1
- package/dist/components/brilliance-menu/brilliance-menu.js +10 -0
- package/dist/components/brilliance-menu/brilliance-menu.js.map +1 -1
- package/dist/components/command-menu/command-menu.css.js +21 -1
- package/dist/components/command-menu/command-menu.css.js.map +1 -1
- package/dist/components/command-menu/command-menu.d.ts +14 -0
- package/dist/components/command-menu/command-menu.d.ts.map +1 -1
- package/dist/components/command-menu/command-menu.js +10 -0
- package/dist/components/command-menu/command-menu.js.map +1 -1
- package/dist/components/context-menu-input/context-menu-input.css.js +58 -3
- package/dist/components/context-menu-input/context-menu-input.css.js.map +1 -1
- package/dist/components/context-menu-input/context-menu-input.d.ts +14 -1
- package/dist/components/context-menu-input/context-menu-input.d.ts.map +1 -1
- package/dist/components/context-menu-input/context-menu-input.js +11 -0
- package/dist/components/context-menu-input/context-menu-input.js.map +1 -1
- package/dist/components/navigation-menu/navigation-menu.css.js +21 -1
- package/dist/components/navigation-menu/navigation-menu.css.js.map +1 -1
- package/dist/components/navigation-menu/navigation-menu.d.ts +14 -0
- package/dist/components/navigation-menu/navigation-menu.d.ts.map +1 -1
- package/dist/components/navigation-menu/navigation-menu.js +10 -0
- package/dist/components/navigation-menu/navigation-menu.js.map +1 -1
- package/dist/components/system-menu/system-menu.css.js +81 -61
- package/dist/components/system-menu/system-menu.css.js.map +1 -1
- package/dist/components/system-menu/system-menu.d.ts +14 -0
- package/dist/components/system-menu/system-menu.d.ts.map +1 -1
- package/dist/components/system-menu/system-menu.js +10 -0
- package/dist/components/system-menu/system-menu.js.map +1 -1
- package/dist/components/top-bar/top-bar.d.ts +11 -4
- package/dist/components/top-bar/top-bar.d.ts.map +1 -1
- package/dist/components/top-bar/top-bar.js +18 -10
- package/dist/components/top-bar/top-bar.js.map +1 -1
- package/dist/components/user-button/user-button.d.ts +1 -0
- package/dist/components/user-button/user-button.d.ts.map +1 -1
- package/dist/components/user-button/user-button.js +11 -1
- package/dist/components/user-button/user-button.js.map +1 -1
- package/dist/components/user-menu/user-menu.css.js +21 -1
- package/dist/components/user-menu/user-menu.css.js.map +1 -1
- package/dist/components/user-menu/user-menu.d.ts +14 -0
- package/dist/components/user-menu/user-menu.d.ts.map +1 -1
- package/dist/components/user-menu/user-menu.js +10 -0
- package/dist/components/user-menu/user-menu.js.map +1 -1
- package/dist/internal/popover-controller.d.ts +128 -0
- package/dist/internal/popover-controller.d.ts.map +1 -0
- package/dist/internal/popover-controller.js +121 -0
- package/dist/internal/popover-controller.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { LitElement, ReactiveController } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* What {@link PopoverController} needs a component to have.
|
|
4
|
+
*
|
|
5
|
+
* `softDismiss` has to be something a component opts into. The browser hides
|
|
6
|
+
* a popover until it is opened, so switching this on for everyone would make
|
|
7
|
+
* every menu that a consumer already shows and positions disappear.
|
|
8
|
+
*/
|
|
9
|
+
export interface SoftDismissHost extends LitElement {
|
|
10
|
+
/** Let the browser take over showing, hiding and closing this panel. */
|
|
11
|
+
softDismiss: boolean;
|
|
12
|
+
/** Whether the panel is showing. Only used when `softDismiss` is on. */
|
|
13
|
+
open: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Lets a menu close itself the way people expect (#1293).
|
|
17
|
+
*
|
|
18
|
+
* Click outside an open menu and it should go away — and that click should
|
|
19
|
+
* do nothing else. So should pressing `Escape`. The browser does most of
|
|
20
|
+
* this on its own for an element marked as a popover, which is what this
|
|
21
|
+
* controller switches on, and it brings two things that are hard to get
|
|
22
|
+
* right by hand: focus returns to the button that opened the menu, and the
|
|
23
|
+
* menu is drawn above the rest of the page whatever its container does with
|
|
24
|
+
* `overflow`, `transform` or `z-index`.
|
|
25
|
+
*
|
|
26
|
+
* The one thing the browser will not do is swallow that outside click: left
|
|
27
|
+
* alone it closes the menu *and* presses whatever was underneath. So the
|
|
28
|
+
* controller gives the menu a see-through cover over the page, kept behind
|
|
29
|
+
* the menu's own content. The menu is drawn above everything else on the
|
|
30
|
+
* page, so its cover is too: clicks and hovers land on the cover instead of
|
|
31
|
+
* the page, and the cover closes the menu.
|
|
32
|
+
*
|
|
33
|
+
* The cover starts below the top bar, so the bar itself stays usable while a
|
|
34
|
+
* menu is open and moving from one menu to the next is a single click. Its
|
|
35
|
+
* top edge is `--topbar-height`, falling back to the top bar's size token; an
|
|
36
|
+
* app without a top bar sets that to `0`. Consumers can also restyle or tint
|
|
37
|
+
* it through `::part(backdrop)`.
|
|
38
|
+
*
|
|
39
|
+
* A component keeps `open` as the property people set. Since the browser can
|
|
40
|
+
* also open and close the menu on its own, the controller writes what the
|
|
41
|
+
* browser did back into `open`, and fires `close` when the menu went away by
|
|
42
|
+
* itself. Skip that and the property drifts out of step with what is on
|
|
43
|
+
* screen, and the next render closes a menu that was just opened.
|
|
44
|
+
*
|
|
45
|
+
* The button that opens the menu usually belongs to whoever is using the
|
|
46
|
+
* component, not to the component itself, so wiring it up is
|
|
47
|
+
* {@link bindPopoverTrigger}'s job.
|
|
48
|
+
*/
|
|
49
|
+
export declare class PopoverController implements ReactiveController {
|
|
50
|
+
private readonly host;
|
|
51
|
+
private installed;
|
|
52
|
+
private backdrop?;
|
|
53
|
+
/** Noted at a close that `open` did not ask for, so it can be reported. */
|
|
54
|
+
private dismissed;
|
|
55
|
+
/**
|
|
56
|
+
* True from `beforetoggle` to `toggle`, while the browser is mid-way through
|
|
57
|
+
* opening or closing. `sync()` stands down for that stretch: it would see
|
|
58
|
+
* `open` already updated but the menu not yet moved, and try to move it
|
|
59
|
+
* itself — nested inside the move the browser is making.
|
|
60
|
+
*/
|
|
61
|
+
private toggling;
|
|
62
|
+
constructor(host: SoftDismissHost);
|
|
63
|
+
hostConnected(): void;
|
|
64
|
+
hostUpdated(): void;
|
|
65
|
+
hostDisconnected(): void;
|
|
66
|
+
/**
|
|
67
|
+
* The menu is about to open or close for a reason other than `open` being
|
|
68
|
+
* set — a button wired straight to it, `Escape`, a click on the cover, a
|
|
69
|
+
* direct `showPopover()`. `open` is brought into line here, at once.
|
|
70
|
+
*
|
|
71
|
+
* The browser offers two events for this and only `beforetoggle` arrives
|
|
72
|
+
* in time. It fires during the call that opens or closes the menu, while
|
|
73
|
+
* `toggle` waits its turn in the queue. A re-render can get in before that
|
|
74
|
+
* turn, read an `open` that still says the old thing, and undo what just
|
|
75
|
+
* happened — close a menu that was just opened, or reopen one that was
|
|
76
|
+
* just closed.
|
|
77
|
+
*/
|
|
78
|
+
private readonly onBeforeToggle;
|
|
79
|
+
/**
|
|
80
|
+
* The menu has closed. Report it only when `open` did not ask for it.
|
|
81
|
+
*
|
|
82
|
+
* By now `open` is `false` either way — `beforetoggle` saw to that — so
|
|
83
|
+
* the note it left in `dismissed` is what tells the two apart.
|
|
84
|
+
*
|
|
85
|
+
* The `softDismiss` check keeps this out of the way of components that run
|
|
86
|
+
* a popover themselves. `obc-menu-button` does that with
|
|
87
|
+
* `obc-context-menu-input`, and those closes are its business.
|
|
88
|
+
*/
|
|
89
|
+
private readonly onToggle;
|
|
90
|
+
private sync;
|
|
91
|
+
/**
|
|
92
|
+
* The see-through cover that swallows clicks while the menu is open.
|
|
93
|
+
*
|
|
94
|
+
* It is an ordinary child of the menu, not a popover of its own: the
|
|
95
|
+
* browser refuses to show one popover while another is on its way up, so
|
|
96
|
+
* the cover cannot be a popover shown as the menu opens. It does not need
|
|
97
|
+
* to be. The menu is drawn above the whole page, so a child of the menu
|
|
98
|
+
* that reaches over the page is drawn above the page as well, and a
|
|
99
|
+
* negative `z-index` keeps it behind the menu's own content. It shows and
|
|
100
|
+
* hides with the menu and never has to be told.
|
|
101
|
+
*
|
|
102
|
+
* Being inside the menu also means the browser treats a click on it as a
|
|
103
|
+
* click *inside* the menu and leaves the menu open — so the cover closes
|
|
104
|
+
* the menu itself.
|
|
105
|
+
*/
|
|
106
|
+
private ensureCover;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Makes a button open and close a menu. Returns a function that unhooks it.
|
|
110
|
+
*
|
|
111
|
+
* Most such buttons sit in the top bar, which the cover leaves reachable, and
|
|
112
|
+
* that is where people get caught out. Clicking the button a second time
|
|
113
|
+
* looks like it should close the menu, but the browser has already closed it
|
|
114
|
+
* as the mouse went down, before the click arrives — so a handler that reads
|
|
115
|
+
* the live state opens it right back up and the menu looks stuck.
|
|
116
|
+
* Remembering the state as the mouse goes down is what makes the second click
|
|
117
|
+
* work. Keyboard presses never trigger that early close, so they read the
|
|
118
|
+
* live state; `MouseEvent.detail` is `0` only for the click that `Enter` and
|
|
119
|
+
* `Space` produce, which is how the two are told apart.
|
|
120
|
+
*
|
|
121
|
+
* `open` is not touched here: the controller mirrors what the browser does,
|
|
122
|
+
* in both directions, the moment it happens.
|
|
123
|
+
*
|
|
124
|
+
* HTML can pair a button with a popover on its own, but only inside the same
|
|
125
|
+
* component. Reach for this when the button and the menu live apart.
|
|
126
|
+
*/
|
|
127
|
+
export declare function bindPopoverTrigger(trigger: HTMLElement, panel: SoftDismissHost): () => void;
|
|
128
|
+
//# sourceMappingURL=popover-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover-controller.d.ts","sourceRoot":"","sources":["../../src/internal/popover-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,kBAAkB,EAAC,MAAM,KAAK,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,wEAAwE;IACxE,WAAW,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAa9C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,2EAA2E;IAC3E,OAAO,CAAC,SAAS,CAAS;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAS;gBAEI,IAAI,EAAE,eAAe;IAIlD,aAAa,IAAI,IAAI;IAQrB,WAAW,IAAI,IAAI;IAInB,gBAAgB,IAAI,IAAI;IAMxB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAkB7B;IAEF;;;;;;;;;OASG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAavB;IAEF,OAAO,CAAC,IAAI;IAiCZ;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;CAiBpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,eAAe,GACrB,MAAM,IAAI,CAwBZ"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
class PopoverController {
|
|
2
|
+
constructor(host) {
|
|
3
|
+
this.host = host;
|
|
4
|
+
this.installed = false;
|
|
5
|
+
this.dismissed = false;
|
|
6
|
+
this.toggling = false;
|
|
7
|
+
this.onBeforeToggle = (event) => {
|
|
8
|
+
if (!this.host.softDismiss) return;
|
|
9
|
+
this.toggling = true;
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
this.toggling = false;
|
|
12
|
+
}, 0);
|
|
13
|
+
if (event.newState === "open") {
|
|
14
|
+
this.ensureCover();
|
|
15
|
+
this.host.open = true;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
this.dismissed = this.host.open;
|
|
19
|
+
this.host.open = false;
|
|
20
|
+
};
|
|
21
|
+
this.onToggle = (event) => {
|
|
22
|
+
if (!this.host.softDismiss) return;
|
|
23
|
+
this.toggling = false;
|
|
24
|
+
if (event.newState !== "closed") return;
|
|
25
|
+
if (!this.dismissed) return;
|
|
26
|
+
this.dismissed = false;
|
|
27
|
+
this.host.dispatchEvent(new CustomEvent("close"));
|
|
28
|
+
};
|
|
29
|
+
host.addController(this);
|
|
30
|
+
}
|
|
31
|
+
hostConnected() {
|
|
32
|
+
this.host.addEventListener("beforetoggle", this.onBeforeToggle);
|
|
33
|
+
this.host.addEventListener("toggle", this.onToggle);
|
|
34
|
+
if (this.host.hasUpdated) {
|
|
35
|
+
this.sync();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
hostUpdated() {
|
|
39
|
+
this.sync();
|
|
40
|
+
}
|
|
41
|
+
hostDisconnected() {
|
|
42
|
+
this.host.removeEventListener("beforetoggle", this.onBeforeToggle);
|
|
43
|
+
this.host.removeEventListener("toggle", this.onToggle);
|
|
44
|
+
this.toggling = false;
|
|
45
|
+
}
|
|
46
|
+
sync() {
|
|
47
|
+
const host = this.host;
|
|
48
|
+
if (!host.softDismiss) {
|
|
49
|
+
if (this.installed) {
|
|
50
|
+
host.removeAttribute("popover");
|
|
51
|
+
this.installed = false;
|
|
52
|
+
this.backdrop?.remove();
|
|
53
|
+
this.backdrop = void 0;
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!this.installed) {
|
|
58
|
+
host.setAttribute("popover", "auto");
|
|
59
|
+
this.installed = true;
|
|
60
|
+
this.ensureCover();
|
|
61
|
+
}
|
|
62
|
+
if (!host.isConnected) return;
|
|
63
|
+
if (this.toggling) return;
|
|
64
|
+
const isOpen = host.matches(":popover-open");
|
|
65
|
+
if (host.open && !isOpen) {
|
|
66
|
+
host.showPopover();
|
|
67
|
+
} else if (!host.open && isOpen) {
|
|
68
|
+
host.hidePopover();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The see-through cover that swallows clicks while the menu is open.
|
|
73
|
+
*
|
|
74
|
+
* It is an ordinary child of the menu, not a popover of its own: the
|
|
75
|
+
* browser refuses to show one popover while another is on its way up, so
|
|
76
|
+
* the cover cannot be a popover shown as the menu opens. It does not need
|
|
77
|
+
* to be. The menu is drawn above the whole page, so a child of the menu
|
|
78
|
+
* that reaches over the page is drawn above the page as well, and a
|
|
79
|
+
* negative `z-index` keeps it behind the menu's own content. It shows and
|
|
80
|
+
* hides with the menu and never has to be told.
|
|
81
|
+
*
|
|
82
|
+
* Being inside the menu also means the browser treats a click on it as a
|
|
83
|
+
* click *inside* the menu and leaves the menu open — so the cover closes
|
|
84
|
+
* the menu itself.
|
|
85
|
+
*/
|
|
86
|
+
ensureCover() {
|
|
87
|
+
const root = this.host.shadowRoot;
|
|
88
|
+
if (!root || this.backdrop) return;
|
|
89
|
+
const cover = document.createElement("div");
|
|
90
|
+
cover.setAttribute("part", "backdrop");
|
|
91
|
+
cover.style.cssText = "position:fixed;top:var(--topbar-height,var(--app-components-topbar-touch-target-size,0px));right:0;bottom:0;left:0;z-index:-1;margin:0;padding:0;border:0;background:transparent";
|
|
92
|
+
cover.addEventListener("click", () => this.host.hidePopover());
|
|
93
|
+
root.append(cover);
|
|
94
|
+
this.backdrop = cover;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function bindPopoverTrigger(trigger, panel) {
|
|
98
|
+
let openBeforeAutoClose = false;
|
|
99
|
+
const onPointerDown = () => {
|
|
100
|
+
openBeforeAutoClose = panel.matches(":popover-open");
|
|
101
|
+
};
|
|
102
|
+
const onClick = (event) => {
|
|
103
|
+
const wasOpen = event.detail > 0 ? openBeforeAutoClose : panel.matches(":popover-open");
|
|
104
|
+
if (wasOpen) {
|
|
105
|
+
panel.hidePopover();
|
|
106
|
+
} else {
|
|
107
|
+
panel.showPopover();
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
trigger.addEventListener("pointerdown", onPointerDown);
|
|
111
|
+
trigger.addEventListener("click", onClick);
|
|
112
|
+
return () => {
|
|
113
|
+
trigger.removeEventListener("pointerdown", onPointerDown);
|
|
114
|
+
trigger.removeEventListener("click", onClick);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
PopoverController,
|
|
119
|
+
bindPopoverTrigger
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=popover-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover-controller.js","sources":["../../src/internal/popover-controller.ts"],"sourcesContent":["import type {LitElement, ReactiveController} from 'lit';\n\n/**\n * What {@link PopoverController} needs a component to have.\n *\n * `softDismiss` has to be something a component opts into. The browser hides\n * a popover until it is opened, so switching this on for everyone would make\n * every menu that a consumer already shows and positions disappear.\n */\nexport interface SoftDismissHost extends LitElement {\n /** Let the browser take over showing, hiding and closing this panel. */\n softDismiss: boolean;\n /** Whether the panel is showing. Only used when `softDismiss` is on. */\n open: boolean;\n}\n\n/**\n * Lets a menu close itself the way people expect (#1293).\n *\n * Click outside an open menu and it should go away — and that click should\n * do nothing else. So should pressing `Escape`. The browser does most of\n * this on its own for an element marked as a popover, which is what this\n * controller switches on, and it brings two things that are hard to get\n * right by hand: focus returns to the button that opened the menu, and the\n * menu is drawn above the rest of the page whatever its container does with\n * `overflow`, `transform` or `z-index`.\n *\n * The one thing the browser will not do is swallow that outside click: left\n * alone it closes the menu *and* presses whatever was underneath. So the\n * controller gives the menu a see-through cover over the page, kept behind\n * the menu's own content. The menu is drawn above everything else on the\n * page, so its cover is too: clicks and hovers land on the cover instead of\n * the page, and the cover closes the menu.\n *\n * The cover starts below the top bar, so the bar itself stays usable while a\n * menu is open and moving from one menu to the next is a single click. Its\n * top edge is `--topbar-height`, falling back to the top bar's size token; an\n * app without a top bar sets that to `0`. Consumers can also restyle or tint\n * it through `::part(backdrop)`.\n *\n * A component keeps `open` as the property people set. Since the browser can\n * also open and close the menu on its own, the controller writes what the\n * browser did back into `open`, and fires `close` when the menu went away by\n * itself. Skip that and the property drifts out of step with what is on\n * screen, and the next render closes a menu that was just opened.\n *\n * The button that opens the menu usually belongs to whoever is using the\n * component, not to the component itself, so wiring it up is\n * {@link bindPopoverTrigger}'s job.\n */\nexport class PopoverController implements ReactiveController {\n private installed = false;\n private backdrop?: HTMLElement;\n /** Noted at a close that `open` did not ask for, so it can be reported. */\n private dismissed = false;\n /**\n * True from `beforetoggle` to `toggle`, while the browser is mid-way through\n * opening or closing. `sync()` stands down for that stretch: it would see\n * `open` already updated but the menu not yet moved, and try to move it\n * itself — nested inside the move the browser is making.\n */\n private toggling = false;\n\n constructor(private readonly host: SoftDismissHost) {\n host.addController(this);\n }\n\n hostConnected(): void {\n this.host.addEventListener('beforetoggle', this.onBeforeToggle);\n this.host.addEventListener('toggle', this.onToggle);\n if (this.host.hasUpdated) {\n this.sync();\n }\n }\n\n hostUpdated(): void {\n this.sync();\n }\n\n hostDisconnected(): void {\n this.host.removeEventListener('beforetoggle', this.onBeforeToggle);\n this.host.removeEventListener('toggle', this.onToggle);\n this.toggling = false;\n }\n\n /**\n * The menu is about to open or close for a reason other than `open` being\n * set — a button wired straight to it, `Escape`, a click on the cover, a\n * direct `showPopover()`. `open` is brought into line here, at once.\n *\n * The browser offers two events for this and only `beforetoggle` arrives\n * in time. It fires during the call that opens or closes the menu, while\n * `toggle` waits its turn in the queue. A re-render can get in before that\n * turn, read an `open` that still says the old thing, and undo what just\n * happened — close a menu that was just opened, or reopen one that was\n * just closed.\n */\n private readonly onBeforeToggle = (event: Event): void => {\n if (!this.host.softDismiss) return;\n this.toggling = true;\n // `toggle` clears this. Should it never come — someone cancelled the\n // opening — the flag must not stay up, or `open` could never drive the\n // menu again. A timer runs after the browser has finished either way.\n setTimeout(() => {\n this.toggling = false;\n }, 0);\n if ((event as ToggleEvent).newState === 'open') {\n this.ensureCover();\n this.host.open = true;\n return;\n }\n // `open` is already false when this controller asked for the close;\n // still true means somebody else did, and the consumer should hear.\n this.dismissed = this.host.open;\n this.host.open = false;\n };\n\n /**\n * The menu has closed. Report it only when `open` did not ask for it.\n *\n * By now `open` is `false` either way — `beforetoggle` saw to that — so\n * the note it left in `dismissed` is what tells the two apart.\n *\n * The `softDismiss` check keeps this out of the way of components that run\n * a popover themselves. `obc-menu-button` does that with\n * `obc-context-menu-input`, and those closes are its business.\n */\n private readonly onToggle = (event: Event): void => {\n if (!this.host.softDismiss) return;\n this.toggling = false;\n if ((event as ToggleEvent).newState !== 'closed') return;\n if (!this.dismissed) return;\n this.dismissed = false;\n /**\n * Fired when the popover closed on its own — a click outside, `Escape`,\n * or another popover opening.\n * @event close\n * @type {CustomEvent<void>}\n */\n this.host.dispatchEvent(new CustomEvent('close'));\n };\n\n private sync(): void {\n const host = this.host;\n\n if (!host.softDismiss) {\n if (this.installed) {\n // Dropping the attribute puts the component back to an ordinary\n // element, which is what it was before opting in.\n host.removeAttribute('popover');\n this.installed = false;\n this.backdrop?.remove();\n this.backdrop = undefined;\n }\n return;\n }\n\n if (!this.installed) {\n host.setAttribute('popover', 'auto');\n this.installed = true;\n this.ensureCover();\n }\n\n // Showing a popover that is not on the page throws.\n if (!host.isConnected) return;\n if (this.toggling) return;\n\n const isOpen = host.matches(':popover-open');\n if (host.open && !isOpen) {\n host.showPopover();\n } else if (!host.open && isOpen) {\n host.hidePopover();\n }\n }\n\n /**\n * The see-through cover that swallows clicks while the menu is open.\n *\n * It is an ordinary child of the menu, not a popover of its own: the\n * browser refuses to show one popover while another is on its way up, so\n * the cover cannot be a popover shown as the menu opens. It does not need\n * to be. The menu is drawn above the whole page, so a child of the menu\n * that reaches over the page is drawn above the page as well, and a\n * negative `z-index` keeps it behind the menu's own content. It shows and\n * hides with the menu and never has to be told.\n *\n * Being inside the menu also means the browser treats a click on it as a\n * click *inside* the menu and leaves the menu open — so the cover closes\n * the menu itself.\n */\n private ensureCover(): void {\n const root = this.host.shadowRoot;\n if (!root || this.backdrop) return;\n const cover = document.createElement('div');\n cover.setAttribute('part', 'backdrop');\n // The last fallback keeps the whole page covered where no palette is\n // loaded; an `auto` top would leave the cover wherever it happened to\n // land.\n cover.style.cssText =\n 'position:fixed;' +\n 'top:var(--topbar-height,var(--app-components-topbar-touch-target-size,0px));' +\n 'right:0;bottom:0;left:0;z-index:-1;margin:0;padding:0;border:0;' +\n 'background:transparent';\n cover.addEventListener('click', () => this.host.hidePopover());\n root.append(cover);\n this.backdrop = cover;\n }\n}\n\n/**\n * Makes a button open and close a menu. Returns a function that unhooks it.\n *\n * Most such buttons sit in the top bar, which the cover leaves reachable, and\n * that is where people get caught out. Clicking the button a second time\n * looks like it should close the menu, but the browser has already closed it\n * as the mouse went down, before the click arrives — so a handler that reads\n * the live state opens it right back up and the menu looks stuck.\n * Remembering the state as the mouse goes down is what makes the second click\n * work. Keyboard presses never trigger that early close, so they read the\n * live state; `MouseEvent.detail` is `0` only for the click that `Enter` and\n * `Space` produce, which is how the two are told apart.\n *\n * `open` is not touched here: the controller mirrors what the browser does,\n * in both directions, the moment it happens.\n *\n * HTML can pair a button with a popover on its own, but only inside the same\n * component. Reach for this when the button and the menu live apart.\n */\nexport function bindPopoverTrigger(\n trigger: HTMLElement,\n panel: SoftDismissHost\n): () => void {\n let openBeforeAutoClose = false;\n\n const onPointerDown = (): void => {\n openBeforeAutoClose = panel.matches(':popover-open');\n };\n\n const onClick = (event: MouseEvent): void => {\n const wasOpen =\n event.detail > 0 ? openBeforeAutoClose : panel.matches(':popover-open');\n if (wasOpen) {\n panel.hidePopover();\n } else {\n panel.showPopover();\n }\n };\n\n trigger.addEventListener('pointerdown', onPointerDown);\n trigger.addEventListener('click', onClick);\n\n return () => {\n trigger.removeEventListener('pointerdown', onPointerDown);\n trigger.removeEventListener('click', onClick);\n };\n}\n"],"names":[],"mappings":"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"}
|