@atlaskit/top-layer 0.15.0 → 0.17.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/CHANGELOG.md +45 -0
- package/README.md +4 -4
- package/dist/cjs/animations/presets.js +96 -9
- package/dist/cjs/dialog/dialog-content.js +33 -4
- package/dist/cjs/dialog-scroll-lock/index.js +17 -7
- package/dist/cjs/entry-points/animations.js +12 -0
- package/dist/cjs/entry-points/use-popover-id.js +3 -1
- package/dist/cjs/internal/get-aria-for-trigger.js +8 -3
- package/dist/cjs/internal/use-anchor-position-at-point.js +4 -2
- package/dist/cjs/internal/use-anchor-position.js +3 -2
- package/dist/cjs/internal/use-animated-visibility.js +233 -155
- package/dist/cjs/internal/use-focus-wrap.js +5 -3
- package/dist/cjs/internal/use-initial-focus.js +196 -50
- package/dist/cjs/internal/use-width-from-anchor.js +5 -4
- package/dist/cjs/popover/popover.js +58 -9
- package/dist/es2019/animations/presets.js +145 -2
- package/dist/es2019/dialog/dialog-content.js +33 -4
- package/dist/es2019/dialog-scroll-lock/index.js +18 -8
- package/dist/es2019/entry-points/animations.js +1 -1
- package/dist/es2019/entry-points/use-popover-id.js +3 -1
- package/dist/es2019/internal/get-aria-for-trigger.js +8 -3
- package/dist/es2019/internal/use-anchor-position-at-point.js +4 -2
- package/dist/es2019/internal/use-anchor-position.js +3 -2
- package/dist/es2019/internal/use-animated-visibility.js +222 -149
- package/dist/es2019/internal/use-focus-wrap.js +5 -4
- package/dist/es2019/internal/use-initial-focus.js +189 -45
- package/dist/es2019/internal/use-width-from-anchor.js +5 -4
- package/dist/es2019/popover/popover.js +59 -10
- package/dist/esm/animations/presets.js +93 -9
- package/dist/esm/dialog/dialog-content.js +33 -4
- package/dist/esm/dialog-scroll-lock/index.js +17 -8
- package/dist/esm/entry-points/animations.js +1 -1
- package/dist/esm/entry-points/use-popover-id.js +3 -1
- package/dist/esm/internal/get-aria-for-trigger.js +8 -3
- package/dist/esm/internal/use-anchor-position-at-point.js +4 -2
- package/dist/esm/internal/use-anchor-position.js +3 -2
- package/dist/esm/internal/use-animated-visibility.js +235 -157
- package/dist/esm/internal/use-focus-wrap.js +5 -4
- package/dist/esm/internal/use-initial-focus.js +196 -51
- package/dist/esm/internal/use-width-from-anchor.js +5 -4
- package/dist/esm/popover/popover.js +59 -10
- package/dist/types/animations/presets.d.ts +16 -0
- package/dist/types/dialog/types.d.ts +9 -1
- package/dist/types/dialog-scroll-lock/index.d.ts +22 -6
- package/dist/types/entry-points/animations.d.ts +1 -1
- package/dist/types/internal/get-aria-for-trigger.d.ts +8 -1
- package/dist/types/internal/use-anchor-position-at-point.d.ts +6 -1
- package/dist/types/internal/use-anchor-position.d.ts +14 -1
- package/dist/types/internal/use-animated-visibility.d.ts +48 -19
- package/dist/types/internal/use-focus-wrap.d.ts +6 -1
- package/dist/types/internal/use-initial-focus.d.ts +55 -4
- package/dist/types/internal/use-width-from-anchor.d.ts +3 -2
- package/dist/types/popover/types.d.ts +13 -2
- package/dist/types-ts4.5/animations/presets.d.ts +16 -0
- package/dist/types-ts4.5/dialog/types.d.ts +9 -1
- package/dist/types-ts4.5/dialog-scroll-lock/index.d.ts +22 -6
- package/dist/types-ts4.5/entry-points/animations.d.ts +1 -1
- package/dist/types-ts4.5/internal/get-aria-for-trigger.d.ts +8 -1
- package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +6 -1
- package/dist/types-ts4.5/internal/use-anchor-position.d.ts +14 -1
- package/dist/types-ts4.5/internal/use-animated-visibility.d.ts +48 -19
- package/dist/types-ts4.5/internal/use-focus-wrap.d.ts +6 -1
- package/dist/types-ts4.5/internal/use-initial-focus.d.ts +55 -4
- package/dist/types-ts4.5/internal/use-width-from-anchor.d.ts +3 -2
- package/dist/types-ts4.5/popover/types.d.ts +13 -2
- package/package.json +3 -3
|
@@ -56,16 +56,30 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
56
56
|
elementRef: ownRef,
|
|
57
57
|
onExitFinish: onExitFinish
|
|
58
58
|
}),
|
|
59
|
-
|
|
59
|
+
phase = _useAnimatedVisibilit.phase,
|
|
60
60
|
preset = _useAnimatedVisibilit.preset;
|
|
61
61
|
|
|
62
|
+
// Pre-computed phase predicate. `isVisible` means "host element is
|
|
63
|
+
// mounted and on screen" (any phase except `closed`), used as a dep
|
|
64
|
+
// signal for the backdrop-click rebind effect so it re-attaches to
|
|
65
|
+
// the fresh <dialog> element after a close → reopen unmount/remount
|
|
66
|
+
// cycle.
|
|
67
|
+
var isVisible = phase !== 'closed';
|
|
68
|
+
|
|
62
69
|
// Focus wrap
|
|
63
70
|
// Native <dialog>.showModal() traps focus but wraps through <body> at
|
|
64
71
|
// the boundary (A → B → C → body → A). This hook intercepts Tab to
|
|
65
72
|
// wrap directly (A → B → C → A), matching the WAI-ARIA APG pattern.
|
|
73
|
+
//
|
|
74
|
+
// Takes `phase` directly: the listener stays attached while
|
|
75
|
+
// `phase !== 'closed'`, i.e. through the animated-exit window.
|
|
76
|
+
// Gating on `isOpen` would unbind the listener at the start of an
|
|
77
|
+
// exit animation while the dialog is still visible, letting focus
|
|
78
|
+
// escape (WCAG 2.4.3 Focus Order regression).
|
|
66
79
|
useFocusWrap({
|
|
67
80
|
elementRef: ownRef,
|
|
68
|
-
role: 'dialog'
|
|
81
|
+
role: 'dialog',
|
|
82
|
+
phase: phase
|
|
69
83
|
});
|
|
70
84
|
|
|
71
85
|
// Open layer observer registration
|
|
@@ -111,6 +125,8 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
111
125
|
// Attached via bind-event-listener rather than a React prop so we avoid
|
|
112
126
|
// a11y lint suppressions on the <dialog> element.
|
|
113
127
|
// Keyboard dismiss is already handled natively (Escape → onCancel above).
|
|
128
|
+
// `isVisible` is in deps so the listener re-binds to the new <dialog>
|
|
129
|
+
// element after a host unmount / remount cycle.
|
|
114
130
|
useEffect(function () {
|
|
115
131
|
var dialog = ownRef.current;
|
|
116
132
|
if (!dialog) {
|
|
@@ -126,13 +142,26 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
126
142
|
}
|
|
127
143
|
}
|
|
128
144
|
});
|
|
129
|
-
}, [onClose]);
|
|
145
|
+
}, [onClose, isVisible]);
|
|
130
146
|
|
|
131
147
|
// Atomic CSS (Compiled) deduplicates ::backdrop { background-color }
|
|
132
148
|
// into a single class, making it impossible to toggle between two values
|
|
133
149
|
// via cssMap entries. An ID-scoped <style> has higher specificity than
|
|
134
150
|
// any atomic class, so the override always wins.
|
|
135
151
|
var escapedId = CSS.escape(dialogId);
|
|
152
|
+
|
|
153
|
+
// The `<dialog>` host element is only rendered while open or its exit
|
|
154
|
+
// animation is playing. After exit completes the phase returns to
|
|
155
|
+
// `closed` and we unmount the dialog entirely so it does not leave an
|
|
156
|
+
// empty `role="dialog"` element in the accessibility tree. The element
|
|
157
|
+
// re-mounts on the next `isOpen=true` commit; `showModal()` is called
|
|
158
|
+
// from the existing `useLayoutEffect([isOpen])` on that same commit,
|
|
159
|
+
// and listeners (cancel, click) re-bind via the `[onClose, isVisible]`
|
|
160
|
+
// effect re-running after remount (React clears the ref on unmount, so
|
|
161
|
+
// we need the listener effect to re-run with the fresh element).
|
|
162
|
+
if (!isVisible) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
136
165
|
return /*#__PURE__*/React.createElement("dialog", _extends({
|
|
137
166
|
ref: combinedRef,
|
|
138
167
|
id: dialogId
|
|
@@ -153,5 +182,5 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
153
182
|
}), shouldHideBackdrop &&
|
|
154
183
|
/*#__PURE__*/
|
|
155
184
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
|
|
156
|
-
React.createElement("style", null, "#".concat(escapedId, "::backdrop{background-color:transparent}")),
|
|
185
|
+
React.createElement("style", null, "#".concat(escapedId, "::backdrop{background-color:transparent}")), children);
|
|
157
186
|
});
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { useLayoutEffect } from 'react';
|
|
2
2
|
import { getDocument } from '@atlaskit/browser-apis';
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* __DialogScrollLock__
|
|
6
5
|
*
|
|
7
|
-
* Prevents background page scrolling while
|
|
8
|
-
* inside a `Dialog`
|
|
9
|
-
* dialog closes (unmounts its children).
|
|
6
|
+
* Prevents background page scrolling while a dialog is open. Designed to be
|
|
7
|
+
* rendered inside a `Dialog` and given the same `isOpen` value.
|
|
10
8
|
*
|
|
11
9
|
* Native `<dialog>.showModal()` makes background content _inert_ (not
|
|
12
10
|
* focusable/clickable), but does **not** prevent scroll events from reaching
|
|
13
11
|
* the document body. This component fills that gap by setting
|
|
14
|
-
* `overflow: hidden` on `document.body`.
|
|
12
|
+
* `overflow: hidden` on `document.body` while `isOpen` is `true`.
|
|
13
|
+
*
|
|
14
|
+
* The lock is driven by `isOpen` rather than by mount/unmount so that release
|
|
15
|
+
* happens synchronously in the same commit that flips `isOpen` to `false`.
|
|
16
|
+
* Relying on unmount alone was racy: `Dialog` defers the unmount of its
|
|
17
|
+
* children until the host element's close/toggle event fires, which can be
|
|
18
|
+
* after a consumer or test observes the dialog as hidden, leading to a
|
|
19
|
+
* stale `overflow: hidden` window.
|
|
15
20
|
*
|
|
16
21
|
* @example
|
|
17
22
|
* ```tsx
|
|
@@ -19,14 +24,18 @@ import { getDocument } from '@atlaskit/browser-apis';
|
|
|
19
24
|
* import { DialogScrollLock } from '@atlaskit/top-layer/dialog-scroll-lock';
|
|
20
25
|
*
|
|
21
26
|
* <Dialog isOpen={isOpen} onClose={onClose}>
|
|
22
|
-
* <DialogScrollLock />
|
|
27
|
+
* <DialogScrollLock isOpen={isOpen} />
|
|
23
28
|
* {children}
|
|
24
29
|
* </Dialog>
|
|
25
30
|
* ```
|
|
26
31
|
*/
|
|
27
|
-
export function DialogScrollLock() {
|
|
32
|
+
export function DialogScrollLock(_ref) {
|
|
33
|
+
var isOpen = _ref.isOpen;
|
|
28
34
|
useLayoutEffect(function () {
|
|
29
35
|
var _getDocument;
|
|
36
|
+
if (!isOpen) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
30
39
|
var body = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.body;
|
|
31
40
|
if (!body) {
|
|
32
41
|
return;
|
|
@@ -36,6 +45,6 @@ export function DialogScrollLock() {
|
|
|
36
45
|
return function () {
|
|
37
46
|
body.style.overflow = previousOverflow;
|
|
38
47
|
};
|
|
39
|
-
}, []);
|
|
48
|
+
}, [isOpen]);
|
|
40
49
|
return null;
|
|
41
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { slideAndFade, fade, scaleAndFade, dialogSlideUpAndFade, dialogFade } from '../animations/presets';
|
|
1
|
+
export { dialogMotion, popupMotion, slideAndFade, fade, scaleAndFade, dialogSlideUpAndFade, dialogFade } from '../animations/presets';
|
|
@@ -10,6 +10,8 @@ import { useId } from '@atlaskit/ds-lib/use-id';
|
|
|
10
10
|
* own `anchor-name` on the trigger element.
|
|
11
11
|
*/
|
|
12
12
|
export function usePopoverId() {
|
|
13
|
-
//
|
|
13
|
+
// `@atlaskit/ds-lib/use-id` already strips characters that React's
|
|
14
|
+
// `useId` produces (`:`, `«`, `»`) which are invalid in HTML ID
|
|
15
|
+
// tokens. The `popover-` prefix is purely for DOM debug clarity.
|
|
14
16
|
return "popover-".concat(useId());
|
|
15
17
|
}
|
|
@@ -73,11 +73,16 @@ export function getAriaForTrigger(_ref) {
|
|
|
73
73
|
var ariaHasPopup = roleToAriaHasPopup({
|
|
74
74
|
role: role
|
|
75
75
|
});
|
|
76
|
+
// `aria-controls` is set to `undefined` while closed. The `Popover` /
|
|
77
|
+
// `Dialog` primitives unmount their host element after the exit
|
|
78
|
+
// animation finishes, so a closed-state `aria-controls` would point
|
|
79
|
+
// at a node that is not in the DOM. Returning `undefined` here means
|
|
80
|
+
// JSX spread renders no `aria-controls` attribute on the trigger
|
|
81
|
+
// until the target exists, avoiding the dangling reference while
|
|
82
|
+
// keeping the relationship live whenever it is meaningful.
|
|
76
83
|
return {
|
|
77
84
|
'aria-haspopup': ariaHasPopup,
|
|
78
85
|
'aria-expanded': isOpen,
|
|
79
|
-
|
|
80
|
-
// tells assistive technology whether the controlled popover is visible.
|
|
81
|
-
'aria-controls': popoverId
|
|
86
|
+
'aria-controls': isOpen ? popoverId : undefined
|
|
82
87
|
};
|
|
83
88
|
}
|
|
@@ -70,7 +70,8 @@ export function useAnchorPositionAtPoint(_ref) {
|
|
|
70
70
|
placement = _ref.placement,
|
|
71
71
|
getPoint = _ref.getPoint,
|
|
72
72
|
_ref$isEnabled = _ref.isEnabled,
|
|
73
|
-
isEnabled = _ref$isEnabled === void 0 ? true : _ref$isEnabled
|
|
73
|
+
isEnabled = _ref$isEnabled === void 0 ? true : _ref$isEnabled,
|
|
74
|
+
isOpen = _ref.isOpen;
|
|
74
75
|
var syntheticAnchorRef = useRef(null);
|
|
75
76
|
|
|
76
77
|
// `getPoint` is kept in a ref so the layout effect can call the
|
|
@@ -117,6 +118,7 @@ export function useAnchorPositionAtPoint(_ref) {
|
|
|
117
118
|
anchorRef: syntheticAnchorRef,
|
|
118
119
|
popoverRef: popoverRef,
|
|
119
120
|
placement: placement,
|
|
120
|
-
isEnabled: isEnabled
|
|
121
|
+
isEnabled: isEnabled,
|
|
122
|
+
isOpen: isOpen
|
|
121
123
|
});
|
|
122
124
|
}
|
|
@@ -214,7 +214,8 @@ export function useAnchorPosition(_ref3) {
|
|
|
214
214
|
_ref3$forceFallbackPo = _ref3.forceFallbackPositioning,
|
|
215
215
|
forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo,
|
|
216
216
|
_ref3$isEnabled = _ref3.isEnabled,
|
|
217
|
-
isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled
|
|
217
|
+
isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled,
|
|
218
|
+
isOpen = _ref3.isOpen;
|
|
218
219
|
var id = useId();
|
|
219
220
|
// Stabilize `placement` object so the same object literal
|
|
220
221
|
// value returns an object with the same reference
|
|
@@ -474,5 +475,5 @@ export function useAnchorPosition(_ref3) {
|
|
|
474
475
|
popover.style.removeProperty('opacity');
|
|
475
476
|
});
|
|
476
477
|
return undoPositioning;
|
|
477
|
-
}, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, isEnabled, id]);
|
|
478
|
+
}, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, isEnabled, id, isOpen]);
|
|
478
479
|
}
|