@workday/canvas-kit-popup-stack 6.1.5 → 6.2.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/README.md +41 -0
- package/dist/commonjs/lib/PopupStack.d.ts +83 -10
- package/dist/commonjs/lib/PopupStack.d.ts.map +1 -1
- package/dist/commonjs/lib/PopupStack.js +129 -15
- package/dist/es6/lib/PopupStack.d.ts +83 -10
- package/dist/es6/lib/PopupStack.d.ts.map +1 -1
- package/dist/es6/lib/PopupStack.js +126 -15
- package/lib/PopupStack.ts +141 -16
- package/package.json +6 -3
- package/ts3.5/dist/commonjs/lib/PopupStack.d.ts +83 -10
- package/ts3.5/dist/es6/lib/PopupStack.d.ts +83 -10
|
@@ -5,6 +5,7 @@ var __spreadArrays = (this && this.__spreadArrays) || function () {
|
|
|
5
5
|
r[k] = a[j];
|
|
6
6
|
return r;
|
|
7
7
|
};
|
|
8
|
+
import screenfull from 'screenfull';
|
|
8
9
|
function getLast(items) {
|
|
9
10
|
if (items.length) {
|
|
10
11
|
return items[items.length - 1];
|
|
@@ -136,12 +137,19 @@ var setToWindow = function (path, value) {
|
|
|
136
137
|
// defined on the page, we need to use that one. Never, ever, ever change this variable name on
|
|
137
138
|
// window
|
|
138
139
|
var stack = getFromWindow('workday.__popupStack') || {
|
|
139
|
-
description: 'Global popup stack from @workday/canvas-kit
|
|
140
|
+
description: 'Global popup stack from @workday/canvas-kit/popup-stack',
|
|
141
|
+
container: function () { return document.body; },
|
|
140
142
|
items: [],
|
|
141
143
|
zIndex: { min: 30, max: 50, getValue: getValue },
|
|
142
144
|
_adapter: {},
|
|
143
145
|
};
|
|
144
146
|
setToWindow('workday.__popupStack', stack);
|
|
147
|
+
var stacks = getFromWindow('workday.__popupStackOfStacks') || [stack];
|
|
148
|
+
stacks.description = 'Global stack of popup stacks from @workday/canvas-kit/popup-stack';
|
|
149
|
+
setToWindow('workday.__popupStackOfStacks', stacks);
|
|
150
|
+
function getTopStack() {
|
|
151
|
+
return stacks[stacks.length - 1];
|
|
152
|
+
}
|
|
145
153
|
export var PopupStack = {
|
|
146
154
|
/**
|
|
147
155
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -151,6 +159,7 @@ export var PopupStack = {
|
|
|
151
159
|
*/
|
|
152
160
|
createContainer: function () {
|
|
153
161
|
var _a;
|
|
162
|
+
var stack = getTopStack();
|
|
154
163
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.createContainer) {
|
|
155
164
|
return stack._adapter.createContainer();
|
|
156
165
|
}
|
|
@@ -165,30 +174,36 @@ export var PopupStack = {
|
|
|
165
174
|
* method when the event triggers.
|
|
166
175
|
*/
|
|
167
176
|
add: function (item) {
|
|
168
|
-
var _a;
|
|
177
|
+
var _a, _b;
|
|
178
|
+
var stack = getTopStack();
|
|
169
179
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.add) {
|
|
170
180
|
stack._adapter.add(item);
|
|
171
181
|
return;
|
|
172
182
|
}
|
|
173
183
|
stack.items.push(item);
|
|
174
|
-
document.body.appendChild(item.element);
|
|
184
|
+
(((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) || document.body).appendChild(item.element);
|
|
175
185
|
setZIndexOfElements(PopupStack.getElements());
|
|
176
186
|
},
|
|
177
187
|
/**
|
|
178
|
-
* Removes an item from
|
|
179
|
-
*
|
|
180
|
-
* cleanup.
|
|
181
|
-
* reset z-index values of the
|
|
188
|
+
* Removes an item from a stack by its `HTMLElement` reference. This should be called when a popup
|
|
189
|
+
* is "closed" or when the element is removed from the page entirely to ensure proper memory
|
|
190
|
+
* cleanup. A popup will be removed from the stack it is a part of. This will not automatically be
|
|
191
|
+
* called when the element is removed from the DOM. This method will reset z-index values of the
|
|
192
|
+
* stack.
|
|
182
193
|
*/
|
|
183
194
|
remove: function (element) {
|
|
184
|
-
var _a;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
var _a, _b;
|
|
196
|
+
// Find the stack the popup belongs to.
|
|
197
|
+
var stack = find(stacks, function (stack) { return !!find(stack.items, function (item) { return item.element === element; }); });
|
|
198
|
+
if (stack) {
|
|
199
|
+
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.remove) {
|
|
200
|
+
stack._adapter.remove(element);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
stack.items = stack.items.filter(function (item) { return item.element !== element; });
|
|
204
|
+
(((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) || document.body).removeChild(element);
|
|
205
|
+
setZIndexOfElements(PopupStack.getElements(stack));
|
|
188
206
|
}
|
|
189
|
-
stack.items = stack.items.filter(function (item) { return item.element !== element; });
|
|
190
|
-
document.body.removeChild(element);
|
|
191
|
-
setZIndexOfElements(PopupStack.getElements());
|
|
192
207
|
},
|
|
193
208
|
/**
|
|
194
209
|
* Returns true when the provided `element` is at the top of the stack. It will return false if it
|
|
@@ -197,6 +212,7 @@ export var PopupStack = {
|
|
|
197
212
|
*/
|
|
198
213
|
isTopmost: function (element) {
|
|
199
214
|
var _a;
|
|
215
|
+
var stack = getTopStack();
|
|
200
216
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.isTopmost) {
|
|
201
217
|
return stack._adapter.isTopmost(element);
|
|
202
218
|
}
|
|
@@ -211,8 +227,9 @@ export var PopupStack = {
|
|
|
211
227
|
* elements in the order of lowest z-index to highest z-index. Some popup behaviors will need to
|
|
212
228
|
* make decisions based on z-index order.
|
|
213
229
|
*/
|
|
214
|
-
getElements: function () {
|
|
230
|
+
getElements: function (stackOverride) {
|
|
215
231
|
var _a;
|
|
232
|
+
var stack = stackOverride || getTopStack();
|
|
216
233
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.getElements) {
|
|
217
234
|
return stack._adapter.getElements();
|
|
218
235
|
}
|
|
@@ -232,6 +249,7 @@ export var PopupStack = {
|
|
|
232
249
|
*/
|
|
233
250
|
bringToTop: function (element) {
|
|
234
251
|
var _a;
|
|
252
|
+
var stack = getTopStack();
|
|
235
253
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.bringToTop) {
|
|
236
254
|
stack._adapter.bringToTop(element);
|
|
237
255
|
return;
|
|
@@ -269,6 +287,7 @@ export var PopupStack = {
|
|
|
269
287
|
*/
|
|
270
288
|
contains: function (element, eventTarget) {
|
|
271
289
|
var _a, _b;
|
|
290
|
+
var stack = getTopStack();
|
|
272
291
|
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.contains) {
|
|
273
292
|
return stack._adapter.contains(element, eventTarget);
|
|
274
293
|
}
|
|
@@ -286,6 +305,79 @@ export var PopupStack = {
|
|
|
286
305
|
}
|
|
287
306
|
return false;
|
|
288
307
|
},
|
|
308
|
+
/**
|
|
309
|
+
* Add a new stack context for popups. This method could be called with the same element multiple
|
|
310
|
+
* times, but should only push a new stack context once. The most common use-case for calling
|
|
311
|
+
* `pushStackContext` is when entering fullscreen, but multiple fullscreen listeners could be
|
|
312
|
+
* pushing the same element which is very difficult to ensure only one stack is used. To mitigate,
|
|
313
|
+
* this method filters out multiple calls to push the same element as a new stack context.
|
|
314
|
+
*/
|
|
315
|
+
pushStackContext: function (container) {
|
|
316
|
+
var _a, _b;
|
|
317
|
+
var stack = getTopStack();
|
|
318
|
+
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.pushStackContext) {
|
|
319
|
+
return stack._adapter.pushStackContext(container);
|
|
320
|
+
}
|
|
321
|
+
// Don't push if the container already exists. This removes duplicates
|
|
322
|
+
if (((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) === container) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
var newStack = {
|
|
326
|
+
items: [],
|
|
327
|
+
zIndex: stack.zIndex,
|
|
328
|
+
container: function () { return container; },
|
|
329
|
+
_adapter: {},
|
|
330
|
+
};
|
|
331
|
+
stacks.push(newStack);
|
|
332
|
+
},
|
|
333
|
+
/**
|
|
334
|
+
* Remove the topmost stack context. The stack context will only be removed if the top stack
|
|
335
|
+
* context container element matches to guard against accidental remove of other stack contexts
|
|
336
|
+
* you don't own.
|
|
337
|
+
*/
|
|
338
|
+
popStackContext: function (container) {
|
|
339
|
+
var _a, _b;
|
|
340
|
+
var stack = getTopStack();
|
|
341
|
+
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.popStackContext) {
|
|
342
|
+
return stack._adapter.popStackContext(container);
|
|
343
|
+
}
|
|
344
|
+
if (((_b = stack.container) === null || _b === void 0 ? void 0 : _b.call(stack)) === container && stacks.length > 1) {
|
|
345
|
+
stacks.pop();
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
/**
|
|
349
|
+
* Transfer the popup stack item into the current popup stack context.
|
|
350
|
+
*
|
|
351
|
+
* An example might be a popup
|
|
352
|
+
* that is opened and an element goes into fullscreen. The default popup stack context is
|
|
353
|
+
* `document.body`, but the [Fullscreen
|
|
354
|
+
* API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API) will only render elements
|
|
355
|
+
* that are children of the fullscreen element. If the popup isn't transferred to the current
|
|
356
|
+
* popup stack context, the popup will remain open, but will no longer be rendered. This method
|
|
357
|
+
* will transfer that popup to the fullscreen element so that it will render. Popups created while
|
|
358
|
+
* in a fullscreen context that need to be transferred back when fullscreen is exited should also
|
|
359
|
+
* call this method. While popups may still render when fullscreen is exited, popups will be
|
|
360
|
+
* members of different popup stack contexts which will cause unspecified results (like the escape
|
|
361
|
+
* key will choose the wrong popup as the "topmost").
|
|
362
|
+
*/
|
|
363
|
+
transferToCurrentContext: function (item) {
|
|
364
|
+
var _a;
|
|
365
|
+
var stack = getTopStack();
|
|
366
|
+
if ((_a = stack._adapter) === null || _a === void 0 ? void 0 : _a.transferToCurrentContext) {
|
|
367
|
+
return stack._adapter.transferToCurrentContext(item);
|
|
368
|
+
}
|
|
369
|
+
if (find(stack.items, function (i) { return i.element === item.element; })) {
|
|
370
|
+
// The element is already in the stack, don't do anything
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// Try to find the element in existing stacks. If it exists, we need to first remove from that
|
|
374
|
+
// stack context
|
|
375
|
+
var oldStack = find(stacks, function (stack) { return !!find(stack.items, function (i) { return i.element === item.element; }); });
|
|
376
|
+
if (oldStack) {
|
|
377
|
+
PopupStack.remove(item.element);
|
|
378
|
+
}
|
|
379
|
+
PopupStack.add(item);
|
|
380
|
+
},
|
|
289
381
|
};
|
|
290
382
|
/**
|
|
291
383
|
* Reset all the items in the stack. This should only be used for testing or if the page doesn't
|
|
@@ -301,3 +393,22 @@ export function resetStack() {
|
|
|
301
393
|
export var createAdapter = function (adapter) {
|
|
302
394
|
stack._adapter = adapter;
|
|
303
395
|
};
|
|
396
|
+
// keep track of the element ourselves to avoid accidentally popping off someone else's stack
|
|
397
|
+
// context
|
|
398
|
+
var element = null;
|
|
399
|
+
// Where should this go? Each version of `PopupStack` on a page will add a listener. The
|
|
400
|
+
// `PopupStack` should guard against multiple handlers like this simultaneously and there is no
|
|
401
|
+
// lifecycle here.
|
|
402
|
+
if (screenfull.isEnabled) {
|
|
403
|
+
screenfull.on('change', function () {
|
|
404
|
+
if (screenfull.isFullscreen) {
|
|
405
|
+
if (screenfull.element) {
|
|
406
|
+
element = screenfull.element;
|
|
407
|
+
PopupStack.pushStackContext(element);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else if (element) {
|
|
411
|
+
PopupStack.popStackContext(element);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
}
|
package/lib/PopupStack.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import screenfull from 'screenfull';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* This type is purposely an interface so that it can be extended for a specific use-case.
|
|
3
5
|
*/
|
|
@@ -106,6 +108,7 @@ function getChildPopups(item: PopupStackItem, items: PopupStackItem[]): PopupSta
|
|
|
106
108
|
|
|
107
109
|
interface Stack {
|
|
108
110
|
items: PopupStackItem[];
|
|
111
|
+
container?: () => HTMLElement;
|
|
109
112
|
zIndex: {
|
|
110
113
|
min: number;
|
|
111
114
|
max: number;
|
|
@@ -183,13 +186,23 @@ const setToWindow = (path: string, value: any) => {
|
|
|
183
186
|
// defined on the page, we need to use that one. Never, ever, ever change this variable name on
|
|
184
187
|
// window
|
|
185
188
|
const stack: Stack = getFromWindow('workday.__popupStack') || {
|
|
186
|
-
description: 'Global popup stack from @workday/canvas-kit
|
|
187
|
-
|
|
189
|
+
description: 'Global popup stack from @workday/canvas-kit/popup-stack',
|
|
190
|
+
container: () => document.body,
|
|
191
|
+
items: [],
|
|
188
192
|
zIndex: {min: 30, max: 50, getValue: getValue},
|
|
189
|
-
_adapter: {}
|
|
193
|
+
_adapter: {},
|
|
190
194
|
};
|
|
191
195
|
setToWindow('workday.__popupStack', stack);
|
|
192
196
|
|
|
197
|
+
const stacks: Stack[] = getFromWindow('workday.__popupStackOfStacks') || [stack];
|
|
198
|
+
|
|
199
|
+
(stacks as any).description = 'Global stack of popup stacks from @workday/canvas-kit/popup-stack';
|
|
200
|
+
setToWindow('workday.__popupStackOfStacks', stacks);
|
|
201
|
+
|
|
202
|
+
function getTopStack() {
|
|
203
|
+
return stacks[stacks.length - 1];
|
|
204
|
+
}
|
|
205
|
+
|
|
193
206
|
export const PopupStack = {
|
|
194
207
|
/**
|
|
195
208
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -198,6 +211,7 @@ export const PopupStack = {
|
|
|
198
211
|
* should be added to this element.
|
|
199
212
|
*/
|
|
200
213
|
createContainer(): HTMLElement {
|
|
214
|
+
const stack = getTopStack();
|
|
201
215
|
if (stack._adapter?.createContainer) {
|
|
202
216
|
return stack._adapter.createContainer();
|
|
203
217
|
}
|
|
@@ -212,31 +226,37 @@ export const PopupStack = {
|
|
|
212
226
|
* method when the event triggers.
|
|
213
227
|
*/
|
|
214
228
|
add(item: PopupStackItem): void {
|
|
229
|
+
const stack = getTopStack();
|
|
215
230
|
if (stack._adapter?.add) {
|
|
216
231
|
stack._adapter.add(item);
|
|
217
232
|
return;
|
|
218
233
|
}
|
|
219
234
|
stack.items.push(item);
|
|
220
|
-
document.body.appendChild(item.element);
|
|
235
|
+
(stack.container?.() || document.body).appendChild(item.element);
|
|
221
236
|
|
|
222
237
|
setZIndexOfElements(PopupStack.getElements());
|
|
223
238
|
},
|
|
224
239
|
|
|
225
240
|
/**
|
|
226
|
-
* Removes an item from
|
|
227
|
-
*
|
|
228
|
-
* cleanup.
|
|
229
|
-
* reset z-index values of the
|
|
241
|
+
* Removes an item from a stack by its `HTMLElement` reference. This should be called when a popup
|
|
242
|
+
* is "closed" or when the element is removed from the page entirely to ensure proper memory
|
|
243
|
+
* cleanup. A popup will be removed from the stack it is a part of. This will not automatically be
|
|
244
|
+
* called when the element is removed from the DOM. This method will reset z-index values of the
|
|
245
|
+
* stack.
|
|
230
246
|
*/
|
|
231
247
|
remove(element: HTMLElement): void {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
248
|
+
// Find the stack the popup belongs to.
|
|
249
|
+
const stack = find(stacks, stack => !!find(stack.items, item => item.element === element));
|
|
250
|
+
if (stack) {
|
|
251
|
+
if (stack._adapter?.remove) {
|
|
252
|
+
stack._adapter.remove(element);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
stack.items = stack.items.filter(item => item.element !== element);
|
|
256
|
+
(stack.container?.() || document.body).removeChild(element);
|
|
257
|
+
|
|
258
|
+
setZIndexOfElements(PopupStack.getElements(stack));
|
|
235
259
|
}
|
|
236
|
-
stack.items = stack.items.filter(item => item.element !== element);
|
|
237
|
-
document.body.removeChild(element);
|
|
238
|
-
|
|
239
|
-
setZIndexOfElements(PopupStack.getElements());
|
|
240
260
|
},
|
|
241
261
|
|
|
242
262
|
/**
|
|
@@ -245,6 +265,7 @@ export const PopupStack = {
|
|
|
245
265
|
* reference that was passed to `add`
|
|
246
266
|
*/
|
|
247
267
|
isTopmost(element: HTMLElement): boolean {
|
|
268
|
+
const stack = getTopStack();
|
|
248
269
|
if (stack._adapter?.isTopmost) {
|
|
249
270
|
return stack._adapter.isTopmost(element);
|
|
250
271
|
}
|
|
@@ -261,7 +282,8 @@ export const PopupStack = {
|
|
|
261
282
|
* elements in the order of lowest z-index to highest z-index. Some popup behaviors will need to
|
|
262
283
|
* make decisions based on z-index order.
|
|
263
284
|
*/
|
|
264
|
-
getElements(): HTMLElement[] {
|
|
285
|
+
getElements(stackOverride?: Stack): HTMLElement[] {
|
|
286
|
+
const stack = stackOverride || getTopStack();
|
|
265
287
|
if (stack._adapter?.getElements) {
|
|
266
288
|
return stack._adapter.getElements();
|
|
267
289
|
}
|
|
@@ -281,6 +303,7 @@ export const PopupStack = {
|
|
|
281
303
|
* the top of the stack.
|
|
282
304
|
*/
|
|
283
305
|
bringToTop(element: HTMLElement): void {
|
|
306
|
+
const stack = getTopStack();
|
|
284
307
|
if (stack._adapter?.bringToTop) {
|
|
285
308
|
stack._adapter.bringToTop(element);
|
|
286
309
|
return;
|
|
@@ -320,6 +343,7 @@ export const PopupStack = {
|
|
|
320
343
|
* is not inside `element`).
|
|
321
344
|
*/
|
|
322
345
|
contains(element: HTMLElement, eventTarget: HTMLElement): boolean {
|
|
346
|
+
const stack = getTopStack();
|
|
323
347
|
if (stack._adapter?.contains) {
|
|
324
348
|
return stack._adapter.contains(element, eventTarget);
|
|
325
349
|
}
|
|
@@ -344,6 +368,87 @@ export const PopupStack = {
|
|
|
344
368
|
}
|
|
345
369
|
return false;
|
|
346
370
|
},
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Add a new stack context for popups. This method could be called with the same element multiple
|
|
374
|
+
* times, but should only push a new stack context once. The most common use-case for calling
|
|
375
|
+
* `pushStackContext` is when entering fullscreen, but multiple fullscreen listeners could be
|
|
376
|
+
* pushing the same element which is very difficult to ensure only one stack is used. To mitigate,
|
|
377
|
+
* this method filters out multiple calls to push the same element as a new stack context.
|
|
378
|
+
*/
|
|
379
|
+
pushStackContext(container: HTMLElement): void {
|
|
380
|
+
const stack = getTopStack();
|
|
381
|
+
|
|
382
|
+
if (stack._adapter?.pushStackContext) {
|
|
383
|
+
return stack._adapter.pushStackContext(container);
|
|
384
|
+
}
|
|
385
|
+
// Don't push if the container already exists. This removes duplicates
|
|
386
|
+
if (stack.container?.() === container) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const newStack: Stack = {
|
|
391
|
+
items: [],
|
|
392
|
+
zIndex: stack.zIndex,
|
|
393
|
+
container: () => container,
|
|
394
|
+
_adapter: {},
|
|
395
|
+
};
|
|
396
|
+
stacks.push(newStack);
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Remove the topmost stack context. The stack context will only be removed if the top stack
|
|
401
|
+
* context container element matches to guard against accidental remove of other stack contexts
|
|
402
|
+
* you don't own.
|
|
403
|
+
*/
|
|
404
|
+
popStackContext(container: HTMLElement): void {
|
|
405
|
+
const stack = getTopStack();
|
|
406
|
+
|
|
407
|
+
if (stack._adapter?.popStackContext) {
|
|
408
|
+
return stack._adapter.popStackContext(container);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (stack.container?.() === container && stacks.length > 1) {
|
|
412
|
+
stacks.pop();
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Transfer the popup stack item into the current popup stack context.
|
|
418
|
+
*
|
|
419
|
+
* An example might be a popup
|
|
420
|
+
* that is opened and an element goes into fullscreen. The default popup stack context is
|
|
421
|
+
* `document.body`, but the [Fullscreen
|
|
422
|
+
* API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API) will only render elements
|
|
423
|
+
* that are children of the fullscreen element. If the popup isn't transferred to the current
|
|
424
|
+
* popup stack context, the popup will remain open, but will no longer be rendered. This method
|
|
425
|
+
* will transfer that popup to the fullscreen element so that it will render. Popups created while
|
|
426
|
+
* in a fullscreen context that need to be transferred back when fullscreen is exited should also
|
|
427
|
+
* call this method. While popups may still render when fullscreen is exited, popups will be
|
|
428
|
+
* members of different popup stack contexts which will cause unspecified results (like the escape
|
|
429
|
+
* key will choose the wrong popup as the "topmost").
|
|
430
|
+
*/
|
|
431
|
+
transferToCurrentContext(item: PopupStackItem): void {
|
|
432
|
+
const stack = getTopStack();
|
|
433
|
+
|
|
434
|
+
if (stack._adapter?.transferToCurrentContext) {
|
|
435
|
+
return stack._adapter.transferToCurrentContext(item);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (find(stack.items, i => i.element === item.element)) {
|
|
439
|
+
// The element is already in the stack, don't do anything
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Try to find the element in existing stacks. If it exists, we need to first remove from that
|
|
444
|
+
// stack context
|
|
445
|
+
const oldStack = find(stacks, stack => !!find(stack.items, i => i.element === item.element));
|
|
446
|
+
if (oldStack) {
|
|
447
|
+
PopupStack.remove(item.element);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
PopupStack.add(item);
|
|
451
|
+
},
|
|
347
452
|
};
|
|
348
453
|
|
|
349
454
|
/**
|
|
@@ -361,3 +466,23 @@ export function resetStack() {
|
|
|
361
466
|
export const createAdapter = (adapter: Partial<typeof PopupStack>) => {
|
|
362
467
|
stack._adapter = adapter;
|
|
363
468
|
};
|
|
469
|
+
|
|
470
|
+
// keep track of the element ourselves to avoid accidentally popping off someone else's stack
|
|
471
|
+
// context
|
|
472
|
+
let element: HTMLElement | null = null;
|
|
473
|
+
|
|
474
|
+
// Where should this go? Each version of `PopupStack` on a page will add a listener. The
|
|
475
|
+
// `PopupStack` should guard against multiple handlers like this simultaneously and there is no
|
|
476
|
+
// lifecycle here.
|
|
477
|
+
if (screenfull.isEnabled) {
|
|
478
|
+
screenfull.on('change', () => {
|
|
479
|
+
if (screenfull.isFullscreen) {
|
|
480
|
+
if (screenfull.element) {
|
|
481
|
+
element = screenfull.element as HTMLElement;
|
|
482
|
+
PopupStack.pushStackContext(element);
|
|
483
|
+
}
|
|
484
|
+
} else if (element) {
|
|
485
|
+
PopupStack.popStackContext(element);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workday/canvas-kit-popup-stack",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
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",
|
|
7
7
|
"main": "dist/commonjs/index.js",
|
|
8
8
|
"module": "dist/es6/index.js",
|
|
9
|
-
"sideEffects":
|
|
9
|
+
"sideEffects": true,
|
|
10
10
|
"types": "dist/es6/index.d.ts",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -42,5 +42,8 @@
|
|
|
42
42
|
"workday",
|
|
43
43
|
"popup-stack"
|
|
44
44
|
],
|
|
45
|
-
"
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"screenfull": "^5.2.0"
|
|
47
|
+
},
|
|
48
|
+
"gitHead": "5eaacab495cdfeacb8dbbaa12782a36382ecc813"
|
|
46
49
|
}
|
|
@@ -32,6 +32,16 @@ export interface PopupStackItem {
|
|
|
32
32
|
* popups.
|
|
33
33
|
*/
|
|
34
34
|
export declare function getValue(index: number, length: number): number;
|
|
35
|
+
interface Stack {
|
|
36
|
+
items: PopupStackItem[];
|
|
37
|
+
container?: () => HTMLElement;
|
|
38
|
+
zIndex: {
|
|
39
|
+
min: number;
|
|
40
|
+
max: number;
|
|
41
|
+
getValue: typeof getValue;
|
|
42
|
+
};
|
|
43
|
+
_adapter: Partial<typeof PopupStack>;
|
|
44
|
+
}
|
|
35
45
|
export declare const PopupStack: {
|
|
36
46
|
/**
|
|
37
47
|
* Create a HTMLElement as the container for the popup stack item. The returned element reference
|
|
@@ -48,10 +58,11 @@ export declare const PopupStack: {
|
|
|
48
58
|
*/
|
|
49
59
|
add(item: PopupStackItem): void;
|
|
50
60
|
/**
|
|
51
|
-
* Removes an item from
|
|
52
|
-
*
|
|
53
|
-
* cleanup.
|
|
54
|
-
* reset z-index values of the
|
|
61
|
+
* Removes an item from a stack by its `HTMLElement` reference. This should be called when a popup
|
|
62
|
+
* is "closed" or when the element is removed from the page entirely to ensure proper memory
|
|
63
|
+
* cleanup. A popup will be removed from the stack it is a part of. This will not automatically be
|
|
64
|
+
* called when the element is removed from the DOM. This method will reset z-index values of the
|
|
65
|
+
* stack.
|
|
55
66
|
*/
|
|
56
67
|
remove(element: HTMLElement): void;
|
|
57
68
|
/**
|
|
@@ -65,7 +76,7 @@ export declare const PopupStack: {
|
|
|
65
76
|
* elements in the order of lowest z-index to highest z-index. Some popup behaviors will need to
|
|
66
77
|
* make decisions based on z-index order.
|
|
67
78
|
*/
|
|
68
|
-
getElements(): HTMLElement[];
|
|
79
|
+
getElements(stackOverride?: Stack | undefined): HTMLElement[];
|
|
69
80
|
/**
|
|
70
81
|
* Bring the element to the top of the stack. This is useful for persistent popups to place them
|
|
71
82
|
* on top of the stack when clicked. If an `owner` was provided to an item when it was added and
|
|
@@ -91,6 +102,36 @@ export declare const PopupStack: {
|
|
|
91
102
|
* is not inside `element`).
|
|
92
103
|
*/
|
|
93
104
|
contains(element: HTMLElement, eventTarget: HTMLElement): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Add a new stack context for popups. This method could be called with the same element multiple
|
|
107
|
+
* times, but should only push a new stack context once. The most common use-case for calling
|
|
108
|
+
* `pushStackContext` is when entering fullscreen, but multiple fullscreen listeners could be
|
|
109
|
+
* pushing the same element which is very difficult to ensure only one stack is used. To mitigate,
|
|
110
|
+
* this method filters out multiple calls to push the same element as a new stack context.
|
|
111
|
+
*/
|
|
112
|
+
pushStackContext(container: HTMLElement): void;
|
|
113
|
+
/**
|
|
114
|
+
* Remove the topmost stack context. The stack context will only be removed if the top stack
|
|
115
|
+
* context container element matches to guard against accidental remove of other stack contexts
|
|
116
|
+
* you don't own.
|
|
117
|
+
*/
|
|
118
|
+
popStackContext(container: HTMLElement): void;
|
|
119
|
+
/**
|
|
120
|
+
* Transfer the popup stack item into the current popup stack context.
|
|
121
|
+
*
|
|
122
|
+
* An example might be a popup
|
|
123
|
+
* that is opened and an element goes into fullscreen. The default popup stack context is
|
|
124
|
+
* `document.body`, but the [Fullscreen
|
|
125
|
+
* API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API) will only render elements
|
|
126
|
+
* that are children of the fullscreen element. If the popup isn't transferred to the current
|
|
127
|
+
* popup stack context, the popup will remain open, but will no longer be rendered. This method
|
|
128
|
+
* will transfer that popup to the fullscreen element so that it will render. Popups created while
|
|
129
|
+
* in a fullscreen context that need to be transferred back when fullscreen is exited should also
|
|
130
|
+
* call this method. While popups may still render when fullscreen is exited, popups will be
|
|
131
|
+
* members of different popup stack contexts which will cause unspecified results (like the escape
|
|
132
|
+
* key will choose the wrong popup as the "topmost").
|
|
133
|
+
*/
|
|
134
|
+
transferToCurrentContext(item: PopupStackItem): void;
|
|
94
135
|
};
|
|
95
136
|
/**
|
|
96
137
|
* Reset all the items in the stack. This should only be used for testing or if the page doesn't
|
|
@@ -117,10 +158,11 @@ export declare const createAdapter: (adapter: Partial<{
|
|
|
117
158
|
*/
|
|
118
159
|
add(item: PopupStackItem): void;
|
|
119
160
|
/**
|
|
120
|
-
* Removes an item from
|
|
121
|
-
*
|
|
122
|
-
* cleanup.
|
|
123
|
-
* reset z-index values of the
|
|
161
|
+
* Removes an item from a stack by its `HTMLElement` reference. This should be called when a popup
|
|
162
|
+
* is "closed" or when the element is removed from the page entirely to ensure proper memory
|
|
163
|
+
* cleanup. A popup will be removed from the stack it is a part of. This will not automatically be
|
|
164
|
+
* called when the element is removed from the DOM. This method will reset z-index values of the
|
|
165
|
+
* stack.
|
|
124
166
|
*/
|
|
125
167
|
remove(element: HTMLElement): void;
|
|
126
168
|
/**
|
|
@@ -134,7 +176,7 @@ export declare const createAdapter: (adapter: Partial<{
|
|
|
134
176
|
* elements in the order of lowest z-index to highest z-index. Some popup behaviors will need to
|
|
135
177
|
* make decisions based on z-index order.
|
|
136
178
|
*/
|
|
137
|
-
getElements(): HTMLElement[];
|
|
179
|
+
getElements(stackOverride?: Stack | undefined): HTMLElement[];
|
|
138
180
|
/**
|
|
139
181
|
* Bring the element to the top of the stack. This is useful for persistent popups to place them
|
|
140
182
|
* on top of the stack when clicked. If an `owner` was provided to an item when it was added and
|
|
@@ -160,5 +202,36 @@ export declare const createAdapter: (adapter: Partial<{
|
|
|
160
202
|
* is not inside `element`).
|
|
161
203
|
*/
|
|
162
204
|
contains(element: HTMLElement, eventTarget: HTMLElement): boolean;
|
|
205
|
+
/**
|
|
206
|
+
* Add a new stack context for popups. This method could be called with the same element multiple
|
|
207
|
+
* times, but should only push a new stack context once. The most common use-case for calling
|
|
208
|
+
* `pushStackContext` is when entering fullscreen, but multiple fullscreen listeners could be
|
|
209
|
+
* pushing the same element which is very difficult to ensure only one stack is used. To mitigate,
|
|
210
|
+
* this method filters out multiple calls to push the same element as a new stack context.
|
|
211
|
+
*/
|
|
212
|
+
pushStackContext(container: HTMLElement): void;
|
|
213
|
+
/**
|
|
214
|
+
* Remove the topmost stack context. The stack context will only be removed if the top stack
|
|
215
|
+
* context container element matches to guard against accidental remove of other stack contexts
|
|
216
|
+
* you don't own.
|
|
217
|
+
*/
|
|
218
|
+
popStackContext(container: HTMLElement): void;
|
|
219
|
+
/**
|
|
220
|
+
* Transfer the popup stack item into the current popup stack context.
|
|
221
|
+
*
|
|
222
|
+
* An example might be a popup
|
|
223
|
+
* that is opened and an element goes into fullscreen. The default popup stack context is
|
|
224
|
+
* `document.body`, but the [Fullscreen
|
|
225
|
+
* API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API) will only render elements
|
|
226
|
+
* that are children of the fullscreen element. If the popup isn't transferred to the current
|
|
227
|
+
* popup stack context, the popup will remain open, but will no longer be rendered. This method
|
|
228
|
+
* will transfer that popup to the fullscreen element so that it will render. Popups created while
|
|
229
|
+
* in a fullscreen context that need to be transferred back when fullscreen is exited should also
|
|
230
|
+
* call this method. While popups may still render when fullscreen is exited, popups will be
|
|
231
|
+
* members of different popup stack contexts which will cause unspecified results (like the escape
|
|
232
|
+
* key will choose the wrong popup as the "topmost").
|
|
233
|
+
*/
|
|
234
|
+
transferToCurrentContext(item: PopupStackItem): void;
|
|
163
235
|
}>) => void;
|
|
236
|
+
export {};
|
|
164
237
|
//# sourceMappingURL=PopupStack.d.ts.map
|