@workday/canvas-kit-popup-stack 7.1.4 → 7.2.0-427-next.2
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/commonjs/lib/PopupStack.d.ts +22 -0
- package/dist/commonjs/lib/PopupStack.d.ts.map +1 -1
- package/dist/commonjs/lib/PopupStack.js +82 -75
- package/dist/es6/lib/PopupStack.d.ts +22 -0
- package/dist/es6/lib/PopupStack.d.ts.map +1 -1
- package/dist/es6/lib/PopupStack.js +82 -75
- package/lib/PopupStack.ts +28 -23
- package/package.json +2 -2
|
@@ -42,6 +42,24 @@ interface Stack {
|
|
|
42
42
|
};
|
|
43
43
|
_adapter: Partial<typeof PopupStack>;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The `PopupStack` is a framework agnostic first-in-last-out (FILO) stack that tracks all popups
|
|
47
|
+
* ("floating UI" or any UI that renders on top of other content). It contains methods that interact
|
|
48
|
+
* with the stack to support all coordinating behaviors of all popups on the page. The `PopupStack`
|
|
49
|
+
* helps:
|
|
50
|
+
*
|
|
51
|
+
* - Render popups in the right order on the page
|
|
52
|
+
* - Helps accessibility with the Escape key (topmost popup is closed)
|
|
53
|
+
* - Handles transition to [Full
|
|
54
|
+
* Screen](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API)
|
|
55
|
+
*
|
|
56
|
+
* The `PopupStack` supports adapters to work with existing popup systems. {@link createAdapter} is
|
|
57
|
+
* exported to accept an adapter. Only a single adapter should be used per page.
|
|
58
|
+
*
|
|
59
|
+
* The `PopupStack` is designed to handle multiple versions of `PopupStack` on the page at once
|
|
60
|
+
* while the internal FILO stack is shared between instances. You should not attempt to use the
|
|
61
|
+
* internal FILO stack. If an adapter is used, the internal FILO stack may be empty.
|
|
62
|
+
*/
|
|
45
63
|
export declare const PopupStack: {
|
|
46
64
|
/**
|
|
47
65
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -139,6 +157,10 @@ export declare const PopupStack: {
|
|
|
139
157
|
*/
|
|
140
158
|
export declare function resetStack(): void;
|
|
141
159
|
/**
|
|
160
|
+
* An adapter is a custom implementation of the {@link PopupStack}. There is only ever a single
|
|
161
|
+
* instance of an adapter on the page. It allows an adapter to intercept any `PopupStack` method.
|
|
162
|
+
* This could bypass the internal FILO stack of the `PopupStack` and allows the FILO stack to be
|
|
163
|
+
* handled by something else.
|
|
142
164
|
*
|
|
143
165
|
* @param adapter The parts of the PopupStack that we want to override
|
|
144
166
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PopupStack.d.ts","sourceRoot":"","sources":["../../../lib/PopupStack.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AASD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO9D;
|
|
1
|
+
{"version":3,"file":"PopupStack.d.ts","sourceRoot":"","sources":["../../../lib/PopupStack.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AASD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO9D;AA2CD,UAAU,KAAK;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,WAAW,CAAC;IAC9B,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,OAAO,QAAQ,CAAC;KAC3B,CAAC;IACF,QAAQ,EAAE,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC;CACtC;AAwFD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU;IACrB;;;;;OAKG;uBACgB,WAAW;IAS9B;;;;;OAKG;cACO,cAAc,GAAG,IAAI;IAY/B;;;;;;OAMG;oBACa,WAAW,GAAG,IAAI;IAelC;;;;OAIG;uBACgB,WAAW,GAAG,OAAO;IAaxC;;;;OAIG;oDACiC,WAAW,EAAE;IAQjD;;;;;;;;;;;OAWG;wBACiB,WAAW,GAAG,IAAI;IA6BtC;;;;;;;;;;OAUG;sBACe,WAAW,eAAe,WAAW,GAAG,OAAO;IA2BjE;;;;;;OAMG;gCACyB,WAAW,GAAG,IAAI;IAoB9C;;;;OAIG;+BACwB,WAAW,GAAG,IAAI;IAY7C;;;;;;;;;;;;;;OAcG;mCAC4B,cAAc,GAAG,IAAI;CAqBrD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,SAEzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,YAAa,QAAQ,iBAAiB,CAAC,SAEhE,CAAC"}
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
|
3
|
-
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
|
4
|
-
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
|
5
|
-
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
|
6
|
-
r[k] = a[j];
|
|
7
|
-
return r;
|
|
8
|
-
};
|
|
9
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
4
|
};
|
|
12
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
6
|
exports.createAdapter = exports.resetStack = exports.PopupStack = exports.getValue = void 0;
|
|
14
|
-
|
|
7
|
+
const screenfull_1 = __importDefault(require("screenfull"));
|
|
15
8
|
function getLast(items) {
|
|
16
9
|
if (items.length) {
|
|
17
10
|
return items[items.length - 1];
|
|
@@ -25,30 +18,20 @@ function getLast(items) {
|
|
|
25
18
|
* popups.
|
|
26
19
|
*/
|
|
27
20
|
function getValue(index, length) {
|
|
28
|
-
|
|
21
|
+
const { min, max } = stack.zIndex;
|
|
29
22
|
if (length <= max - min) {
|
|
30
23
|
return index + min;
|
|
31
24
|
}
|
|
32
25
|
return Math.max(min, max - (length - index) + 1);
|
|
33
26
|
}
|
|
34
27
|
exports.getValue = getValue;
|
|
35
|
-
// IE11 doesn't support Array.prototype.find, so we'll polyfill here
|
|
36
|
-
function find(items, predicate) {
|
|
37
|
-
var length = items.length;
|
|
38
|
-
for (var i = 0; i < length; i++) {
|
|
39
|
-
if (predicate(items[i], i, items)) {
|
|
40
|
-
return items[i];
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
28
|
/**
|
|
46
29
|
* Sets the z-index value of all elements in the stack according to the `getValue` function. This
|
|
47
30
|
* will be run any time the stack changes.
|
|
48
31
|
*/
|
|
49
32
|
function setZIndexOfElements(elements) {
|
|
50
|
-
|
|
51
|
-
elements.forEach(
|
|
33
|
+
const length = elements.length;
|
|
34
|
+
elements.forEach((element, index) => {
|
|
52
35
|
element.style.zIndex = String(getValue(index, length));
|
|
53
36
|
});
|
|
54
37
|
}
|
|
@@ -57,9 +40,9 @@ function setZIndexOfElements(elements) {
|
|
|
57
40
|
* to the stack.
|
|
58
41
|
*/
|
|
59
42
|
function getOwnerPopup(element, items) {
|
|
60
|
-
|
|
43
|
+
let parentEl = element;
|
|
61
44
|
do {
|
|
62
|
-
|
|
45
|
+
const owner = items.find(el => el.element === parentEl);
|
|
63
46
|
if (owner) {
|
|
64
47
|
return owner.element;
|
|
65
48
|
}
|
|
@@ -72,10 +55,10 @@ function getOwnerPopup(element, items) {
|
|
|
72
55
|
* hierarchy.
|
|
73
56
|
*/
|
|
74
57
|
function getChildPopups(item, items) {
|
|
75
|
-
|
|
76
|
-
.filter(
|
|
77
|
-
.map(
|
|
78
|
-
.filter(
|
|
58
|
+
const owners = items
|
|
59
|
+
.filter(i => i.owner)
|
|
60
|
+
.map(i => ({ element: i.element, parent: getOwnerPopup(i.owner, items) }))
|
|
61
|
+
.filter(i => i.parent === item.element);
|
|
79
62
|
return owners;
|
|
80
63
|
}
|
|
81
64
|
/**
|
|
@@ -85,8 +68,8 @@ function getChildPopups(item, items) {
|
|
|
85
68
|
* @param path dot-notation path of a deep property
|
|
86
69
|
*/
|
|
87
70
|
function get(obj, path) {
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
const parts = path.split('.');
|
|
72
|
+
const first = parts.splice(0, 1)[0];
|
|
90
73
|
if (parts.length && obj[first]) {
|
|
91
74
|
return get(obj[first], parts.join('.'));
|
|
92
75
|
}
|
|
@@ -101,8 +84,8 @@ function get(obj, path) {
|
|
|
101
84
|
* @param value Any value
|
|
102
85
|
*/
|
|
103
86
|
function set(obj, path, value) {
|
|
104
|
-
|
|
105
|
-
|
|
87
|
+
const parts = path.split('.');
|
|
88
|
+
const first = parts.splice(0, 1)[0];
|
|
106
89
|
if (parts.length) {
|
|
107
90
|
if (obj[first] === undefined) {
|
|
108
91
|
obj[first] = {};
|
|
@@ -123,7 +106,7 @@ if (typeof window !== 'undefined') {
|
|
|
123
106
|
* environments without a `window` object, so it is safe to use in server-side rendering.
|
|
124
107
|
* @param path Any dot-notation path
|
|
125
108
|
*/
|
|
126
|
-
|
|
109
|
+
const getFromWindow = (path) => {
|
|
127
110
|
if (typeof window !== 'undefined') {
|
|
128
111
|
return get(window, path);
|
|
129
112
|
}
|
|
@@ -135,7 +118,7 @@ var getFromWindow = function (path) {
|
|
|
135
118
|
* @param path dot-notation path of a deep property
|
|
136
119
|
* @param value Any value
|
|
137
120
|
*/
|
|
138
|
-
|
|
121
|
+
const setToWindow = (path, value) => {
|
|
139
122
|
if (typeof window !== 'undefined') {
|
|
140
123
|
set(window, path, value);
|
|
141
124
|
}
|
|
@@ -143,20 +126,38 @@ var setToWindow = function (path, value) {
|
|
|
143
126
|
// We need to make sure only one stack is ever in use on the page - ever. If a stack is already
|
|
144
127
|
// defined on the page, we need to use that one. Never, ever, ever change this variable name on
|
|
145
128
|
// window
|
|
146
|
-
|
|
129
|
+
const stack = getFromWindow('workday.__popupStack') || {
|
|
147
130
|
description: 'Global popup stack from @workday/canvas-kit/popup-stack',
|
|
148
|
-
container:
|
|
131
|
+
container: () => document.body,
|
|
149
132
|
items: [],
|
|
150
133
|
zIndex: { min: 30, max: 50, getValue: getValue },
|
|
151
134
|
_adapter: {},
|
|
152
135
|
};
|
|
153
136
|
setToWindow('workday.__popupStack', stack);
|
|
154
|
-
|
|
137
|
+
const stacks = getFromWindow('workday.__popupStackOfStacks') || [stack];
|
|
155
138
|
stacks.description = 'Global stack of popup stacks from @workday/canvas-kit/popup-stack';
|
|
156
139
|
setToWindow('workday.__popupStackOfStacks', stacks);
|
|
157
140
|
function getTopStack() {
|
|
158
141
|
return stacks[stacks.length - 1];
|
|
159
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* The `PopupStack` is a framework agnostic first-in-last-out (FILO) stack that tracks all popups
|
|
145
|
+
* ("floating UI" or any UI that renders on top of other content). It contains methods that interact
|
|
146
|
+
* with the stack to support all coordinating behaviors of all popups on the page. The `PopupStack`
|
|
147
|
+
* helps:
|
|
148
|
+
*
|
|
149
|
+
* - Render popups in the right order on the page
|
|
150
|
+
* - Helps accessibility with the Escape key (topmost popup is closed)
|
|
151
|
+
* - Handles transition to [Full
|
|
152
|
+
* Screen](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API)
|
|
153
|
+
*
|
|
154
|
+
* The `PopupStack` supports adapters to work with existing popup systems. {@link createAdapter} is
|
|
155
|
+
* exported to accept an adapter. Only a single adapter should be used per page.
|
|
156
|
+
*
|
|
157
|
+
* The `PopupStack` is designed to handle multiple versions of `PopupStack` on the page at once
|
|
158
|
+
* while the internal FILO stack is shared between instances. You should not attempt to use the
|
|
159
|
+
* internal FILO stack. If an adapter is used, the internal FILO stack may be empty.
|
|
160
|
+
*/
|
|
160
161
|
exports.PopupStack = {
|
|
161
162
|
/**
|
|
162
163
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -164,13 +165,13 @@ exports.PopupStack = {
|
|
|
164
165
|
* element is added and removed from the DOM as well as the `z-index` style property. Your content
|
|
165
166
|
* should be added to this element.
|
|
166
167
|
*/
|
|
167
|
-
createContainer
|
|
168
|
+
createContainer() {
|
|
168
169
|
var _a;
|
|
169
|
-
|
|
170
|
+
const stack = getTopStack();
|
|
170
171
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.createContainer) {
|
|
171
172
|
return stack._adapter.createContainer();
|
|
172
173
|
}
|
|
173
|
-
|
|
174
|
+
const div = document.createElement('div');
|
|
174
175
|
div.style.position = 'relative'; // z-index only works on _positioned_ elements
|
|
175
176
|
return div;
|
|
176
177
|
},
|
|
@@ -180,9 +181,9 @@ exports.PopupStack = {
|
|
|
180
181
|
* popups to be registered initially, but rendered when the user triggers some event, call this
|
|
181
182
|
* method when the event triggers.
|
|
182
183
|
*/
|
|
183
|
-
add
|
|
184
|
+
add(item) {
|
|
184
185
|
var _a, _b;
|
|
185
|
-
|
|
186
|
+
const stack = getTopStack();
|
|
186
187
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.add) {
|
|
187
188
|
stack._adapter.add(item);
|
|
188
189
|
return;
|
|
@@ -198,16 +199,16 @@ exports.PopupStack = {
|
|
|
198
199
|
* called when the element is removed from the DOM. This method will reset z-index values of the
|
|
199
200
|
* stack.
|
|
200
201
|
*/
|
|
201
|
-
remove
|
|
202
|
+
remove(element) {
|
|
202
203
|
var _a, _b;
|
|
203
204
|
// Find the stack the popup belongs to.
|
|
204
|
-
|
|
205
|
+
const stack = stacks.find(stack => !!exports.PopupStack.getElements(stack).find(el => el === element));
|
|
205
206
|
if (stack) {
|
|
206
207
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.remove) {
|
|
207
208
|
stack._adapter.remove(element);
|
|
208
209
|
return;
|
|
209
210
|
}
|
|
210
|
-
stack.items = stack.items.filter(
|
|
211
|
+
stack.items = stack.items.filter(item => item.element !== element);
|
|
211
212
|
(((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) || document.body).removeChild(element);
|
|
212
213
|
setZIndexOfElements(exports.PopupStack.getElements(stack));
|
|
213
214
|
}
|
|
@@ -217,13 +218,13 @@ exports.PopupStack = {
|
|
|
217
218
|
* is not the top of the stack or is not found in the stack. The `element` should be the same
|
|
218
219
|
* reference that was passed to `add`
|
|
219
220
|
*/
|
|
220
|
-
isTopmost
|
|
221
|
+
isTopmost(element) {
|
|
221
222
|
var _a;
|
|
222
|
-
|
|
223
|
+
const stack = getTopStack();
|
|
223
224
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.isTopmost) {
|
|
224
225
|
return stack._adapter.isTopmost(element);
|
|
225
226
|
}
|
|
226
|
-
|
|
227
|
+
const last = getLast(stack.items);
|
|
227
228
|
if (last) {
|
|
228
229
|
return last.element === element;
|
|
229
230
|
}
|
|
@@ -234,13 +235,13 @@ exports.PopupStack = {
|
|
|
234
235
|
* elements in the order of lowest z-index to highest z-index. Some popup behaviors will need to
|
|
235
236
|
* make decisions based on z-index order.
|
|
236
237
|
*/
|
|
237
|
-
getElements
|
|
238
|
+
getElements(stackOverride) {
|
|
238
239
|
var _a;
|
|
239
|
-
|
|
240
|
+
const stack = stackOverride || getTopStack();
|
|
240
241
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.getElements) {
|
|
241
242
|
return stack._adapter.getElements();
|
|
242
243
|
}
|
|
243
|
-
return stack.items.map(
|
|
244
|
+
return stack.items.map(i => i.element);
|
|
244
245
|
},
|
|
245
246
|
/**
|
|
246
247
|
* Bring the element to the top of the stack. This is useful for persistent popups to place them
|
|
@@ -254,30 +255,30 @@ exports.PopupStack = {
|
|
|
254
255
|
* This does not need to be called when a popup is added since added popups are already place on
|
|
255
256
|
* the top of the stack.
|
|
256
257
|
*/
|
|
257
|
-
bringToTop
|
|
258
|
+
bringToTop(element) {
|
|
258
259
|
var _a;
|
|
259
|
-
|
|
260
|
+
const stack = getTopStack();
|
|
260
261
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.bringToTop) {
|
|
261
262
|
stack._adapter.bringToTop(element);
|
|
262
263
|
return;
|
|
263
264
|
}
|
|
264
|
-
|
|
265
|
+
const item = stack.items.find(i => i.element === element);
|
|
265
266
|
if (item) {
|
|
266
|
-
stack.items =
|
|
267
|
+
stack.items = [...stack.items.filter(i => i !== item), item];
|
|
267
268
|
// Also bring children to top. There are a few cases where stacking might break otherwise:
|
|
268
269
|
// - Clicking a Popup calls `bringToTop`, but mouse is over a Tooltip so that Tooltip is now
|
|
269
270
|
// under the Popup
|
|
270
271
|
// - Clicking a button opens a new Popup, but that click bubbles up to a `bringToTop` call
|
|
271
272
|
// putting the new popup under an existing one
|
|
272
273
|
// Example: https://user-images.githubusercontent.com/338257/83924476-031af580-a742-11ea-8f68-0edabdf0fd6a.gif
|
|
273
|
-
getChildPopups(item, stack.items).forEach(
|
|
274
|
+
getChildPopups(item, stack.items).forEach(popup => {
|
|
274
275
|
exports.PopupStack.bringToTop(popup.element);
|
|
275
276
|
});
|
|
276
277
|
setZIndexOfElements(exports.PopupStack.getElements());
|
|
277
278
|
}
|
|
278
279
|
else {
|
|
279
280
|
// not found
|
|
280
|
-
|
|
281
|
+
const e = new Error();
|
|
281
282
|
console.warn('Could not find item', e.stack);
|
|
282
283
|
}
|
|
283
284
|
},
|
|
@@ -292,22 +293,24 @@ exports.PopupStack = {
|
|
|
292
293
|
* opt-in to toggling. Otherwise there is no way to opt-out of toggle behavior (because the target
|
|
293
294
|
* is not inside `element`).
|
|
294
295
|
*/
|
|
295
|
-
contains
|
|
296
|
+
contains(element, eventTarget) {
|
|
296
297
|
var _a, _b;
|
|
297
|
-
|
|
298
|
+
const stack = getTopStack();
|
|
298
299
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.contains) {
|
|
299
300
|
return stack._adapter.contains(element, eventTarget);
|
|
300
301
|
}
|
|
301
|
-
|
|
302
|
-
|
|
302
|
+
const item = stack.items.find(i => i.element === element);
|
|
303
|
+
const containsOwnerOfAnotherPopupTargeted = stack.items.some(currentItem => {
|
|
303
304
|
var _a;
|
|
304
305
|
return ((item === null || item === void 0 ? void 0 : item.element.contains(currentItem.owner || null)) &&
|
|
305
|
-
(eventTarget === currentItem.owner ||
|
|
306
|
+
(eventTarget === currentItem.owner ||
|
|
307
|
+
((_a = currentItem.owner) === null || _a === void 0 ? void 0 : _a.contains(eventTarget)) ||
|
|
306
308
|
currentItem.element.contains(eventTarget)));
|
|
307
309
|
});
|
|
308
310
|
if (item) {
|
|
309
311
|
return (containsOwnerOfAnotherPopupTargeted ||
|
|
310
|
-
eventTarget === item.owner ||
|
|
312
|
+
eventTarget === item.owner ||
|
|
313
|
+
((_b = item.owner) === null || _b === void 0 ? void 0 : _b.contains(eventTarget)) ||
|
|
311
314
|
element.contains(eventTarget));
|
|
312
315
|
}
|
|
313
316
|
return false;
|
|
@@ -319,9 +322,9 @@ exports.PopupStack = {
|
|
|
319
322
|
* pushing the same element which is very difficult to ensure only one stack is used. To mitigate,
|
|
320
323
|
* this method filters out multiple calls to push the same element as a new stack context.
|
|
321
324
|
*/
|
|
322
|
-
pushStackContext
|
|
325
|
+
pushStackContext(container) {
|
|
323
326
|
var _a, _b;
|
|
324
|
-
|
|
327
|
+
const stack = getTopStack();
|
|
325
328
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.pushStackContext) {
|
|
326
329
|
return stack._adapter.pushStackContext(container);
|
|
327
330
|
}
|
|
@@ -329,10 +332,10 @@ exports.PopupStack = {
|
|
|
329
332
|
if (((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) === container) {
|
|
330
333
|
return;
|
|
331
334
|
}
|
|
332
|
-
|
|
335
|
+
const newStack = {
|
|
333
336
|
items: [],
|
|
334
337
|
zIndex: stack.zIndex,
|
|
335
|
-
container:
|
|
338
|
+
container: () => container,
|
|
336
339
|
_adapter: {},
|
|
337
340
|
};
|
|
338
341
|
stacks.push(newStack);
|
|
@@ -342,9 +345,9 @@ exports.PopupStack = {
|
|
|
342
345
|
* context container element matches to guard against accidental remove of other stack contexts
|
|
343
346
|
* you don't own.
|
|
344
347
|
*/
|
|
345
|
-
popStackContext
|
|
348
|
+
popStackContext(container) {
|
|
346
349
|
var _a, _b;
|
|
347
|
-
|
|
350
|
+
const stack = getTopStack();
|
|
348
351
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.popStackContext) {
|
|
349
352
|
return stack._adapter.popStackContext(container);
|
|
350
353
|
}
|
|
@@ -367,19 +370,19 @@ exports.PopupStack = {
|
|
|
367
370
|
* members of different popup stack contexts which will cause unspecified results (like the escape
|
|
368
371
|
* key will choose the wrong popup as the "topmost").
|
|
369
372
|
*/
|
|
370
|
-
transferToCurrentContext
|
|
373
|
+
transferToCurrentContext(item) {
|
|
371
374
|
var _a;
|
|
372
|
-
|
|
375
|
+
const stack = getTopStack();
|
|
373
376
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.transferToCurrentContext) {
|
|
374
377
|
return stack._adapter.transferToCurrentContext(item);
|
|
375
378
|
}
|
|
376
|
-
if (
|
|
379
|
+
if (stack.items.find(i => i.element === item.element)) {
|
|
377
380
|
// The element is already in the stack, don't do anything
|
|
378
381
|
return;
|
|
379
382
|
}
|
|
380
383
|
// Try to find the element in existing stacks. If it exists, we need to first remove from that
|
|
381
384
|
// stack context
|
|
382
|
-
|
|
385
|
+
const oldStack = stacks.find(stack => !!stack.items.find(i => i.element === item.element));
|
|
383
386
|
if (oldStack) {
|
|
384
387
|
exports.PopupStack.remove(item.element);
|
|
385
388
|
}
|
|
@@ -395,21 +398,25 @@ function resetStack() {
|
|
|
395
398
|
}
|
|
396
399
|
exports.resetStack = resetStack;
|
|
397
400
|
/**
|
|
401
|
+
* An adapter is a custom implementation of the {@link PopupStack}. There is only ever a single
|
|
402
|
+
* instance of an adapter on the page. It allows an adapter to intercept any `PopupStack` method.
|
|
403
|
+
* This could bypass the internal FILO stack of the `PopupStack` and allows the FILO stack to be
|
|
404
|
+
* handled by something else.
|
|
398
405
|
*
|
|
399
406
|
* @param adapter The parts of the PopupStack that we want to override
|
|
400
407
|
*/
|
|
401
|
-
|
|
408
|
+
const createAdapter = (adapter) => {
|
|
402
409
|
stack._adapter = adapter;
|
|
403
410
|
};
|
|
404
411
|
exports.createAdapter = createAdapter;
|
|
405
412
|
// keep track of the element ourselves to avoid accidentally popping off someone else's stack
|
|
406
413
|
// context
|
|
407
|
-
|
|
414
|
+
let element = null;
|
|
408
415
|
// Where should this go? Each version of `PopupStack` on a page will add a listener. The
|
|
409
416
|
// `PopupStack` should guard against multiple handlers like this simultaneously and there is no
|
|
410
417
|
// lifecycle here.
|
|
411
418
|
if (screenfull_1.default.isEnabled) {
|
|
412
|
-
screenfull_1.default.on('change',
|
|
419
|
+
screenfull_1.default.on('change', () => {
|
|
413
420
|
if (screenfull_1.default.isFullscreen) {
|
|
414
421
|
if (screenfull_1.default.element) {
|
|
415
422
|
element = screenfull_1.default.element;
|
|
@@ -42,6 +42,24 @@ interface Stack {
|
|
|
42
42
|
};
|
|
43
43
|
_adapter: Partial<typeof PopupStack>;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The `PopupStack` is a framework agnostic first-in-last-out (FILO) stack that tracks all popups
|
|
47
|
+
* ("floating UI" or any UI that renders on top of other content). It contains methods that interact
|
|
48
|
+
* with the stack to support all coordinating behaviors of all popups on the page. The `PopupStack`
|
|
49
|
+
* helps:
|
|
50
|
+
*
|
|
51
|
+
* - Render popups in the right order on the page
|
|
52
|
+
* - Helps accessibility with the Escape key (topmost popup is closed)
|
|
53
|
+
* - Handles transition to [Full
|
|
54
|
+
* Screen](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API)
|
|
55
|
+
*
|
|
56
|
+
* The `PopupStack` supports adapters to work with existing popup systems. {@link createAdapter} is
|
|
57
|
+
* exported to accept an adapter. Only a single adapter should be used per page.
|
|
58
|
+
*
|
|
59
|
+
* The `PopupStack` is designed to handle multiple versions of `PopupStack` on the page at once
|
|
60
|
+
* while the internal FILO stack is shared between instances. You should not attempt to use the
|
|
61
|
+
* internal FILO stack. If an adapter is used, the internal FILO stack may be empty.
|
|
62
|
+
*/
|
|
45
63
|
export declare const PopupStack: {
|
|
46
64
|
/**
|
|
47
65
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -139,6 +157,10 @@ export declare const PopupStack: {
|
|
|
139
157
|
*/
|
|
140
158
|
export declare function resetStack(): void;
|
|
141
159
|
/**
|
|
160
|
+
* An adapter is a custom implementation of the {@link PopupStack}. There is only ever a single
|
|
161
|
+
* instance of an adapter on the page. It allows an adapter to intercept any `PopupStack` method.
|
|
162
|
+
* This could bypass the internal FILO stack of the `PopupStack` and allows the FILO stack to be
|
|
163
|
+
* handled by something else.
|
|
142
164
|
*
|
|
143
165
|
* @param adapter The parts of the PopupStack that we want to override
|
|
144
166
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PopupStack.d.ts","sourceRoot":"","sources":["../../../lib/PopupStack.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AASD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO9D;
|
|
1
|
+
{"version":3,"file":"PopupStack.d.ts","sourceRoot":"","sources":["../../../lib/PopupStack.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AASD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO9D;AA2CD,UAAU,KAAK;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,WAAW,CAAC;IAC9B,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,OAAO,QAAQ,CAAC;KAC3B,CAAC;IACF,QAAQ,EAAE,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC;CACtC;AAwFD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU;IACrB;;;;;OAKG;uBACgB,WAAW;IAS9B;;;;;OAKG;cACO,cAAc,GAAG,IAAI;IAY/B;;;;;;OAMG;oBACa,WAAW,GAAG,IAAI;IAelC;;;;OAIG;uBACgB,WAAW,GAAG,OAAO;IAaxC;;;;OAIG;oDACiC,WAAW,EAAE;IAQjD;;;;;;;;;;;OAWG;wBACiB,WAAW,GAAG,IAAI;IA6BtC;;;;;;;;;;OAUG;sBACe,WAAW,eAAe,WAAW,GAAG,OAAO;IA2BjE;;;;;;OAMG;gCACyB,WAAW,GAAG,IAAI;IAoB9C;;;;OAIG;+BACwB,WAAW,GAAG,IAAI;IAY7C;;;;;;;;;;;;;;OAcG;mCAC4B,cAAc,GAAG,IAAI;CAqBrD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,SAEzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,YAAa,QAAQ,iBAAiB,CAAC,SAEhE,CAAC"}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
|
2
|
-
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
|
3
|
-
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
|
4
|
-
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
|
5
|
-
r[k] = a[j];
|
|
6
|
-
return r;
|
|
7
|
-
};
|
|
8
1
|
import screenfull from 'screenfull';
|
|
9
2
|
function getLast(items) {
|
|
10
3
|
if (items.length) {
|
|
@@ -19,29 +12,19 @@ function getLast(items) {
|
|
|
19
12
|
* popups.
|
|
20
13
|
*/
|
|
21
14
|
export function getValue(index, length) {
|
|
22
|
-
|
|
15
|
+
const { min, max } = stack.zIndex;
|
|
23
16
|
if (length <= max - min) {
|
|
24
17
|
return index + min;
|
|
25
18
|
}
|
|
26
19
|
return Math.max(min, max - (length - index) + 1);
|
|
27
20
|
}
|
|
28
|
-
// IE11 doesn't support Array.prototype.find, so we'll polyfill here
|
|
29
|
-
function find(items, predicate) {
|
|
30
|
-
var length = items.length;
|
|
31
|
-
for (var i = 0; i < length; i++) {
|
|
32
|
-
if (predicate(items[i], i, items)) {
|
|
33
|
-
return items[i];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
21
|
/**
|
|
39
22
|
* Sets the z-index value of all elements in the stack according to the `getValue` function. This
|
|
40
23
|
* will be run any time the stack changes.
|
|
41
24
|
*/
|
|
42
25
|
function setZIndexOfElements(elements) {
|
|
43
|
-
|
|
44
|
-
elements.forEach(
|
|
26
|
+
const length = elements.length;
|
|
27
|
+
elements.forEach((element, index) => {
|
|
45
28
|
element.style.zIndex = String(getValue(index, length));
|
|
46
29
|
});
|
|
47
30
|
}
|
|
@@ -50,9 +33,9 @@ function setZIndexOfElements(elements) {
|
|
|
50
33
|
* to the stack.
|
|
51
34
|
*/
|
|
52
35
|
function getOwnerPopup(element, items) {
|
|
53
|
-
|
|
36
|
+
let parentEl = element;
|
|
54
37
|
do {
|
|
55
|
-
|
|
38
|
+
const owner = items.find(el => el.element === parentEl);
|
|
56
39
|
if (owner) {
|
|
57
40
|
return owner.element;
|
|
58
41
|
}
|
|
@@ -65,10 +48,10 @@ function getOwnerPopup(element, items) {
|
|
|
65
48
|
* hierarchy.
|
|
66
49
|
*/
|
|
67
50
|
function getChildPopups(item, items) {
|
|
68
|
-
|
|
69
|
-
.filter(
|
|
70
|
-
.map(
|
|
71
|
-
.filter(
|
|
51
|
+
const owners = items
|
|
52
|
+
.filter(i => i.owner)
|
|
53
|
+
.map(i => ({ element: i.element, parent: getOwnerPopup(i.owner, items) }))
|
|
54
|
+
.filter(i => i.parent === item.element);
|
|
72
55
|
return owners;
|
|
73
56
|
}
|
|
74
57
|
/**
|
|
@@ -78,8 +61,8 @@ function getChildPopups(item, items) {
|
|
|
78
61
|
* @param path dot-notation path of a deep property
|
|
79
62
|
*/
|
|
80
63
|
function get(obj, path) {
|
|
81
|
-
|
|
82
|
-
|
|
64
|
+
const parts = path.split('.');
|
|
65
|
+
const first = parts.splice(0, 1)[0];
|
|
83
66
|
if (parts.length && obj[first]) {
|
|
84
67
|
return get(obj[first], parts.join('.'));
|
|
85
68
|
}
|
|
@@ -94,8 +77,8 @@ function get(obj, path) {
|
|
|
94
77
|
* @param value Any value
|
|
95
78
|
*/
|
|
96
79
|
function set(obj, path, value) {
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
const parts = path.split('.');
|
|
81
|
+
const first = parts.splice(0, 1)[0];
|
|
99
82
|
if (parts.length) {
|
|
100
83
|
if (obj[first] === undefined) {
|
|
101
84
|
obj[first] = {};
|
|
@@ -116,7 +99,7 @@ if (typeof window !== 'undefined') {
|
|
|
116
99
|
* environments without a `window` object, so it is safe to use in server-side rendering.
|
|
117
100
|
* @param path Any dot-notation path
|
|
118
101
|
*/
|
|
119
|
-
|
|
102
|
+
const getFromWindow = (path) => {
|
|
120
103
|
if (typeof window !== 'undefined') {
|
|
121
104
|
return get(window, path);
|
|
122
105
|
}
|
|
@@ -128,7 +111,7 @@ var getFromWindow = function (path) {
|
|
|
128
111
|
* @param path dot-notation path of a deep property
|
|
129
112
|
* @param value Any value
|
|
130
113
|
*/
|
|
131
|
-
|
|
114
|
+
const setToWindow = (path, value) => {
|
|
132
115
|
if (typeof window !== 'undefined') {
|
|
133
116
|
set(window, path, value);
|
|
134
117
|
}
|
|
@@ -136,34 +119,52 @@ var setToWindow = function (path, value) {
|
|
|
136
119
|
// We need to make sure only one stack is ever in use on the page - ever. If a stack is already
|
|
137
120
|
// defined on the page, we need to use that one. Never, ever, ever change this variable name on
|
|
138
121
|
// window
|
|
139
|
-
|
|
122
|
+
const stack = getFromWindow('workday.__popupStack') || {
|
|
140
123
|
description: 'Global popup stack from @workday/canvas-kit/popup-stack',
|
|
141
|
-
container:
|
|
124
|
+
container: () => document.body,
|
|
142
125
|
items: [],
|
|
143
126
|
zIndex: { min: 30, max: 50, getValue: getValue },
|
|
144
127
|
_adapter: {},
|
|
145
128
|
};
|
|
146
129
|
setToWindow('workday.__popupStack', stack);
|
|
147
|
-
|
|
130
|
+
const stacks = getFromWindow('workday.__popupStackOfStacks') || [stack];
|
|
148
131
|
stacks.description = 'Global stack of popup stacks from @workday/canvas-kit/popup-stack';
|
|
149
132
|
setToWindow('workday.__popupStackOfStacks', stacks);
|
|
150
133
|
function getTopStack() {
|
|
151
134
|
return stacks[stacks.length - 1];
|
|
152
135
|
}
|
|
153
|
-
|
|
136
|
+
/**
|
|
137
|
+
* The `PopupStack` is a framework agnostic first-in-last-out (FILO) stack that tracks all popups
|
|
138
|
+
* ("floating UI" or any UI that renders on top of other content). It contains methods that interact
|
|
139
|
+
* with the stack to support all coordinating behaviors of all popups on the page. The `PopupStack`
|
|
140
|
+
* helps:
|
|
141
|
+
*
|
|
142
|
+
* - Render popups in the right order on the page
|
|
143
|
+
* - Helps accessibility with the Escape key (topmost popup is closed)
|
|
144
|
+
* - Handles transition to [Full
|
|
145
|
+
* Screen](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API)
|
|
146
|
+
*
|
|
147
|
+
* The `PopupStack` supports adapters to work with existing popup systems. {@link createAdapter} is
|
|
148
|
+
* exported to accept an adapter. Only a single adapter should be used per page.
|
|
149
|
+
*
|
|
150
|
+
* The `PopupStack` is designed to handle multiple versions of `PopupStack` on the page at once
|
|
151
|
+
* while the internal FILO stack is shared between instances. You should not attempt to use the
|
|
152
|
+
* internal FILO stack. If an adapter is used, the internal FILO stack may be empty.
|
|
153
|
+
*/
|
|
154
|
+
export const PopupStack = {
|
|
154
155
|
/**
|
|
155
156
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
156
157
|
* will be the reference to be passed to all other methods. The Popup Stack will control when this
|
|
157
158
|
* element is added and removed from the DOM as well as the `z-index` style property. Your content
|
|
158
159
|
* should be added to this element.
|
|
159
160
|
*/
|
|
160
|
-
createContainer
|
|
161
|
+
createContainer() {
|
|
161
162
|
var _a;
|
|
162
|
-
|
|
163
|
+
const stack = getTopStack();
|
|
163
164
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.createContainer) {
|
|
164
165
|
return stack._adapter.createContainer();
|
|
165
166
|
}
|
|
166
|
-
|
|
167
|
+
const div = document.createElement('div');
|
|
167
168
|
div.style.position = 'relative'; // z-index only works on _positioned_ elements
|
|
168
169
|
return div;
|
|
169
170
|
},
|
|
@@ -173,9 +174,9 @@ export var PopupStack = {
|
|
|
173
174
|
* popups to be registered initially, but rendered when the user triggers some event, call this
|
|
174
175
|
* method when the event triggers.
|
|
175
176
|
*/
|
|
176
|
-
add
|
|
177
|
+
add(item) {
|
|
177
178
|
var _a, _b;
|
|
178
|
-
|
|
179
|
+
const stack = getTopStack();
|
|
179
180
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.add) {
|
|
180
181
|
stack._adapter.add(item);
|
|
181
182
|
return;
|
|
@@ -191,16 +192,16 @@ export var PopupStack = {
|
|
|
191
192
|
* called when the element is removed from the DOM. This method will reset z-index values of the
|
|
192
193
|
* stack.
|
|
193
194
|
*/
|
|
194
|
-
remove
|
|
195
|
+
remove(element) {
|
|
195
196
|
var _a, _b;
|
|
196
197
|
// Find the stack the popup belongs to.
|
|
197
|
-
|
|
198
|
+
const stack = stacks.find(stack => !!PopupStack.getElements(stack).find(el => el === element));
|
|
198
199
|
if (stack) {
|
|
199
200
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.remove) {
|
|
200
201
|
stack._adapter.remove(element);
|
|
201
202
|
return;
|
|
202
203
|
}
|
|
203
|
-
stack.items = stack.items.filter(
|
|
204
|
+
stack.items = stack.items.filter(item => item.element !== element);
|
|
204
205
|
(((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) || document.body).removeChild(element);
|
|
205
206
|
setZIndexOfElements(PopupStack.getElements(stack));
|
|
206
207
|
}
|
|
@@ -210,13 +211,13 @@ export var PopupStack = {
|
|
|
210
211
|
* is not the top of the stack or is not found in the stack. The `element` should be the same
|
|
211
212
|
* reference that was passed to `add`
|
|
212
213
|
*/
|
|
213
|
-
isTopmost
|
|
214
|
+
isTopmost(element) {
|
|
214
215
|
var _a;
|
|
215
|
-
|
|
216
|
+
const stack = getTopStack();
|
|
216
217
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.isTopmost) {
|
|
217
218
|
return stack._adapter.isTopmost(element);
|
|
218
219
|
}
|
|
219
|
-
|
|
220
|
+
const last = getLast(stack.items);
|
|
220
221
|
if (last) {
|
|
221
222
|
return last.element === element;
|
|
222
223
|
}
|
|
@@ -227,13 +228,13 @@ export var PopupStack = {
|
|
|
227
228
|
* elements in the order of lowest z-index to highest z-index. Some popup behaviors will need to
|
|
228
229
|
* make decisions based on z-index order.
|
|
229
230
|
*/
|
|
230
|
-
getElements
|
|
231
|
+
getElements(stackOverride) {
|
|
231
232
|
var _a;
|
|
232
|
-
|
|
233
|
+
const stack = stackOverride || getTopStack();
|
|
233
234
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.getElements) {
|
|
234
235
|
return stack._adapter.getElements();
|
|
235
236
|
}
|
|
236
|
-
return stack.items.map(
|
|
237
|
+
return stack.items.map(i => i.element);
|
|
237
238
|
},
|
|
238
239
|
/**
|
|
239
240
|
* Bring the element to the top of the stack. This is useful for persistent popups to place them
|
|
@@ -247,30 +248,30 @@ export var PopupStack = {
|
|
|
247
248
|
* This does not need to be called when a popup is added since added popups are already place on
|
|
248
249
|
* the top of the stack.
|
|
249
250
|
*/
|
|
250
|
-
bringToTop
|
|
251
|
+
bringToTop(element) {
|
|
251
252
|
var _a;
|
|
252
|
-
|
|
253
|
+
const stack = getTopStack();
|
|
253
254
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.bringToTop) {
|
|
254
255
|
stack._adapter.bringToTop(element);
|
|
255
256
|
return;
|
|
256
257
|
}
|
|
257
|
-
|
|
258
|
+
const item = stack.items.find(i => i.element === element);
|
|
258
259
|
if (item) {
|
|
259
|
-
stack.items =
|
|
260
|
+
stack.items = [...stack.items.filter(i => i !== item), item];
|
|
260
261
|
// Also bring children to top. There are a few cases where stacking might break otherwise:
|
|
261
262
|
// - Clicking a Popup calls `bringToTop`, but mouse is over a Tooltip so that Tooltip is now
|
|
262
263
|
// under the Popup
|
|
263
264
|
// - Clicking a button opens a new Popup, but that click bubbles up to a `bringToTop` call
|
|
264
265
|
// putting the new popup under an existing one
|
|
265
266
|
// Example: https://user-images.githubusercontent.com/338257/83924476-031af580-a742-11ea-8f68-0edabdf0fd6a.gif
|
|
266
|
-
getChildPopups(item, stack.items).forEach(
|
|
267
|
+
getChildPopups(item, stack.items).forEach(popup => {
|
|
267
268
|
PopupStack.bringToTop(popup.element);
|
|
268
269
|
});
|
|
269
270
|
setZIndexOfElements(PopupStack.getElements());
|
|
270
271
|
}
|
|
271
272
|
else {
|
|
272
273
|
// not found
|
|
273
|
-
|
|
274
|
+
const e = new Error();
|
|
274
275
|
console.warn('Could not find item', e.stack);
|
|
275
276
|
}
|
|
276
277
|
},
|
|
@@ -285,22 +286,24 @@ export var PopupStack = {
|
|
|
285
286
|
* opt-in to toggling. Otherwise there is no way to opt-out of toggle behavior (because the target
|
|
286
287
|
* is not inside `element`).
|
|
287
288
|
*/
|
|
288
|
-
contains
|
|
289
|
+
contains(element, eventTarget) {
|
|
289
290
|
var _a, _b;
|
|
290
|
-
|
|
291
|
+
const stack = getTopStack();
|
|
291
292
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.contains) {
|
|
292
293
|
return stack._adapter.contains(element, eventTarget);
|
|
293
294
|
}
|
|
294
|
-
|
|
295
|
-
|
|
295
|
+
const item = stack.items.find(i => i.element === element);
|
|
296
|
+
const containsOwnerOfAnotherPopupTargeted = stack.items.some(currentItem => {
|
|
296
297
|
var _a;
|
|
297
298
|
return ((item === null || item === void 0 ? void 0 : item.element.contains(currentItem.owner || null)) &&
|
|
298
|
-
(eventTarget === currentItem.owner ||
|
|
299
|
+
(eventTarget === currentItem.owner ||
|
|
300
|
+
((_a = currentItem.owner) === null || _a === void 0 ? void 0 : _a.contains(eventTarget)) ||
|
|
299
301
|
currentItem.element.contains(eventTarget)));
|
|
300
302
|
});
|
|
301
303
|
if (item) {
|
|
302
304
|
return (containsOwnerOfAnotherPopupTargeted ||
|
|
303
|
-
eventTarget === item.owner ||
|
|
305
|
+
eventTarget === item.owner ||
|
|
306
|
+
((_b = item.owner) === null || _b === void 0 ? void 0 : _b.contains(eventTarget)) ||
|
|
304
307
|
element.contains(eventTarget));
|
|
305
308
|
}
|
|
306
309
|
return false;
|
|
@@ -312,9 +315,9 @@ export var PopupStack = {
|
|
|
312
315
|
* pushing the same element which is very difficult to ensure only one stack is used. To mitigate,
|
|
313
316
|
* this method filters out multiple calls to push the same element as a new stack context.
|
|
314
317
|
*/
|
|
315
|
-
pushStackContext
|
|
318
|
+
pushStackContext(container) {
|
|
316
319
|
var _a, _b;
|
|
317
|
-
|
|
320
|
+
const stack = getTopStack();
|
|
318
321
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.pushStackContext) {
|
|
319
322
|
return stack._adapter.pushStackContext(container);
|
|
320
323
|
}
|
|
@@ -322,10 +325,10 @@ export var PopupStack = {
|
|
|
322
325
|
if (((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) === container) {
|
|
323
326
|
return;
|
|
324
327
|
}
|
|
325
|
-
|
|
328
|
+
const newStack = {
|
|
326
329
|
items: [],
|
|
327
330
|
zIndex: stack.zIndex,
|
|
328
|
-
container:
|
|
331
|
+
container: () => container,
|
|
329
332
|
_adapter: {},
|
|
330
333
|
};
|
|
331
334
|
stacks.push(newStack);
|
|
@@ -335,9 +338,9 @@ export var PopupStack = {
|
|
|
335
338
|
* context container element matches to guard against accidental remove of other stack contexts
|
|
336
339
|
* you don't own.
|
|
337
340
|
*/
|
|
338
|
-
popStackContext
|
|
341
|
+
popStackContext(container) {
|
|
339
342
|
var _a, _b;
|
|
340
|
-
|
|
343
|
+
const stack = getTopStack();
|
|
341
344
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.popStackContext) {
|
|
342
345
|
return stack._adapter.popStackContext(container);
|
|
343
346
|
}
|
|
@@ -360,19 +363,19 @@ export var PopupStack = {
|
|
|
360
363
|
* members of different popup stack contexts which will cause unspecified results (like the escape
|
|
361
364
|
* key will choose the wrong popup as the "topmost").
|
|
362
365
|
*/
|
|
363
|
-
transferToCurrentContext
|
|
366
|
+
transferToCurrentContext(item) {
|
|
364
367
|
var _a;
|
|
365
|
-
|
|
368
|
+
const stack = getTopStack();
|
|
366
369
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.transferToCurrentContext) {
|
|
367
370
|
return stack._adapter.transferToCurrentContext(item);
|
|
368
371
|
}
|
|
369
|
-
if (
|
|
372
|
+
if (stack.items.find(i => i.element === item.element)) {
|
|
370
373
|
// The element is already in the stack, don't do anything
|
|
371
374
|
return;
|
|
372
375
|
}
|
|
373
376
|
// Try to find the element in existing stacks. If it exists, we need to first remove from that
|
|
374
377
|
// stack context
|
|
375
|
-
|
|
378
|
+
const oldStack = stacks.find(stack => !!stack.items.find(i => i.element === item.element));
|
|
376
379
|
if (oldStack) {
|
|
377
380
|
PopupStack.remove(item.element);
|
|
378
381
|
}
|
|
@@ -387,20 +390,24 @@ export function resetStack() {
|
|
|
387
390
|
stack.items = [];
|
|
388
391
|
}
|
|
389
392
|
/**
|
|
393
|
+
* An adapter is a custom implementation of the {@link PopupStack}. There is only ever a single
|
|
394
|
+
* instance of an adapter on the page. It allows an adapter to intercept any `PopupStack` method.
|
|
395
|
+
* This could bypass the internal FILO stack of the `PopupStack` and allows the FILO stack to be
|
|
396
|
+
* handled by something else.
|
|
390
397
|
*
|
|
391
398
|
* @param adapter The parts of the PopupStack that we want to override
|
|
392
399
|
*/
|
|
393
|
-
export
|
|
400
|
+
export const createAdapter = (adapter) => {
|
|
394
401
|
stack._adapter = adapter;
|
|
395
402
|
};
|
|
396
403
|
// keep track of the element ourselves to avoid accidentally popping off someone else's stack
|
|
397
404
|
// context
|
|
398
|
-
|
|
405
|
+
let element = null;
|
|
399
406
|
// Where should this go? Each version of `PopupStack` on a page will add a listener. The
|
|
400
407
|
// `PopupStack` should guard against multiple handlers like this simultaneously and there is no
|
|
401
408
|
// lifecycle here.
|
|
402
409
|
if (screenfull.isEnabled) {
|
|
403
|
-
screenfull.on('change',
|
|
410
|
+
screenfull.on('change', () => {
|
|
404
411
|
if (screenfull.isFullscreen) {
|
|
405
412
|
if (screenfull.element) {
|
|
406
413
|
element = screenfull.element;
|
package/lib/PopupStack.ts
CHANGED
|
@@ -50,20 +50,6 @@ export function getValue(index: number, length: number): number {
|
|
|
50
50
|
return Math.max(min, max - (length - index) + 1);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// IE11 doesn't support Array.prototype.find, so we'll polyfill here
|
|
54
|
-
function find<T>(
|
|
55
|
-
items: T[],
|
|
56
|
-
predicate: (value: T, index: number, obj: T[]) => boolean
|
|
57
|
-
): T | undefined {
|
|
58
|
-
const length = items.length;
|
|
59
|
-
for (let i = 0; i < length; i++) {
|
|
60
|
-
if (predicate(items[i], i, items)) {
|
|
61
|
-
return items[i];
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
53
|
/**
|
|
68
54
|
* Sets the z-index value of all elements in the stack according to the `getValue` function. This
|
|
69
55
|
* will be run any time the stack changes.
|
|
@@ -82,7 +68,7 @@ function setZIndexOfElements(elements: HTMLElement[]): void {
|
|
|
82
68
|
function getOwnerPopup(element: HTMLElement, items: PopupStackItem[]): HTMLElement | undefined {
|
|
83
69
|
let parentEl: HTMLElement | null = element;
|
|
84
70
|
do {
|
|
85
|
-
const owner = find(
|
|
71
|
+
const owner = items.find(el => el.element === parentEl);
|
|
86
72
|
if (owner) {
|
|
87
73
|
return owner.element;
|
|
88
74
|
}
|
|
@@ -202,6 +188,24 @@ function getTopStack() {
|
|
|
202
188
|
return stacks[stacks.length - 1];
|
|
203
189
|
}
|
|
204
190
|
|
|
191
|
+
/**
|
|
192
|
+
* The `PopupStack` is a framework agnostic first-in-last-out (FILO) stack that tracks all popups
|
|
193
|
+
* ("floating UI" or any UI that renders on top of other content). It contains methods that interact
|
|
194
|
+
* with the stack to support all coordinating behaviors of all popups on the page. The `PopupStack`
|
|
195
|
+
* helps:
|
|
196
|
+
*
|
|
197
|
+
* - Render popups in the right order on the page
|
|
198
|
+
* - Helps accessibility with the Escape key (topmost popup is closed)
|
|
199
|
+
* - Handles transition to [Full
|
|
200
|
+
* Screen](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API)
|
|
201
|
+
*
|
|
202
|
+
* The `PopupStack` supports adapters to work with existing popup systems. {@link createAdapter} is
|
|
203
|
+
* exported to accept an adapter. Only a single adapter should be used per page.
|
|
204
|
+
*
|
|
205
|
+
* The `PopupStack` is designed to handle multiple versions of `PopupStack` on the page at once
|
|
206
|
+
* while the internal FILO stack is shared between instances. You should not attempt to use the
|
|
207
|
+
* internal FILO stack. If an adapter is used, the internal FILO stack may be empty.
|
|
208
|
+
*/
|
|
205
209
|
export const PopupStack = {
|
|
206
210
|
/**
|
|
207
211
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -245,10 +249,7 @@ export const PopupStack = {
|
|
|
245
249
|
*/
|
|
246
250
|
remove(element: HTMLElement): void {
|
|
247
251
|
// Find the stack the popup belongs to.
|
|
248
|
-
const stack = find(
|
|
249
|
-
stacks,
|
|
250
|
-
stack => !!find(PopupStack.getElements(stack), el => el === element)
|
|
251
|
-
);
|
|
252
|
+
const stack = stacks.find(stack => !!PopupStack.getElements(stack).find(el => el === element));
|
|
252
253
|
if (stack) {
|
|
253
254
|
if (stack._adapter?.remove) {
|
|
254
255
|
stack._adapter.remove(element);
|
|
@@ -310,7 +311,7 @@ export const PopupStack = {
|
|
|
310
311
|
stack._adapter.bringToTop(element);
|
|
311
312
|
return;
|
|
312
313
|
}
|
|
313
|
-
const item =
|
|
314
|
+
const item = stack.items.find(i => i.element === element);
|
|
314
315
|
|
|
315
316
|
if (item) {
|
|
316
317
|
stack.items = [...stack.items.filter(i => i !== item), item];
|
|
@@ -349,7 +350,7 @@ export const PopupStack = {
|
|
|
349
350
|
if (stack._adapter?.contains) {
|
|
350
351
|
return stack._adapter.contains(element, eventTarget);
|
|
351
352
|
}
|
|
352
|
-
const item =
|
|
353
|
+
const item = stack.items.find(i => i.element === element);
|
|
353
354
|
|
|
354
355
|
const containsOwnerOfAnotherPopupTargeted = stack.items.some(currentItem => {
|
|
355
356
|
return (
|
|
@@ -437,14 +438,14 @@ export const PopupStack = {
|
|
|
437
438
|
return stack._adapter.transferToCurrentContext(item);
|
|
438
439
|
}
|
|
439
440
|
|
|
440
|
-
if (
|
|
441
|
+
if (stack.items.find(i => i.element === item.element)) {
|
|
441
442
|
// The element is already in the stack, don't do anything
|
|
442
443
|
return;
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
// Try to find the element in existing stacks. If it exists, we need to first remove from that
|
|
446
447
|
// stack context
|
|
447
|
-
const oldStack = find(
|
|
448
|
+
const oldStack = stacks.find(stack => !!stack.items.find(i => i.element === item.element));
|
|
448
449
|
if (oldStack) {
|
|
449
450
|
PopupStack.remove(item.element);
|
|
450
451
|
}
|
|
@@ -462,6 +463,10 @@ export function resetStack() {
|
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
/**
|
|
466
|
+
* An adapter is a custom implementation of the {@link PopupStack}. There is only ever a single
|
|
467
|
+
* instance of an adapter on the page. It allows an adapter to intercept any `PopupStack` method.
|
|
468
|
+
* This could bypass the internal FILO stack of the `PopupStack` and allows the FILO stack to be
|
|
469
|
+
* handled by something else.
|
|
465
470
|
*
|
|
466
471
|
* @param adapter The parts of the PopupStack that we want to override
|
|
467
472
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workday/canvas-kit-popup-stack",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0-427-next.2+2c79134a",
|
|
4
4
|
"description": "Stack for managing popup UIs to coordinate global concerns like escape key handling and rendering order",
|
|
5
5
|
"author": "Workday, Inc. (https://www.workday.com)",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"screenfull": "^5.2.0"
|
|
39
39
|
},
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "2c79134aa48ef0efde9cdf883b529b8157337a28"
|
|
41
41
|
}
|