vira 27.0.0 → 28.0.0
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/dist/elements/index.d.ts +7 -7
- package/dist/elements/index.js +7 -7
- package/dist/elements/{popover/popover-helpers.d.ts → pop-up/pop-up-helpers.d.ts} +6 -6
- package/dist/elements/{popover/popover-helpers.js → pop-up/pop-up-helpers.js} +5 -5
- package/dist/elements/{popover → pop-up}/vira-menu-item.element.d.ts +2 -2
- package/dist/elements/{popover → pop-up}/vira-menu-item.element.js +1 -1
- package/dist/elements/pop-up/vira-menu-trigger.element.d.ts +55 -0
- package/dist/elements/{popover → pop-up}/vira-menu-trigger.element.js +33 -29
- package/dist/elements/{popover → pop-up}/vira-menu.element.d.ts +2 -2
- package/dist/elements/{popover → pop-up}/vira-menu.element.js +2 -2
- package/dist/elements/pop-up/vira-pop-up-menu.element.d.ts +35 -0
- package/dist/elements/{popover/vira-popover-menu.element.js → pop-up/vira-pop-up-menu.element.js} +23 -23
- package/dist/elements/pop-up/vira-pop-up-trigger.element.d.ts +81 -0
- package/dist/elements/pop-up/vira-pop-up-trigger.element.js +282 -0
- package/dist/elements/vira-dropdown.element.d.ts +18 -6
- package/dist/elements/vira-dropdown.element.js +10 -8
- package/dist/util/index.d.ts +1 -1
- package/dist/util/index.js +1 -1
- package/dist/util/{popover-manager.d.ts → pop-up-manager.d.ts} +39 -60
- package/dist/util/{popover-manager.js → pop-up-manager.js} +45 -42
- package/package.json +1 -1
- package/dist/elements/popover/vira-menu-trigger.element.d.ts +0 -41
- package/dist/elements/popover/vira-popover-menu.element.d.ts +0 -35
- package/dist/elements/popover/vira-popover-trigger.element.d.ts +0 -67
- package/dist/elements/popover/vira-popover-trigger.element.js +0 -301
- /package/dist/elements/{popover/popover-menu-item.d.ts → pop-up/pop-up-menu-item.d.ts} +0 -0
- /package/dist/elements/{popover/popover-menu-item.js → pop-up/pop-up-menu-item.js} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { assert } from '@augment-vir/assert';
|
|
1
2
|
import { mapObjectValues } from '@augment-vir/common';
|
|
3
|
+
import { findOverflowAncestor } from '@augment-vir/web';
|
|
2
4
|
import { NavActivateEvent, NavDirection } from 'device-navigation';
|
|
3
5
|
import { listenToPageActivation } from 'page-active';
|
|
4
6
|
import { ListenTarget, defineTypedCustomEvent, defineTypedEvent, listenToGlobal, } from 'typed-event-target';
|
|
@@ -14,30 +16,30 @@ export const emptyPositionRect = {
|
|
|
14
16
|
bottom: 0,
|
|
15
17
|
};
|
|
16
18
|
/**
|
|
17
|
-
* An event fired from {@link
|
|
19
|
+
* An event fired from {@link PopUpManager} when the pop up should be hidden.
|
|
18
20
|
*
|
|
19
|
-
* @category
|
|
21
|
+
* @category PopUp
|
|
20
22
|
*/
|
|
21
|
-
export class
|
|
23
|
+
export class HidePopUpEvent extends defineTypedEvent('hide-pop-up') {
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
|
-
* An event fired from {@link
|
|
25
|
-
*
|
|
26
|
+
* An event fired from {@link PopUpManager} when an individual item in the pop up has been selected
|
|
27
|
+
* by the user.
|
|
26
28
|
*
|
|
27
|
-
* @category
|
|
29
|
+
* @category PopUp
|
|
28
30
|
*/
|
|
29
31
|
export class NavSelectEvent extends defineTypedCustomEvent()('nav-select') {
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
|
-
* A "
|
|
34
|
+
* A "pop up" manager for items that pop up from the HTML page, like dropdowns or menus.
|
|
33
35
|
*
|
|
34
|
-
* @category
|
|
36
|
+
* @category PopUp
|
|
35
37
|
*/
|
|
36
|
-
export class
|
|
38
|
+
export class PopUpManager {
|
|
37
39
|
navController;
|
|
38
40
|
listenTarget = new ListenTarget();
|
|
39
41
|
options = {
|
|
40
|
-
|
|
42
|
+
minDownSpace: 200,
|
|
41
43
|
verticalDiffThreshold: 20,
|
|
42
44
|
supportNavigation: true,
|
|
43
45
|
};
|
|
@@ -51,7 +53,7 @@ export class PopoverManager {
|
|
|
51
53
|
let firstFired = false;
|
|
52
54
|
const resizeObserver = new ResizeObserver(() => {
|
|
53
55
|
if (firstFired) {
|
|
54
|
-
this.
|
|
56
|
+
this.removePopUp();
|
|
55
57
|
}
|
|
56
58
|
else {
|
|
57
59
|
firstFired = true;
|
|
@@ -64,7 +66,7 @@ export class PopoverManager {
|
|
|
64
66
|
},
|
|
65
67
|
listenToPageActivation(false, (isPageActive) => {
|
|
66
68
|
if (!isPageActive) {
|
|
67
|
-
this.
|
|
69
|
+
this.removePopUp();
|
|
68
70
|
}
|
|
69
71
|
}),
|
|
70
72
|
this.navController.listen(NavActivateEvent, (event) => {
|
|
@@ -78,15 +80,15 @@ export class PopoverManager {
|
|
|
78
80
|
listenToGlobal('mousedown', (event) => {
|
|
79
81
|
if (this.lastRootElement &&
|
|
80
82
|
event.composedPath().includes(this.lastRootElement)) {
|
|
81
|
-
/** Ignore clicks that came from the
|
|
83
|
+
/** Ignore clicks that came from the pop up host itself. */
|
|
82
84
|
return;
|
|
83
85
|
}
|
|
84
|
-
this.
|
|
86
|
+
this.removePopUp();
|
|
85
87
|
}, { passive: true }),
|
|
86
88
|
listenToGlobal('keydown', (event) => {
|
|
87
89
|
const keyCode = event.code;
|
|
88
90
|
if (keyCode === 'Escape') {
|
|
89
|
-
this.
|
|
91
|
+
this.removePopUp();
|
|
90
92
|
}
|
|
91
93
|
else if (this.options.supportNavigation) {
|
|
92
94
|
if (keyCode === 'ArrowDown') {
|
|
@@ -130,50 +132,51 @@ export class PopoverManager {
|
|
|
130
132
|
}),
|
|
131
133
|
];
|
|
132
134
|
}
|
|
133
|
-
/** Listen to events emitted from a {@link
|
|
135
|
+
/** Listen to events emitted from a {@link PopUpManager} instance. */
|
|
134
136
|
listen(event, listener, options) {
|
|
135
137
|
return this.listenTarget.listen(event, listener, options);
|
|
136
138
|
}
|
|
137
|
-
/** Trigger removal or hiding of the
|
|
138
|
-
|
|
139
|
+
/** Trigger removal or hiding of the pop up. */
|
|
140
|
+
removePopUp() {
|
|
139
141
|
this.cleanupCallbacks.forEach((callback) => callback());
|
|
140
|
-
this.listenTarget.dispatch(new
|
|
142
|
+
this.listenTarget.dispatch(new HidePopUpEvent());
|
|
141
143
|
}
|
|
142
|
-
/** Trigger showing the
|
|
143
|
-
|
|
144
|
+
/** Trigger showing the pop up. */
|
|
145
|
+
showPopUp(rootElement, options) {
|
|
144
146
|
this.lastRootElement = rootElement;
|
|
145
147
|
const currentOptions = { ...this.options, ...options };
|
|
146
|
-
const container =
|
|
148
|
+
const container = findOverflowAncestor(rootElement);
|
|
149
|
+
assert.instanceOf(container, HTMLElement);
|
|
147
150
|
const rootRect = rootElement.getBoundingClientRect();
|
|
148
151
|
const containerRect = container.getBoundingClientRect();
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
const containerScrollbarWidth = container.offsetWidth - container.clientWidth;
|
|
153
|
+
const containerScrollbarHeight = container.offsetHeight - container.clientHeight;
|
|
154
|
+
const containerPosition = container === document.body
|
|
155
|
+
? {
|
|
156
|
+
top: 0,
|
|
157
|
+
left: 0,
|
|
158
|
+
right: containerRect.width,
|
|
159
|
+
bottom: containerRect.height,
|
|
160
|
+
}
|
|
161
|
+
: {
|
|
162
|
+
top: containerRect.top,
|
|
163
|
+
left: containerRect.left,
|
|
164
|
+
right: containerRect.right - containerScrollbarWidth,
|
|
165
|
+
bottom: containerRect.bottom - containerScrollbarHeight,
|
|
166
|
+
};
|
|
155
167
|
const rootPosition = mapObjectValues(emptyPositionRect, (key) => {
|
|
156
168
|
return rootRect[key];
|
|
157
169
|
});
|
|
158
|
-
const
|
|
170
|
+
const diff = mapObjectValues(emptyPositionRect, (key) => {
|
|
159
171
|
const containerDimension = containerPosition[key];
|
|
160
172
|
const hostDimension = rootPosition[key];
|
|
161
173
|
return Math.abs(containerDimension - hostDimension);
|
|
162
174
|
});
|
|
163
|
-
const diff = {
|
|
164
|
-
...diffPositions,
|
|
165
|
-
rootLeftToContainerRight: containerPosition.right - diffPositions.left,
|
|
166
|
-
rootRightToContainerLeft: containerPosition.right - diffPositions.right,
|
|
167
|
-
rootTopToContainerBottom: containerPosition.bottom - diffPositions.top,
|
|
168
|
-
rootBottomToContainerTop: containerPosition.bottom - diffPositions.bottom,
|
|
169
|
-
};
|
|
170
175
|
const useUp = diff.top > diff.bottom + currentOptions.verticalDiffThreshold &&
|
|
171
|
-
diff.bottom < currentOptions.
|
|
176
|
+
diff.bottom < currentOptions.minDownSpace;
|
|
172
177
|
this.attachGlobalListeners(container);
|
|
173
|
-
const useLeft = diff.rootLeftToContainerRight + 100 < diff.rootRightToContainerLeft;
|
|
174
178
|
return {
|
|
175
179
|
popDown: !useUp,
|
|
176
|
-
popRight: !useLeft,
|
|
177
180
|
positions: {
|
|
178
181
|
container: containerPosition,
|
|
179
182
|
root: rootPosition,
|
|
@@ -182,13 +185,13 @@ export class PopoverManager {
|
|
|
182
185
|
};
|
|
183
186
|
}
|
|
184
187
|
/**
|
|
185
|
-
* Cleanup and destroy the {@link
|
|
188
|
+
* Cleanup and destroy the {@link PopUpManager} instance. This:
|
|
186
189
|
*
|
|
187
|
-
* - Removes the existing
|
|
190
|
+
* - Removes the existing pop up
|
|
188
191
|
* - Cleans up all internal and external listeners
|
|
189
192
|
*/
|
|
190
193
|
destroy() {
|
|
191
|
-
this.
|
|
194
|
+
this.removePopUp();
|
|
192
195
|
this.listenTarget.destroy();
|
|
193
196
|
}
|
|
194
197
|
}
|
package/package.json
CHANGED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
-
import { type NavController } from 'device-navigation';
|
|
3
|
-
import { type PopoverManager, type ShowPopoverResult } from '../../util/popover-manager.js';
|
|
4
|
-
import { type MenuItem } from './popover-menu-item.js';
|
|
5
|
-
import { type PopoverMenuCornerStyle } from './vira-popover-menu.element.js';
|
|
6
|
-
import { type PopoverOffset } from './vira-popover-trigger.element.js';
|
|
7
|
-
/**
|
|
8
|
-
* Test ids for {@link ViraMenuTrigger}.
|
|
9
|
-
*
|
|
10
|
-
* @category Internal
|
|
11
|
-
*/
|
|
12
|
-
export declare const viraMenuTriggerTestIds: {
|
|
13
|
-
menu: string;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* A more specific wrapper of `ViraPopoverTrigger` that always opens a menu.
|
|
17
|
-
*
|
|
18
|
-
* @category Popover
|
|
19
|
-
* @category Elements
|
|
20
|
-
*/
|
|
21
|
-
export declare const ViraMenuTrigger: import("element-vir").DeclarativeElementDefinition<"vira-menu-trigger", {
|
|
22
|
-
items: ReadonlyArray<Readonly<MenuItem>>;
|
|
23
|
-
} & PartialWithUndefined<{
|
|
24
|
-
/** The selected item ids from the given `items` object. */
|
|
25
|
-
selected: ReadonlyArray<PropertyKey>;
|
|
26
|
-
isDisabled: boolean;
|
|
27
|
-
isMultiSelect: boolean;
|
|
28
|
-
z_debug_forceOpenState: boolean;
|
|
29
|
-
popoverOffset: PopoverOffset;
|
|
30
|
-
/** Hide menu item check mark icons. */
|
|
31
|
-
hideCheckIcons: boolean;
|
|
32
|
-
menuCornerStyle: PopoverMenuCornerStyle;
|
|
33
|
-
}>, {
|
|
34
|
-
navController: undefined | NavController;
|
|
35
|
-
popoverManager: undefined | PopoverManager;
|
|
36
|
-
/** `undefined` means the popover is not currently showing. */
|
|
37
|
-
showPopoverResult: ShowPopoverResult | undefined;
|
|
38
|
-
}, {
|
|
39
|
-
itemActivate: import("element-vir").DefineEvent<PropertyKey[]>;
|
|
40
|
-
openChange: import("element-vir").DefineEvent<ShowPopoverResult | undefined>;
|
|
41
|
-
}, "vira-menu-trigger-", "vira-menu-trigger-", readonly [], readonly []>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
-
/**
|
|
3
|
-
* Possible corner styles for {@link ViraPopoverMenu}.
|
|
4
|
-
*
|
|
5
|
-
* @category Internal
|
|
6
|
-
*/
|
|
7
|
-
export declare enum PopoverMenuCornerStyle {
|
|
8
|
-
/** Rounding of corners depends on the open direction of the menu. */
|
|
9
|
-
Directional = "directional",
|
|
10
|
-
/** All of the menus corners should be rounded. */
|
|
11
|
-
AllRounded = "all-rounded",
|
|
12
|
-
/** None of the menus corners should be rounded. */
|
|
13
|
-
AllSquare = "all-square"
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Menu popover directions available for {@link ViraPopoverMenu}.
|
|
17
|
-
*
|
|
18
|
-
* @category Internal
|
|
19
|
-
*/
|
|
20
|
-
export declare enum PopoverMenuDirection {
|
|
21
|
-
Downwards = "downwards",
|
|
22
|
-
Upwards = "upwards"
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* A simple default style wrapper for popover menus.
|
|
26
|
-
*
|
|
27
|
-
* @category Popover
|
|
28
|
-
* @category Elements
|
|
29
|
-
*/
|
|
30
|
-
export declare const ViraPopoverMenu: import("element-vir").DeclarativeElementDefinition<"vira-popover-menu", PartialWithUndefined<{
|
|
31
|
-
/** @default PopoverMenuDirection.Downwards */
|
|
32
|
-
direction: PopoverMenuDirection;
|
|
33
|
-
/** @default PopoverMenuCornerStyle.Directional */
|
|
34
|
-
cornerStyle: PopoverMenuCornerStyle;
|
|
35
|
-
}>, {}, {}, "vira-popover-menu-open-upwards" | "vira-popover-menu-rounded" | "vira-popover-menu-square", "vira-popover-menu-", readonly [], readonly []>;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
-
import { NavController, type Coords } from 'device-navigation';
|
|
3
|
-
import { PopoverManager, type ShowPopoverResult } from '../../util/popover-manager.js';
|
|
4
|
-
/**
|
|
5
|
-
* Offsets applied to any menu opened by {@link ViraPopoverTrigger}.
|
|
6
|
-
*
|
|
7
|
-
* @category Internal
|
|
8
|
-
*/
|
|
9
|
-
export type PopoverOffset = PartialWithUndefined<{
|
|
10
|
-
vertical: number;
|
|
11
|
-
right: number;
|
|
12
|
-
left: number;
|
|
13
|
-
}>;
|
|
14
|
-
/**
|
|
15
|
-
* Anchor options for popovers.
|
|
16
|
-
*
|
|
17
|
-
* @category Internal
|
|
18
|
-
*/
|
|
19
|
-
export declare enum HorizontalAnchor {
|
|
20
|
-
/**
|
|
21
|
-
* The left side of the popover will be anchored to the left side of the trigger, allowing the
|
|
22
|
-
* popover to grow on the right side of the trigger.
|
|
23
|
-
*/
|
|
24
|
-
Left = "left",
|
|
25
|
-
/**
|
|
26
|
-
* The Right side of the popover will be anchored to the right side of the trigger, allowing the
|
|
27
|
-
* popover to grow on the left side of the trigger.
|
|
28
|
-
*/
|
|
29
|
-
Right = "right",
|
|
30
|
-
/**
|
|
31
|
-
* Restrict the popover on both sides.
|
|
32
|
-
*
|
|
33
|
-
* This is the default anchor for {@link ViraPopoverTrigger}.
|
|
34
|
-
*/
|
|
35
|
-
Both = "both"
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* An element with slots for a popover trigger and popover contents.
|
|
39
|
-
*
|
|
40
|
-
* @category Popover
|
|
41
|
-
* @category Elements
|
|
42
|
-
* @see https://electrovir.github.io/vira/book/elements/vira-popover-trigger
|
|
43
|
-
*/
|
|
44
|
-
export declare const ViraPopoverTrigger: import("element-vir").DeclarativeElementDefinition<"vira-popover-trigger", PartialWithUndefined<{
|
|
45
|
-
isDisabled: boolean;
|
|
46
|
-
/** For debugging purposes only. Very bad for actual production code use. */
|
|
47
|
-
z_debug_forceOpenState: boolean;
|
|
48
|
-
/** Set to `true` to keep the popover open if it is interacted with. */
|
|
49
|
-
keepOpenAfterInteraction: boolean;
|
|
50
|
-
/** All values in px. */
|
|
51
|
-
popoverOffset?: PopoverOffset;
|
|
52
|
-
}>, {
|
|
53
|
-
/** `undefined` means the popover is not currently showing. */
|
|
54
|
-
showPopoverResult: ShowPopoverResult | undefined;
|
|
55
|
-
popoverManager: PopoverManager;
|
|
56
|
-
}, {
|
|
57
|
-
navSelect: import("element-vir").DefineEvent<Coords>;
|
|
58
|
-
/**
|
|
59
|
-
* - `undefined` indicates that the popover just closed.
|
|
60
|
-
* - {@link ShowPopoverResult} indicates that the popover just opened.
|
|
61
|
-
*/
|
|
62
|
-
openChange: import("element-vir").DefineEvent<ShowPopoverResult | undefined>;
|
|
63
|
-
init: import("element-vir").DefineEvent<{
|
|
64
|
-
navController: NavController;
|
|
65
|
-
popoverManager: PopoverManager;
|
|
66
|
-
}>;
|
|
67
|
-
}, "vira-popover-trigger-disabled", "vira-popover-trigger-", readonly ["trigger", "popover"], readonly []>;
|
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import { assert } from '@augment-vir/assert';
|
|
2
|
-
import { NavController } from 'device-navigation';
|
|
3
|
-
import { css, defineElementEvent, html, listen, onDomRendered, renderIf } from 'element-vir';
|
|
4
|
-
import { viraDisabledStyles } from '../../styles/disabled.js';
|
|
5
|
-
import { createFocusStyles } from '../../styles/focus.js';
|
|
6
|
-
import { noNativeFormStyles } from '../../styles/native-styles.js';
|
|
7
|
-
import { noUserSelect } from '../../styles/user-select.js';
|
|
8
|
-
import { HidePopoverEvent, NavSelectEvent, PopoverManager, } from '../../util/popover-manager.js';
|
|
9
|
-
import { defineViraElement } from '../define-vira-element.js';
|
|
10
|
-
import { triggerPopoverState } from './popover-helpers.js';
|
|
11
|
-
/**
|
|
12
|
-
* Anchor options for popovers.
|
|
13
|
-
*
|
|
14
|
-
* @category Internal
|
|
15
|
-
*/
|
|
16
|
-
export var HorizontalAnchor;
|
|
17
|
-
(function (HorizontalAnchor) {
|
|
18
|
-
/**
|
|
19
|
-
* The left side of the popover will be anchored to the left side of the trigger, allowing the
|
|
20
|
-
* popover to grow on the right side of the trigger.
|
|
21
|
-
*/
|
|
22
|
-
HorizontalAnchor["Left"] = "left";
|
|
23
|
-
/**
|
|
24
|
-
* The Right side of the popover will be anchored to the right side of the trigger, allowing the
|
|
25
|
-
* popover to grow on the left side of the trigger.
|
|
26
|
-
*/
|
|
27
|
-
HorizontalAnchor["Right"] = "right";
|
|
28
|
-
/**
|
|
29
|
-
* Restrict the popover on both sides.
|
|
30
|
-
*
|
|
31
|
-
* This is the default anchor for {@link ViraPopoverTrigger}.
|
|
32
|
-
*/
|
|
33
|
-
HorizontalAnchor["Both"] = "both";
|
|
34
|
-
})(HorizontalAnchor || (HorizontalAnchor = {}));
|
|
35
|
-
/**
|
|
36
|
-
* An element with slots for a popover trigger and popover contents.
|
|
37
|
-
*
|
|
38
|
-
* @category Popover
|
|
39
|
-
* @category Elements
|
|
40
|
-
* @see https://electrovir.github.io/vira/book/elements/vira-popover-trigger
|
|
41
|
-
*/
|
|
42
|
-
export const ViraPopoverTrigger = defineViraElement()({
|
|
43
|
-
tagName: 'vira-popover-trigger',
|
|
44
|
-
state({ host }) {
|
|
45
|
-
return {
|
|
46
|
-
/** `undefined` means the popover is not currently showing. */
|
|
47
|
-
showPopoverResult: undefined,
|
|
48
|
-
popoverManager: new PopoverManager(new NavController(host, { activateOnMouseUp: true })),
|
|
49
|
-
};
|
|
50
|
-
},
|
|
51
|
-
slotNames: [
|
|
52
|
-
'trigger',
|
|
53
|
-
'popover',
|
|
54
|
-
],
|
|
55
|
-
hostClasses: {
|
|
56
|
-
'vira-popover-trigger-disabled': ({ inputs }) => !!inputs.isDisabled,
|
|
57
|
-
},
|
|
58
|
-
styles: ({ hostClasses }) => css `
|
|
59
|
-
:host {
|
|
60
|
-
display: inline-flex;
|
|
61
|
-
box-sizing: border-box;
|
|
62
|
-
max-width: 100%;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.wrapper {
|
|
66
|
-
max-width: 100%;
|
|
67
|
-
box-sizing: border-box;
|
|
68
|
-
position: relative;
|
|
69
|
-
/* Do not use display:flex. Doing so will break positioning for Firefox and Safari. */
|
|
70
|
-
display: block;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.dropdown-trigger {
|
|
74
|
-
${noNativeFormStyles};
|
|
75
|
-
${noUserSelect};
|
|
76
|
-
cursor: pointer;
|
|
77
|
-
max-width: 100%;
|
|
78
|
-
position: relative;
|
|
79
|
-
flex-grow: 1;
|
|
80
|
-
box-sizing: border-box;
|
|
81
|
-
anchor-name: --popover-trigger;
|
|
82
|
-
|
|
83
|
-
${createFocusStyles({
|
|
84
|
-
elementBorderSize: 1,
|
|
85
|
-
})}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
${hostClasses['vira-popover-trigger-disabled'].selector} {
|
|
89
|
-
${viraDisabledStyles}
|
|
90
|
-
pointer-events: auto;
|
|
91
|
-
|
|
92
|
-
& .dropdown-wrapper {
|
|
93
|
-
pointer-events: none;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
[popover] {
|
|
98
|
-
/* More styles are set internally via JS. */
|
|
99
|
-
|
|
100
|
-
position: absolute;
|
|
101
|
-
box-sizing: border-box;
|
|
102
|
-
inset: auto;
|
|
103
|
-
display: flex;
|
|
104
|
-
/* Allow menu shadows to overflow. Without this they are hidden. */
|
|
105
|
-
overflow: visible;
|
|
106
|
-
pointer-events: none;
|
|
107
|
-
|
|
108
|
-
> * {
|
|
109
|
-
pointer-events: auto;
|
|
110
|
-
max-width: 100%;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
:popover-open {
|
|
115
|
-
${noNativeFormStyles}
|
|
116
|
-
}
|
|
117
|
-
`,
|
|
118
|
-
events: {
|
|
119
|
-
navSelect: defineElementEvent(),
|
|
120
|
-
/**
|
|
121
|
-
* - `undefined` indicates that the popover just closed.
|
|
122
|
-
* - {@link ShowPopoverResult} indicates that the popover just opened.
|
|
123
|
-
*/
|
|
124
|
-
openChange: defineElementEvent(),
|
|
125
|
-
init: defineElementEvent(),
|
|
126
|
-
},
|
|
127
|
-
cleanup({ state, updateState }) {
|
|
128
|
-
updateState({
|
|
129
|
-
showPopoverResult: undefined,
|
|
130
|
-
});
|
|
131
|
-
state.popoverManager.destroy();
|
|
132
|
-
},
|
|
133
|
-
init({ state, updateState, host, inputs, dispatch, events }) {
|
|
134
|
-
/** Refocus the trigger and set the result to `undefined` when the popover closes. */
|
|
135
|
-
state.popoverManager.listen(HidePopoverEvent, () => {
|
|
136
|
-
updateState({
|
|
137
|
-
showPopoverResult: undefined,
|
|
138
|
-
});
|
|
139
|
-
dispatch(new events.openChange(undefined));
|
|
140
|
-
if (!inputs.isDisabled) {
|
|
141
|
-
const dropdownWrapper = host.shadowRoot.querySelector('.dropdown-trigger');
|
|
142
|
-
assert.instanceOf(dropdownWrapper, HTMLButtonElement, 'failed to find dropdown wrapper child');
|
|
143
|
-
dropdownWrapper.focus();
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
state.popoverManager.listen(NavSelectEvent, (event) => {
|
|
147
|
-
if (!inputs.keepOpenAfterInteraction) {
|
|
148
|
-
triggerPopoverState({
|
|
149
|
-
open: false,
|
|
150
|
-
callback(showPopoverResult) {
|
|
151
|
-
updateState({
|
|
152
|
-
showPopoverResult,
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
host,
|
|
156
|
-
popoverManager: state.popoverManager,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
dispatch(new events.navSelect(event.detail));
|
|
160
|
-
});
|
|
161
|
-
dispatch(new events.init({
|
|
162
|
-
navController: state.popoverManager.navController,
|
|
163
|
-
popoverManager: state.popoverManager,
|
|
164
|
-
}));
|
|
165
|
-
},
|
|
166
|
-
render({ dispatch, events, state, inputs, updateState, host, slotNames }) {
|
|
167
|
-
function triggerPopover({ emitEvent, open, }, event) {
|
|
168
|
-
if (state.showPopoverResult && inputs.keepOpenAfterInteraction && event) {
|
|
169
|
-
const dropdownTrigger = host.shadowRoot.querySelector('.dropdown-trigger');
|
|
170
|
-
if (dropdownTrigger && !event.composedPath().includes(dropdownTrigger)) {
|
|
171
|
-
/**
|
|
172
|
-
* Prevent closing the popover when `keepOpenAfterInteraction` is turned on and
|
|
173
|
-
* the popover was interacted with.
|
|
174
|
-
*/
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
triggerPopoverState({
|
|
179
|
-
open,
|
|
180
|
-
callback(showPopoverResult) {
|
|
181
|
-
updateState({ showPopoverResult });
|
|
182
|
-
if (emitEvent) {
|
|
183
|
-
dispatch(new events.openChange(showPopoverResult));
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
host,
|
|
187
|
-
popoverManager: state.popoverManager,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
if (inputs.isDisabled) {
|
|
191
|
-
triggerPopover({ open: false, emitEvent: false }, undefined);
|
|
192
|
-
}
|
|
193
|
-
else if (inputs.z_debug_forceOpenState != undefined) {
|
|
194
|
-
if (!inputs.z_debug_forceOpenState && state.showPopoverResult) {
|
|
195
|
-
triggerPopover({ emitEvent: false, open: false }, undefined);
|
|
196
|
-
}
|
|
197
|
-
else if (inputs.z_debug_forceOpenState && !state.showPopoverResult) {
|
|
198
|
-
triggerPopover({ emitEvent: false, open: true }, undefined);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const horizontalPositionStyle = state.showPopoverResult
|
|
202
|
-
? state.showPopoverResult.popRight
|
|
203
|
-
? css `
|
|
204
|
-
width: ${state.showPopoverResult.positions.diff.rootLeftToContainerRight}px;
|
|
205
|
-
left: anchor(--popover-trigger left);
|
|
206
|
-
align-items: flex-start;
|
|
207
|
-
`
|
|
208
|
-
: css `
|
|
209
|
-
width: ${state.showPopoverResult.positions.diff.rootRightToContainerLeft}px;
|
|
210
|
-
right: anchor(--popover-trigger right);
|
|
211
|
-
/* Fallback for Firefox and Safari that don't yet support anchor() */
|
|
212
|
-
right: ${state.showPopoverResult.positions.diff.right}px;
|
|
213
|
-
align-items: flex-end;
|
|
214
|
-
`
|
|
215
|
-
: css `
|
|
216
|
-
display: none;
|
|
217
|
-
`;
|
|
218
|
-
const verticalPositionStyle = state.showPopoverResult
|
|
219
|
-
? state.showPopoverResult.popDown
|
|
220
|
-
? css `
|
|
221
|
-
top: anchor(--popover-trigger bottom);
|
|
222
|
-
height: ${state.showPopoverResult.positions.diff.bottom}px;
|
|
223
|
-
flex-direction: column;
|
|
224
|
-
`
|
|
225
|
-
: css `
|
|
226
|
-
bottom: anchor(--popover-trigger top);
|
|
227
|
-
height: ${state.showPopoverResult.positions.diff.top}px;
|
|
228
|
-
flex-direction: column-reverse;
|
|
229
|
-
`
|
|
230
|
-
: css `
|
|
231
|
-
display: none;
|
|
232
|
-
`;
|
|
233
|
-
/**
|
|
234
|
-
* These styles do _not_ account for window resizing while the menu is open. I decided this
|
|
235
|
-
* was not a major enough problem to tackle. If it becomes major enough in the future,
|
|
236
|
-
* you'll need to hook into a window _or_ container resize listener inside `PopoverManager`
|
|
237
|
-
* and emit a new `ShowPopoverResult` instance when it changes.
|
|
238
|
-
*
|
|
239
|
-
* Currently, the popover will simply close when the window is resized.
|
|
240
|
-
*/
|
|
241
|
-
const positionerStyles = css `
|
|
242
|
-
${verticalPositionStyle}
|
|
243
|
-
${horizontalPositionStyle}
|
|
244
|
-
`;
|
|
245
|
-
function respondToClick(event) {
|
|
246
|
-
triggerPopover({
|
|
247
|
-
emitEvent: true,
|
|
248
|
-
open: !state.showPopoverResult,
|
|
249
|
-
}, event);
|
|
250
|
-
}
|
|
251
|
-
return html `
|
|
252
|
-
<div class="wrapper">
|
|
253
|
-
<button
|
|
254
|
-
?disabled=${!!inputs.isDisabled}
|
|
255
|
-
class="dropdown-trigger"
|
|
256
|
-
role="listbox"
|
|
257
|
-
aria-expanded=${!!state.showPopoverResult}
|
|
258
|
-
${listen('keydown', (event) => {
|
|
259
|
-
if (!state.showPopoverResult && event.code.startsWith('Arrow')) {
|
|
260
|
-
triggerPopover({ emitEvent: true, open: true }, event);
|
|
261
|
-
}
|
|
262
|
-
})}
|
|
263
|
-
${listen('click', (event) => {
|
|
264
|
-
/**
|
|
265
|
-
* Detail is 0 if it was a keyboard key (like Enter) that triggered this
|
|
266
|
-
* click.
|
|
267
|
-
*/
|
|
268
|
-
if (event.detail === 0) {
|
|
269
|
-
respondToClick(event);
|
|
270
|
-
}
|
|
271
|
-
})}
|
|
272
|
-
${listen('mousedown', (event) => {
|
|
273
|
-
/** Ignore any clicks that aren't the main button. */
|
|
274
|
-
if (event.button === 0) {
|
|
275
|
-
respondToClick(event);
|
|
276
|
-
}
|
|
277
|
-
})}
|
|
278
|
-
>
|
|
279
|
-
<slot name=${slotNames.trigger}></slot>
|
|
280
|
-
</button>
|
|
281
|
-
<div
|
|
282
|
-
popover="manual"
|
|
283
|
-
style=${positionerStyles}
|
|
284
|
-
${onDomRendered((popoverElement) => {
|
|
285
|
-
assert.instanceOf(popoverElement, HTMLElement);
|
|
286
|
-
if (state.showPopoverResult) {
|
|
287
|
-
popoverElement.showPopover();
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
popoverElement.hidePopover();
|
|
291
|
-
}
|
|
292
|
-
})}
|
|
293
|
-
>
|
|
294
|
-
${renderIf(!!state.showPopoverResult, html `
|
|
295
|
-
<slot name=${slotNames.popover}></slot>
|
|
296
|
-
`)}
|
|
297
|
-
</div>
|
|
298
|
-
</div>
|
|
299
|
-
`;
|
|
300
|
-
},
|
|
301
|
-
});
|
|
File without changes
|
|
File without changes
|