@atlaskit/top-layer 4.2.0 → 4.2.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/CHANGELOG.md +16 -0
- package/dist/cjs/dialog/dialog-content.js +0 -1
- package/dist/cjs/internal/get-aria-for-trigger.js +2 -3
- package/dist/cjs/internal/use-animated-visibility.js +42 -72
- package/dist/cjs/popover/popover.js +0 -1
- package/dist/es2019/dialog/dialog-content.js +0 -1
- package/dist/es2019/internal/get-aria-for-trigger.js +2 -3
- package/dist/es2019/internal/use-animated-visibility.js +34 -65
- package/dist/es2019/popover/popover.js +0 -1
- package/dist/esm/dialog/dialog-content.js +0 -1
- package/dist/esm/internal/get-aria-for-trigger.js +2 -3
- package/dist/esm/internal/use-animated-visibility.js +42 -72
- package/dist/esm/popover/popover.js +0 -1
- package/dist/types/internal/get-aria-for-trigger.d.ts +2 -3
- package/dist/types/internal/use-animated-visibility.d.ts +2 -7
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @atlaskit/top-layer
|
|
2
2
|
|
|
3
|
+
## 4.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`c3cae446aea72`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c3cae446aea72) -
|
|
8
|
+
Only set `aria-controls` while popup content is mounted and clarify the top-layer trigger helper
|
|
9
|
+
documentation.
|
|
10
|
+
|
|
11
|
+
## 4.2.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`b348503c8c9cb`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b348503c8c9cb) -
|
|
16
|
+
Use the Web Animations API to wait for all active top-layer animations before completing
|
|
17
|
+
visibility callbacks.
|
|
18
|
+
|
|
3
19
|
## 4.2.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -87,7 +87,6 @@ var Dialog = exports.Dialog = /*#__PURE__*/(0, _react.forwardRef)(function Dialo
|
|
|
87
87
|
var closeReasonRef = (0, _react.useRef)(null);
|
|
88
88
|
var _useAnimatedVisibilit = (0, _useAnimatedVisibility.useAnimatedVisibility)({
|
|
89
89
|
isOpen: isOpen,
|
|
90
|
-
animationKind: 'dialog',
|
|
91
90
|
shouldAnimate: shouldAnimate,
|
|
92
91
|
elementRef: ownRef,
|
|
93
92
|
onEnterFinish: onEnterFinish,
|
|
@@ -50,9 +50,8 @@ var roleToAriaHasPopup = {
|
|
|
50
50
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
51
51
|
* the value is `undefined`.
|
|
52
52
|
* - `aria-expanded` - reflects the current open state.
|
|
53
|
-
* - `aria-controls` - references the popover element by its id
|
|
54
|
-
* while the popover is closed
|
|
55
|
-
* carries the current visibility state.
|
|
53
|
+
* - `aria-controls` - references the popover element by its id while the
|
|
54
|
+
* popover is open, and is omitted while the popover is closed and unmounted.
|
|
56
55
|
*
|
|
57
56
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
58
57
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -7,64 +7,39 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.useAnimatedVisibility = useAnimatedVisibility;
|
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
9
|
var _react = require("react");
|
|
10
|
-
var _bindEventListener = require("bind-event-listener");
|
|
11
10
|
var _reducedMotion = require("./reduced-motion");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
* Not setting these timings on the preset config because they're defined in the CSS,
|
|
16
|
-
* so configuring it in the preset doesn't achieve anything except introduce a potential
|
|
17
|
-
* source of drift.
|
|
18
|
-
*
|
|
19
|
-
* TODO: figure out a way to not need to redeclare these in the future,
|
|
20
|
-
* and keep CSS and JS timings in sync always.
|
|
21
|
-
*/
|
|
22
|
-
var safetyNetMsMap = {
|
|
23
|
-
dialog: {
|
|
24
|
-
enter: 250,
|
|
25
|
-
// Corresponds to `motion.duration.long`
|
|
26
|
-
exit: 200 // Corresponds to `motion.duration.medium`
|
|
27
|
-
},
|
|
28
|
-
popover: {
|
|
29
|
-
enter: 150,
|
|
30
|
-
// Corresponds to `motion.duration.short`
|
|
31
|
-
exit: 100 // Corresponds to `motion.duration.xshort`
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Fires `onSettled` once when `transitionend` fires on `element`, or after
|
|
37
|
-
* `safetyNetMs` if the event is swallowed (mid-transition unmount, CSS
|
|
38
|
-
* mismatch, browser quirks). Returns a cleanup that cancels both paths.
|
|
39
|
-
*/
|
|
40
|
-
function runOnTransitionEndOrTimeout(_ref) {
|
|
41
|
-
var element = _ref.element,
|
|
42
|
-
safetyNetMs = _ref.safetyNetMs,
|
|
11
|
+
function runCancellableSettlement(_ref) {
|
|
12
|
+
var schedule = _ref.schedule,
|
|
43
13
|
onSettled = _ref.onSettled;
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function teardown() {
|
|
48
|
-
state.isSettled = true;
|
|
49
|
-
clearTimeout(safetyNetId);
|
|
50
|
-
unbind();
|
|
51
|
-
}
|
|
52
|
-
function settle() {
|
|
53
|
-
if (state.isSettled) {
|
|
14
|
+
var isCancelled = false;
|
|
15
|
+
schedule(function () {
|
|
16
|
+
if (isCancelled) {
|
|
54
17
|
return;
|
|
55
18
|
}
|
|
56
|
-
teardown();
|
|
57
19
|
onSettled();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
20
|
+
});
|
|
21
|
+
return function () {
|
|
22
|
+
isCancelled = true;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fires `onSettled` once all animations currently applied to `element`
|
|
28
|
+
* finish or are cancelled. Returns a cleanup that prevents stale settlement.
|
|
29
|
+
*/
|
|
30
|
+
function runOnAnimationsSettled(_ref2) {
|
|
31
|
+
var element = _ref2.element,
|
|
32
|
+
onSettled = _ref2.onSettled;
|
|
33
|
+
return runCancellableSettlement({
|
|
34
|
+
onSettled: onSettled,
|
|
35
|
+
schedule: function schedule(settle) {
|
|
36
|
+
var _element$getAnimation, _element$getAnimation2;
|
|
37
|
+
var animations = (_element$getAnimation = (_element$getAnimation2 = element.getAnimations) === null || _element$getAnimation2 === void 0 ? void 0 : _element$getAnimation2.call(element)) !== null && _element$getAnimation !== void 0 ? _element$getAnimation : [];
|
|
38
|
+
Promise.allSettled(animations.map(function (animation) {
|
|
39
|
+
return animation.finished;
|
|
40
|
+
})).then(settle);
|
|
64
41
|
}
|
|
65
42
|
});
|
|
66
|
-
var safetyNetId = setTimeout(settle, safetyNetMs);
|
|
67
|
-
return teardown;
|
|
68
43
|
}
|
|
69
44
|
|
|
70
45
|
/**
|
|
@@ -128,13 +103,12 @@ function runOnTransitionEndOrTimeout(_ref) {
|
|
|
128
103
|
* Every effect dispatches on the single `phase` value instead of a
|
|
129
104
|
* cross-product of booleans.
|
|
130
105
|
*/
|
|
131
|
-
function useAnimatedVisibility(
|
|
132
|
-
var isOpen =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
onExitFinish = _ref2.onExitFinish;
|
|
106
|
+
function useAnimatedVisibility(_ref3) {
|
|
107
|
+
var isOpen = _ref3.isOpen,
|
|
108
|
+
shouldAnimate = _ref3.shouldAnimate,
|
|
109
|
+
elementRef = _ref3.elementRef,
|
|
110
|
+
onEnterFinish = _ref3.onEnterFinish,
|
|
111
|
+
onExitFinish = _ref3.onExitFinish;
|
|
138
112
|
var willAnimate = shouldAnimate && !(0, _reducedMotion.prefersReducedMotion)();
|
|
139
113
|
var isNativeClosedRef = (0, _react.useRef)(false);
|
|
140
114
|
|
|
@@ -175,16 +149,14 @@ function useAnimatedVisibility(_ref2) {
|
|
|
175
149
|
setPhase(willAnimate ? 'entering' : 'open');
|
|
176
150
|
}
|
|
177
151
|
|
|
178
|
-
// Keep stable refs to the latest callbacks so effects and
|
|
152
|
+
// Keep stable refs to the latest callbacks so effects and promise callbacks
|
|
179
153
|
// always call the current callback without needing them in dependency arrays.
|
|
180
154
|
var onEnterFinishRef = (0, _react.useRef)(onEnterFinish);
|
|
181
155
|
onEnterFinishRef.current = onEnterFinish;
|
|
182
156
|
var onExitFinishRef = (0, _react.useRef)(onExitFinish);
|
|
183
157
|
onExitFinishRef.current = onExitFinish;
|
|
184
158
|
|
|
185
|
-
// Animated entry: wait for
|
|
186
|
-
// move to `open`. The fallback guarantees settle on swallowed events
|
|
187
|
-
// (zero-duration, browser quirk, mid-transition unmount).
|
|
159
|
+
// Animated entry: wait for all active animations and move to `open`.
|
|
188
160
|
(0, _react.useEffect)(function () {
|
|
189
161
|
if (phase !== 'entering') {
|
|
190
162
|
return;
|
|
@@ -193,9 +165,8 @@ function useAnimatedVisibility(_ref2) {
|
|
|
193
165
|
if (!element) {
|
|
194
166
|
return;
|
|
195
167
|
}
|
|
196
|
-
return
|
|
168
|
+
return runOnAnimationsSettled({
|
|
197
169
|
element: element,
|
|
198
|
-
safetyNetMs: safetyNetMsMap[animationKind].enter + 50,
|
|
199
170
|
onSettled: function onSettled() {
|
|
200
171
|
var _onEnterFinishRef$cur;
|
|
201
172
|
// Fire the consumer callback before transitioning phase so any
|
|
@@ -208,7 +179,7 @@ function useAnimatedVisibility(_ref2) {
|
|
|
208
179
|
});
|
|
209
180
|
}
|
|
210
181
|
});
|
|
211
|
-
}, [phase,
|
|
182
|
+
}, [phase, elementRef]);
|
|
212
183
|
|
|
213
184
|
// Non-animated entry callback. The lifecycle goes `closed → open`
|
|
214
185
|
// directly with no `entering` phase; fire `onEnterFinish` on that jump.
|
|
@@ -226,8 +197,8 @@ function useAnimatedVisibility(_ref2) {
|
|
|
226
197
|
}
|
|
227
198
|
}, [phase]);
|
|
228
199
|
|
|
229
|
-
// Animated exit: wait for
|
|
230
|
-
//
|
|
200
|
+
// Animated exit: wait for all active animations, fire `onExitFinish`,
|
|
201
|
+
// then move to `closed` to unmount the host.
|
|
231
202
|
(0, _react.useEffect)(function () {
|
|
232
203
|
if (phase !== 'exiting' || !willAnimate) {
|
|
233
204
|
return;
|
|
@@ -236,9 +207,8 @@ function useAnimatedVisibility(_ref2) {
|
|
|
236
207
|
if (!element) {
|
|
237
208
|
return;
|
|
238
209
|
}
|
|
239
|
-
return
|
|
210
|
+
return runOnAnimationsSettled({
|
|
240
211
|
element: element,
|
|
241
|
-
safetyNetMs: safetyNetMsMap[animationKind].exit + 50,
|
|
242
212
|
onSettled: function onSettled() {
|
|
243
213
|
var _onExitFinishRef$curr;
|
|
244
214
|
// Fire the consumer callback before transitioning phase to
|
|
@@ -253,7 +223,7 @@ function useAnimatedVisibility(_ref2) {
|
|
|
253
223
|
});
|
|
254
224
|
}
|
|
255
225
|
});
|
|
256
|
-
}, [phase, willAnimate,
|
|
226
|
+
}, [phase, willAnimate, elementRef]);
|
|
257
227
|
|
|
258
228
|
// Native dismissal is authoritative. Its toggle can arrive while controlled
|
|
259
229
|
// state still says open, so remember that native state and do not
|
|
@@ -283,7 +253,7 @@ function useAnimatedVisibility(_ref2) {
|
|
|
283
253
|
});
|
|
284
254
|
}, [willAnimate]);
|
|
285
255
|
|
|
286
|
-
// Non-animated exit callback.
|
|
256
|
+
// Non-animated exit callback. There are no animations to wait for, so fire
|
|
287
257
|
// `onExitFinish` on the transition into `exiting` (the unmount itself
|
|
288
258
|
// is gated by the closing toggle handshake above).
|
|
289
259
|
var prevPhaseForExitRef = (0, _react.useRef)('closed');
|
|
@@ -293,7 +263,7 @@ function useAnimatedVisibility(_ref2) {
|
|
|
293
263
|
|
|
294
264
|
// Only fire on the non-animated transition into `exiting`. The
|
|
295
265
|
// animated path's `exiting → closed` settle already fires
|
|
296
|
-
// `onExitFinish` from `
|
|
266
|
+
// `onExitFinish` from `runOnAnimationsSettled`.
|
|
297
267
|
if (prevPhase !== 'exiting' && phase === 'exiting' && !willAnimate) {
|
|
298
268
|
var _onExitFinishRef$curr2;
|
|
299
269
|
(_onExitFinishRef$curr2 = onExitFinishRef.current) === null || _onExitFinishRef$curr2 === void 0 || _onExitFinishRef$curr2.call(onExitFinishRef);
|
|
@@ -139,7 +139,6 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
139
139
|
var popoverId = idProp !== null && idProp !== void 0 ? idProp : "popover-".concat(autoId.replace(/:/g, ''));
|
|
140
140
|
var _useAnimatedVisibilit = (0, _useAnimatedVisibility.useAnimatedVisibility)({
|
|
141
141
|
isOpen: isOpen,
|
|
142
|
-
animationKind: 'popover',
|
|
143
142
|
shouldAnimate: shouldAnimate,
|
|
144
143
|
elementRef: ownRef,
|
|
145
144
|
onEnterFinish: onEnterFinish,
|
|
@@ -44,9 +44,8 @@ const roleToAriaHasPopup = {
|
|
|
44
44
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
45
45
|
* the value is `undefined`.
|
|
46
46
|
* - `aria-expanded` - reflects the current open state.
|
|
47
|
-
* - `aria-controls` - references the popover element by its id
|
|
48
|
-
* while the popover is closed
|
|
49
|
-
* carries the current visibility state.
|
|
47
|
+
* - `aria-controls` - references the popover element by its id while the
|
|
48
|
+
* popover is open, and is omitted while the popover is closed and unmounted.
|
|
50
49
|
*
|
|
51
50
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
52
51
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -1,63 +1,37 @@
|
|
|
1
1
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
-
import { bind } from 'bind-event-listener';
|
|
3
2
|
import { prefersReducedMotion } from './reduced-motion';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
popover: {
|
|
21
|
-
enter: 150,
|
|
22
|
-
// Corresponds to `motion.duration.short`
|
|
23
|
-
exit: 100 // Corresponds to `motion.duration.xshort`
|
|
24
|
-
}
|
|
25
|
-
};
|
|
3
|
+
function runCancellableSettlement({
|
|
4
|
+
schedule,
|
|
5
|
+
onSettled
|
|
6
|
+
}) {
|
|
7
|
+
let isCancelled = false;
|
|
8
|
+
schedule(() => {
|
|
9
|
+
if (isCancelled) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
onSettled();
|
|
13
|
+
});
|
|
14
|
+
return () => {
|
|
15
|
+
isCancelled = true;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
26
18
|
|
|
27
19
|
/**
|
|
28
|
-
* Fires `onSettled` once
|
|
29
|
-
*
|
|
30
|
-
* mismatch, browser quirks). Returns a cleanup that cancels both paths.
|
|
20
|
+
* Fires `onSettled` once all animations currently applied to `element`
|
|
21
|
+
* finish or are cancelled. Returns a cleanup that prevents stale settlement.
|
|
31
22
|
*/
|
|
32
|
-
function
|
|
23
|
+
function runOnAnimationsSettled({
|
|
33
24
|
element,
|
|
34
|
-
safetyNetMs,
|
|
35
25
|
onSettled
|
|
36
26
|
}) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
unbind();
|
|
44
|
-
}
|
|
45
|
-
function settle() {
|
|
46
|
-
if (state.isSettled) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
teardown();
|
|
50
|
-
onSettled();
|
|
51
|
-
}
|
|
52
|
-
const unbind = bind(element, {
|
|
53
|
-
type: 'transitionend',
|
|
54
|
-
listener: settle,
|
|
55
|
-
options: {
|
|
56
|
-
once: true
|
|
27
|
+
return runCancellableSettlement({
|
|
28
|
+
onSettled,
|
|
29
|
+
schedule: settle => {
|
|
30
|
+
var _element$getAnimation, _element$getAnimation2;
|
|
31
|
+
const animations = (_element$getAnimation = (_element$getAnimation2 = element.getAnimations) === null || _element$getAnimation2 === void 0 ? void 0 : _element$getAnimation2.call(element)) !== null && _element$getAnimation !== void 0 ? _element$getAnimation : [];
|
|
32
|
+
Promise.allSettled(animations.map(animation => animation.finished)).then(settle);
|
|
57
33
|
}
|
|
58
34
|
});
|
|
59
|
-
const safetyNetId = setTimeout(settle, safetyNetMs);
|
|
60
|
-
return teardown;
|
|
61
35
|
}
|
|
62
36
|
|
|
63
37
|
/**
|
|
@@ -123,7 +97,6 @@ function runOnTransitionEndOrTimeout({
|
|
|
123
97
|
*/
|
|
124
98
|
export function useAnimatedVisibility({
|
|
125
99
|
isOpen,
|
|
126
|
-
animationKind,
|
|
127
100
|
shouldAnimate,
|
|
128
101
|
elementRef,
|
|
129
102
|
onEnterFinish,
|
|
@@ -166,16 +139,14 @@ export function useAnimatedVisibility({
|
|
|
166
139
|
setPhase(willAnimate ? 'entering' : 'open');
|
|
167
140
|
}
|
|
168
141
|
|
|
169
|
-
// Keep stable refs to the latest callbacks so effects and
|
|
142
|
+
// Keep stable refs to the latest callbacks so effects and promise callbacks
|
|
170
143
|
// always call the current callback without needing them in dependency arrays.
|
|
171
144
|
const onEnterFinishRef = useRef(onEnterFinish);
|
|
172
145
|
onEnterFinishRef.current = onEnterFinish;
|
|
173
146
|
const onExitFinishRef = useRef(onExitFinish);
|
|
174
147
|
onExitFinishRef.current = onExitFinish;
|
|
175
148
|
|
|
176
|
-
// Animated entry: wait for
|
|
177
|
-
// move to `open`. The fallback guarantees settle on swallowed events
|
|
178
|
-
// (zero-duration, browser quirk, mid-transition unmount).
|
|
149
|
+
// Animated entry: wait for all active animations and move to `open`.
|
|
179
150
|
useEffect(() => {
|
|
180
151
|
if (phase !== 'entering') {
|
|
181
152
|
return;
|
|
@@ -184,9 +155,8 @@ export function useAnimatedVisibility({
|
|
|
184
155
|
if (!element) {
|
|
185
156
|
return;
|
|
186
157
|
}
|
|
187
|
-
return
|
|
158
|
+
return runOnAnimationsSettled({
|
|
188
159
|
element,
|
|
189
|
-
safetyNetMs: safetyNetMsMap[animationKind].enter + 50,
|
|
190
160
|
onSettled: () => {
|
|
191
161
|
var _onEnterFinishRef$cur;
|
|
192
162
|
// Fire the consumer callback before transitioning phase so any
|
|
@@ -197,7 +167,7 @@ export function useAnimatedVisibility({
|
|
|
197
167
|
setPhase(current => current === 'entering' ? 'open' : current);
|
|
198
168
|
}
|
|
199
169
|
});
|
|
200
|
-
}, [phase,
|
|
170
|
+
}, [phase, elementRef]);
|
|
201
171
|
|
|
202
172
|
// Non-animated entry callback. The lifecycle goes `closed → open`
|
|
203
173
|
// directly with no `entering` phase; fire `onEnterFinish` on that jump.
|
|
@@ -215,8 +185,8 @@ export function useAnimatedVisibility({
|
|
|
215
185
|
}
|
|
216
186
|
}, [phase]);
|
|
217
187
|
|
|
218
|
-
// Animated exit: wait for
|
|
219
|
-
//
|
|
188
|
+
// Animated exit: wait for all active animations, fire `onExitFinish`,
|
|
189
|
+
// then move to `closed` to unmount the host.
|
|
220
190
|
useEffect(() => {
|
|
221
191
|
if (phase !== 'exiting' || !willAnimate) {
|
|
222
192
|
return;
|
|
@@ -225,9 +195,8 @@ export function useAnimatedVisibility({
|
|
|
225
195
|
if (!element) {
|
|
226
196
|
return;
|
|
227
197
|
}
|
|
228
|
-
return
|
|
198
|
+
return runOnAnimationsSettled({
|
|
229
199
|
element,
|
|
230
|
-
safetyNetMs: safetyNetMsMap[animationKind].exit + 50,
|
|
231
200
|
onSettled: () => {
|
|
232
201
|
var _onExitFinishRef$curr;
|
|
233
202
|
// Fire the consumer callback before transitioning phase to
|
|
@@ -240,7 +209,7 @@ export function useAnimatedVisibility({
|
|
|
240
209
|
setPhase(current => current === 'exiting' ? 'closed' : current);
|
|
241
210
|
}
|
|
242
211
|
});
|
|
243
|
-
}, [phase, willAnimate,
|
|
212
|
+
}, [phase, willAnimate, elementRef]);
|
|
244
213
|
|
|
245
214
|
// Native dismissal is authoritative. Its toggle can arrive while controlled
|
|
246
215
|
// state still says open, so remember that native state and do not
|
|
@@ -270,7 +239,7 @@ export function useAnimatedVisibility({
|
|
|
270
239
|
});
|
|
271
240
|
}, [willAnimate]);
|
|
272
241
|
|
|
273
|
-
// Non-animated exit callback.
|
|
242
|
+
// Non-animated exit callback. There are no animations to wait for, so fire
|
|
274
243
|
// `onExitFinish` on the transition into `exiting` (the unmount itself
|
|
275
244
|
// is gated by the closing toggle handshake above).
|
|
276
245
|
const prevPhaseForExitRef = useRef('closed');
|
|
@@ -280,7 +249,7 @@ export function useAnimatedVisibility({
|
|
|
280
249
|
|
|
281
250
|
// Only fire on the non-animated transition into `exiting`. The
|
|
282
251
|
// animated path's `exiting → closed` settle already fires
|
|
283
|
-
// `onExitFinish` from `
|
|
252
|
+
// `onExitFinish` from `runOnAnimationsSettled`.
|
|
284
253
|
if (prevPhase !== 'exiting' && phase === 'exiting' && !willAnimate) {
|
|
285
254
|
var _onExitFinishRef$curr2;
|
|
286
255
|
(_onExitFinishRef$curr2 = onExitFinishRef.current) === null || _onExitFinishRef$curr2 === void 0 ? void 0 : _onExitFinishRef$curr2.call(onExitFinishRef);
|
|
@@ -78,7 +78,6 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
78
78
|
var closeReasonRef = useRef(null);
|
|
79
79
|
var _useAnimatedVisibilit = useAnimatedVisibility({
|
|
80
80
|
isOpen: isOpen,
|
|
81
|
-
animationKind: 'dialog',
|
|
82
81
|
shouldAnimate: shouldAnimate,
|
|
83
82
|
elementRef: ownRef,
|
|
84
83
|
onEnterFinish: onEnterFinish,
|
|
@@ -44,9 +44,8 @@ var roleToAriaHasPopup = {
|
|
|
44
44
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
45
45
|
* the value is `undefined`.
|
|
46
46
|
* - `aria-expanded` - reflects the current open state.
|
|
47
|
-
* - `aria-controls` - references the popover element by its id
|
|
48
|
-
* while the popover is closed
|
|
49
|
-
* carries the current visibility state.
|
|
47
|
+
* - `aria-controls` - references the popover element by its id while the
|
|
48
|
+
* popover is open, and is omitted while the popover is closed and unmounted.
|
|
50
49
|
*
|
|
51
50
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
52
51
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -1,63 +1,38 @@
|
|
|
1
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
-
import { bind } from 'bind-event-listener';
|
|
4
3
|
import { prefersReducedMotion } from './reduced-motion';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* Not setting these timings on the preset config because they're defined in the CSS,
|
|
9
|
-
* so configuring it in the preset doesn't achieve anything except introduce a potential
|
|
10
|
-
* source of drift.
|
|
11
|
-
*
|
|
12
|
-
* TODO: figure out a way to not need to redeclare these in the future,
|
|
13
|
-
* and keep CSS and JS timings in sync always.
|
|
14
|
-
*/
|
|
15
|
-
var safetyNetMsMap = {
|
|
16
|
-
dialog: {
|
|
17
|
-
enter: 250,
|
|
18
|
-
// Corresponds to `motion.duration.long`
|
|
19
|
-
exit: 200 // Corresponds to `motion.duration.medium`
|
|
20
|
-
},
|
|
21
|
-
popover: {
|
|
22
|
-
enter: 150,
|
|
23
|
-
// Corresponds to `motion.duration.short`
|
|
24
|
-
exit: 100 // Corresponds to `motion.duration.xshort`
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Fires `onSettled` once when `transitionend` fires on `element`, or after
|
|
30
|
-
* `safetyNetMs` if the event is swallowed (mid-transition unmount, CSS
|
|
31
|
-
* mismatch, browser quirks). Returns a cleanup that cancels both paths.
|
|
32
|
-
*/
|
|
33
|
-
function runOnTransitionEndOrTimeout(_ref) {
|
|
34
|
-
var element = _ref.element,
|
|
35
|
-
safetyNetMs = _ref.safetyNetMs,
|
|
4
|
+
function runCancellableSettlement(_ref) {
|
|
5
|
+
var schedule = _ref.schedule,
|
|
36
6
|
onSettled = _ref.onSettled;
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function teardown() {
|
|
41
|
-
state.isSettled = true;
|
|
42
|
-
clearTimeout(safetyNetId);
|
|
43
|
-
unbind();
|
|
44
|
-
}
|
|
45
|
-
function settle() {
|
|
46
|
-
if (state.isSettled) {
|
|
7
|
+
var isCancelled = false;
|
|
8
|
+
schedule(function () {
|
|
9
|
+
if (isCancelled) {
|
|
47
10
|
return;
|
|
48
11
|
}
|
|
49
|
-
teardown();
|
|
50
12
|
onSettled();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
13
|
+
});
|
|
14
|
+
return function () {
|
|
15
|
+
isCancelled = true;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fires `onSettled` once all animations currently applied to `element`
|
|
21
|
+
* finish or are cancelled. Returns a cleanup that prevents stale settlement.
|
|
22
|
+
*/
|
|
23
|
+
function runOnAnimationsSettled(_ref2) {
|
|
24
|
+
var element = _ref2.element,
|
|
25
|
+
onSettled = _ref2.onSettled;
|
|
26
|
+
return runCancellableSettlement({
|
|
27
|
+
onSettled: onSettled,
|
|
28
|
+
schedule: function schedule(settle) {
|
|
29
|
+
var _element$getAnimation, _element$getAnimation2;
|
|
30
|
+
var animations = (_element$getAnimation = (_element$getAnimation2 = element.getAnimations) === null || _element$getAnimation2 === void 0 ? void 0 : _element$getAnimation2.call(element)) !== null && _element$getAnimation !== void 0 ? _element$getAnimation : [];
|
|
31
|
+
Promise.allSettled(animations.map(function (animation) {
|
|
32
|
+
return animation.finished;
|
|
33
|
+
})).then(settle);
|
|
57
34
|
}
|
|
58
35
|
});
|
|
59
|
-
var safetyNetId = setTimeout(settle, safetyNetMs);
|
|
60
|
-
return teardown;
|
|
61
36
|
}
|
|
62
37
|
|
|
63
38
|
/**
|
|
@@ -121,13 +96,12 @@ function runOnTransitionEndOrTimeout(_ref) {
|
|
|
121
96
|
* Every effect dispatches on the single `phase` value instead of a
|
|
122
97
|
* cross-product of booleans.
|
|
123
98
|
*/
|
|
124
|
-
export function useAnimatedVisibility(
|
|
125
|
-
var isOpen =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
onExitFinish = _ref2.onExitFinish;
|
|
99
|
+
export function useAnimatedVisibility(_ref3) {
|
|
100
|
+
var isOpen = _ref3.isOpen,
|
|
101
|
+
shouldAnimate = _ref3.shouldAnimate,
|
|
102
|
+
elementRef = _ref3.elementRef,
|
|
103
|
+
onEnterFinish = _ref3.onEnterFinish,
|
|
104
|
+
onExitFinish = _ref3.onExitFinish;
|
|
131
105
|
var willAnimate = shouldAnimate && !prefersReducedMotion();
|
|
132
106
|
var isNativeClosedRef = useRef(false);
|
|
133
107
|
|
|
@@ -168,16 +142,14 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
168
142
|
setPhase(willAnimate ? 'entering' : 'open');
|
|
169
143
|
}
|
|
170
144
|
|
|
171
|
-
// Keep stable refs to the latest callbacks so effects and
|
|
145
|
+
// Keep stable refs to the latest callbacks so effects and promise callbacks
|
|
172
146
|
// always call the current callback without needing them in dependency arrays.
|
|
173
147
|
var onEnterFinishRef = useRef(onEnterFinish);
|
|
174
148
|
onEnterFinishRef.current = onEnterFinish;
|
|
175
149
|
var onExitFinishRef = useRef(onExitFinish);
|
|
176
150
|
onExitFinishRef.current = onExitFinish;
|
|
177
151
|
|
|
178
|
-
// Animated entry: wait for
|
|
179
|
-
// move to `open`. The fallback guarantees settle on swallowed events
|
|
180
|
-
// (zero-duration, browser quirk, mid-transition unmount).
|
|
152
|
+
// Animated entry: wait for all active animations and move to `open`.
|
|
181
153
|
useEffect(function () {
|
|
182
154
|
if (phase !== 'entering') {
|
|
183
155
|
return;
|
|
@@ -186,9 +158,8 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
186
158
|
if (!element) {
|
|
187
159
|
return;
|
|
188
160
|
}
|
|
189
|
-
return
|
|
161
|
+
return runOnAnimationsSettled({
|
|
190
162
|
element: element,
|
|
191
|
-
safetyNetMs: safetyNetMsMap[animationKind].enter + 50,
|
|
192
163
|
onSettled: function onSettled() {
|
|
193
164
|
var _onEnterFinishRef$cur;
|
|
194
165
|
// Fire the consumer callback before transitioning phase so any
|
|
@@ -201,7 +172,7 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
201
172
|
});
|
|
202
173
|
}
|
|
203
174
|
});
|
|
204
|
-
}, [phase,
|
|
175
|
+
}, [phase, elementRef]);
|
|
205
176
|
|
|
206
177
|
// Non-animated entry callback. The lifecycle goes `closed → open`
|
|
207
178
|
// directly with no `entering` phase; fire `onEnterFinish` on that jump.
|
|
@@ -219,8 +190,8 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
219
190
|
}
|
|
220
191
|
}, [phase]);
|
|
221
192
|
|
|
222
|
-
// Animated exit: wait for
|
|
223
|
-
//
|
|
193
|
+
// Animated exit: wait for all active animations, fire `onExitFinish`,
|
|
194
|
+
// then move to `closed` to unmount the host.
|
|
224
195
|
useEffect(function () {
|
|
225
196
|
if (phase !== 'exiting' || !willAnimate) {
|
|
226
197
|
return;
|
|
@@ -229,9 +200,8 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
229
200
|
if (!element) {
|
|
230
201
|
return;
|
|
231
202
|
}
|
|
232
|
-
return
|
|
203
|
+
return runOnAnimationsSettled({
|
|
233
204
|
element: element,
|
|
234
|
-
safetyNetMs: safetyNetMsMap[animationKind].exit + 50,
|
|
235
205
|
onSettled: function onSettled() {
|
|
236
206
|
var _onExitFinishRef$curr;
|
|
237
207
|
// Fire the consumer callback before transitioning phase to
|
|
@@ -246,7 +216,7 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
246
216
|
});
|
|
247
217
|
}
|
|
248
218
|
});
|
|
249
|
-
}, [phase, willAnimate,
|
|
219
|
+
}, [phase, willAnimate, elementRef]);
|
|
250
220
|
|
|
251
221
|
// Native dismissal is authoritative. Its toggle can arrive while controlled
|
|
252
222
|
// state still says open, so remember that native state and do not
|
|
@@ -276,7 +246,7 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
276
246
|
});
|
|
277
247
|
}, [willAnimate]);
|
|
278
248
|
|
|
279
|
-
// Non-animated exit callback.
|
|
249
|
+
// Non-animated exit callback. There are no animations to wait for, so fire
|
|
280
250
|
// `onExitFinish` on the transition into `exiting` (the unmount itself
|
|
281
251
|
// is gated by the closing toggle handshake above).
|
|
282
252
|
var prevPhaseForExitRef = useRef('closed');
|
|
@@ -286,7 +256,7 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
286
256
|
|
|
287
257
|
// Only fire on the non-animated transition into `exiting`. The
|
|
288
258
|
// animated path's `exiting → closed` settle already fires
|
|
289
|
-
// `onExitFinish` from `
|
|
259
|
+
// `onExitFinish` from `runOnAnimationsSettled`.
|
|
290
260
|
if (prevPhase !== 'exiting' && phase === 'exiting' && !willAnimate) {
|
|
291
261
|
var _onExitFinishRef$curr2;
|
|
292
262
|
(_onExitFinishRef$curr2 = onExitFinishRef.current) === null || _onExitFinishRef$curr2 === void 0 || _onExitFinishRef$curr2.call(onExitFinishRef);
|
|
@@ -130,7 +130,6 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
|
|
|
130
130
|
var popoverId = idProp !== null && idProp !== void 0 ? idProp : "popover-".concat(autoId.replace(/:/g, ''));
|
|
131
131
|
var _useAnimatedVisibilit = useAnimatedVisibility({
|
|
132
132
|
isOpen: isOpen,
|
|
133
|
-
animationKind: 'popover',
|
|
134
133
|
shouldAnimate: shouldAnimate,
|
|
135
134
|
elementRef: ownRef,
|
|
136
135
|
onEnterFinish: onEnterFinish,
|
|
@@ -65,9 +65,8 @@ type TAriaForTrigger = {
|
|
|
65
65
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
66
66
|
* the value is `undefined`.
|
|
67
67
|
* - `aria-expanded` - reflects the current open state.
|
|
68
|
-
* - `aria-controls` - references the popover element by its id
|
|
69
|
-
* while the popover is closed
|
|
70
|
-
* carries the current visibility state.
|
|
68
|
+
* - `aria-controls` - references the popover element by its id while the
|
|
69
|
+
* popover is open, and is omitted while the popover is closed and unmounted.
|
|
71
70
|
*
|
|
72
71
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
73
72
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { type RefObject } from 'react';
|
|
2
|
-
type TAnimationKind = 'dialog' | 'popover';
|
|
3
2
|
type TUseAnimatedVisibilityArgs = {
|
|
4
3
|
/**
|
|
5
4
|
* Whether the element is logically open.
|
|
6
5
|
*/
|
|
7
6
|
isOpen: boolean;
|
|
8
|
-
/**
|
|
9
|
-
* Animation kind used to select default safety-net timings.
|
|
10
|
-
*/
|
|
11
|
-
animationKind: TAnimationKind;
|
|
12
7
|
/**
|
|
13
8
|
* Animation config for entry/exit transitions.
|
|
14
9
|
* Pass `false` or `undefined` to disable animation.
|
|
@@ -16,7 +11,7 @@ type TUseAnimatedVisibilityArgs = {
|
|
|
16
11
|
shouldAnimate: boolean;
|
|
17
12
|
/**
|
|
18
13
|
* Ref to the DOM element that plays the entry/exit transitions.
|
|
19
|
-
* Used to
|
|
14
|
+
* Used to inspect active animations.
|
|
20
15
|
*/
|
|
21
16
|
elementRef: RefObject<HTMLElement | null>;
|
|
22
17
|
/**
|
|
@@ -110,5 +105,5 @@ export type TPhase = 'closed' | 'entering' | 'open' | 'exiting';
|
|
|
110
105
|
* Every effect dispatches on the single `phase` value instead of a
|
|
111
106
|
* cross-product of booleans.
|
|
112
107
|
*/
|
|
113
|
-
export declare function useAnimatedVisibility({ isOpen,
|
|
108
|
+
export declare function useAnimatedVisibility({ isOpen, shouldAnimate, elementRef, onEnterFinish, onExitFinish, }: TUseAnimatedVisibilityArgs): TUseAnimatedVisibilityResult;
|
|
114
109
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/top-layer",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "Low-level layering primitives based on popover and dialog",
|
|
5
5
|
"homepage": "https://atlassian.design/components/top-layer",
|
|
6
6
|
"repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@af/visual-regression": "workspace:^",
|
|
56
56
|
"@atlaskit/button": "^25.0.0",
|
|
57
57
|
"@atlaskit/heading": "^7.0.0",
|
|
58
|
-
"@atlaskit/lozenge": "^15.
|
|
58
|
+
"@atlaskit/lozenge": "^15.1.0",
|
|
59
59
|
"@atlaskit/primitives": "^22.2.0",
|
|
60
60
|
"@atlaskit/ssr": "workspace:^",
|
|
61
61
|
"@atlaskit/textfield": "^10.0.0",
|