@atlaskit/top-layer 3.2.0 → 4.1.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/AGENTS.md +3 -1
- package/CHANGELOG.md +18 -0
- package/README.md +0 -14
- package/dist/cjs/dialog/dialog-content.js +47 -20
- package/dist/cjs/focus/is-in-same-layer.js +1 -7
- package/dist/cjs/focus/selectors.js +1 -1
- package/dist/cjs/internal/get-aria-for-trigger.js +23 -9
- package/dist/cjs/internal/role-types.js +1 -64
- package/dist/cjs/internal/use-anchor-position.js +2 -3
- package/dist/cjs/internal/use-animated-visibility.js +36 -39
- package/dist/cjs/popover/popover.js +18 -20
- package/dist/es2019/dialog/dialog-content.js +48 -22
- package/dist/es2019/focus/is-in-same-layer.js +1 -7
- package/dist/es2019/focus/selectors.js +1 -1
- package/dist/es2019/internal/get-aria-for-trigger.js +23 -10
- package/dist/es2019/internal/role-types.js +0 -59
- package/dist/es2019/internal/use-anchor-position.js +2 -3
- package/dist/es2019/internal/use-animated-visibility.js +38 -39
- package/dist/es2019/popover/popover.js +15 -17
- package/dist/esm/dialog/dialog-content.js +49 -22
- package/dist/esm/focus/is-in-same-layer.js +1 -7
- package/dist/esm/focus/selectors.js +1 -1
- package/dist/esm/internal/get-aria-for-trigger.js +23 -10
- package/dist/esm/internal/role-types.js +0 -58
- package/dist/esm/internal/use-anchor-position.js +2 -3
- package/dist/esm/internal/use-animated-visibility.js +38 -41
- package/dist/esm/popover/popover.js +17 -19
- package/dist/types/dialog/dialog-content.d.ts +2 -2
- package/dist/types/dialog/index.d.ts +1 -1
- package/dist/types/dialog/types.d.ts +17 -3
- package/dist/types/entry-points/dialog.d.ts +1 -1
- package/dist/types/internal/get-aria-for-trigger.d.ts +21 -7
- package/dist/types/internal/role-types.d.ts +0 -32
- package/dist/types/internal/use-animated-visibility.d.ts +7 -1
- package/dist/types/popover/popover.d.ts +1 -5
- package/dist/types/popover/types.d.ts +1 -1
- package/package.json +3 -30
- package/animations/package.json +0 -10
- package/dist/cjs/entry-points/animations.js +0 -1
- package/dist/cjs/internal/role-to-aria-has-popup.js +0 -37
- package/dist/cjs/internal/slot.js +0 -65
- package/dist/es2019/entry-points/animations.js +0 -0
- package/dist/es2019/internal/role-to-aria-has-popup.js +0 -32
- package/dist/es2019/internal/slot.js +0 -57
- package/dist/esm/entry-points/animations.js +0 -0
- package/dist/esm/internal/role-to-aria-has-popup.js +0 -31
- package/dist/esm/internal/slot.js +0 -59
- package/dist/types/entry-points/animations.d.ts +0 -0
- package/dist/types/internal/role-to-aria-has-popup.d.ts +0 -13
- package/dist/types/internal/slot.d.ts +0 -29
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { roleToAriaHasPopup } from './role-to-aria-has-popup';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Roles accepted by `getAriaForTrigger`.
|
|
5
3
|
*
|
|
@@ -9,12 +7,34 @@ import { roleToAriaHasPopup } from './role-to-aria-has-popup';
|
|
|
9
7
|
* manually for tooltip triggers.
|
|
10
8
|
*/
|
|
11
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Valid values for the `aria-haspopup` attribute.
|
|
12
|
+
*
|
|
13
|
+
* Derived from the HTML spec - maps popover roles to what the trigger
|
|
14
|
+
* announces. `undefined` means the attribute is omitted entirely (used for
|
|
15
|
+
* non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
|
|
16
|
+
* `aria-haspopup` would be misleading).
|
|
17
|
+
*
|
|
18
|
+
* `true` is intentionally NOT in the union - the runtime only ever produces
|
|
19
|
+
* the explicit string forms, so widening the type would invite consumers to
|
|
20
|
+
* pass `true` and get an `aria-haspopup="true"` serialisation that the
|
|
21
|
+
* runtime no longer emits.
|
|
22
|
+
*/
|
|
23
|
+
|
|
12
24
|
/**
|
|
13
25
|
* Non-undefined subset of `TAriaHasPopupValue`. Every role accepted by
|
|
14
26
|
* `getAriaForTrigger` maps to a concrete `aria-haspopup` string, so the
|
|
15
27
|
* trigger always receives a defined attribute value.
|
|
16
28
|
*/
|
|
17
29
|
|
|
30
|
+
var roleToAriaHasPopup = {
|
|
31
|
+
dialog: 'dialog',
|
|
32
|
+
alertdialog: 'dialog',
|
|
33
|
+
menu: 'menu',
|
|
34
|
+
listbox: 'listbox',
|
|
35
|
+
tree: 'tree',
|
|
36
|
+
grid: 'grid'
|
|
37
|
+
};
|
|
18
38
|
/**
|
|
19
39
|
* Returns the ARIA attributes to spread onto a click-activated popover trigger.
|
|
20
40
|
*
|
|
@@ -66,13 +86,6 @@ export function getAriaForTrigger(_ref) {
|
|
|
66
86
|
var role = _ref.role,
|
|
67
87
|
isOpen = _ref.isOpen,
|
|
68
88
|
popoverId = _ref.popoverId;
|
|
69
|
-
// `roleToAriaHasPopup` accepts a wider role union (including tooltip-family
|
|
70
|
-
// roles that map to `undefined`). `TAriaForTriggerRole` excludes those, so
|
|
71
|
-
// the returned value is guaranteed to be a defined string. The cast narrows
|
|
72
|
-
// the wider helper signature to match.
|
|
73
|
-
var ariaHasPopup = roleToAriaHasPopup({
|
|
74
|
-
role: role
|
|
75
|
-
});
|
|
76
89
|
// `aria-controls` is set to `undefined` while closed. The `Popover` /
|
|
77
90
|
// `Dialog` primitives unmount their host element after the exit
|
|
78
91
|
// animation finishes, so a closed-state `aria-controls` would point
|
|
@@ -81,7 +94,7 @@ export function getAriaForTrigger(_ref) {
|
|
|
81
94
|
// until the target exists, avoiding the dangling reference while
|
|
82
95
|
// keeping the relationship live whenever it is meaningful.
|
|
83
96
|
return {
|
|
84
|
-
'aria-haspopup':
|
|
97
|
+
'aria-haspopup': roleToAriaHasPopup[role],
|
|
85
98
|
'aria-expanded': isOpen,
|
|
86
99
|
'aria-controls': isOpen ? popoverId : undefined
|
|
87
100
|
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Valid values for the `aria-haspopup` attribute.
|
|
3
|
-
*
|
|
4
|
-
* Derived from the HTML spec - maps popover roles to what the trigger
|
|
5
|
-
* announces. `undefined` means the attribute is omitted entirely (used for
|
|
6
|
-
* non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
|
|
7
|
-
* `aria-haspopup` would be misleading).
|
|
8
|
-
*
|
|
9
|
-
* `true` is intentionally NOT in the union - the runtime only ever produces
|
|
10
|
-
* the explicit string forms, so widening the type would invite consumers to
|
|
11
|
-
* pass `true` and get an `aria-haspopup="true"` serialisation that the
|
|
12
|
-
* runtime no longer emits.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Roles that require an accessible name (`label` or `labelledBy`) per WCAG 4.1.2.
|
|
17
|
-
* Without a name, assistive technology cannot identify these landmarks.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Roles where the accessible name is derived from content or provided externally
|
|
22
|
-
* (e.g. a combobox labels its listbox, tooltip content IS the accessible name).
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
// Shared ARIA role discrimination types
|
|
26
|
-
//
|
|
27
|
-
// These encode the WCAG 4.1.2 constraint: roles like `dialog`, `alertdialog`,
|
|
28
|
-
// and `menu` MUST have an accessible name (either `label` or `labelledBy`).
|
|
29
|
-
// TypeScript enforces this at compile time via discriminated unions.
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* ARIA role props where a role is always required.
|
|
33
|
-
*
|
|
34
|
-
* Used by `TPopupContentProps`. Popup content must always declare
|
|
35
|
-
* its role so assistive technology can identify the layer.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* ARIA role props where a role is optional.
|
|
40
|
-
*
|
|
41
|
-
* Used by `TPopoverProps`. The low-level primitive allows omitting
|
|
42
|
-
* the role for cases where it is set externally or not needed.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
var rolesThatMoveFocus = new Set(['dialog', 'alertdialog', 'menu', 'listbox', 'tree', 'grid']);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Returns `true` when the given popover role moves focus into the popover
|
|
49
|
-
* on open, and `false` otherwise (including when `role` is `undefined`).
|
|
50
|
-
*/
|
|
51
|
-
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
52
|
-
export function shouldFocusIntoPopover(_ref) {
|
|
53
|
-
var role = _ref.role;
|
|
54
|
-
if (!role) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
return rolesThatMoveFocus.has(role);
|
|
58
|
-
}
|
|
@@ -317,11 +317,10 @@ export function useAnchorPosition(_ref3) {
|
|
|
317
317
|
* See `notes/decisions/anchor-name-lifetime.md`
|
|
318
318
|
*/
|
|
319
319
|
trigger.style.setProperty('anchor-name', anchorName);
|
|
320
|
-
|
|
320
|
+
return setStyle({
|
|
321
321
|
element: popover,
|
|
322
322
|
styles: popoverStyles
|
|
323
|
-
})
|
|
324
|
-
return _undoPositioning;
|
|
323
|
+
});
|
|
325
324
|
}
|
|
326
325
|
|
|
327
326
|
// JS fallback. The popover is already in the top layer via
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
-
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
-
import { bind
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
+
import { bind } from 'bind-event-listener';
|
|
4
4
|
import { prefersReducedMotion } from './reduced-motion';
|
|
5
5
|
/**
|
|
6
6
|
* Using the recommended motion timings for safety net.
|
|
@@ -110,7 +110,7 @@ function runOnTransitionEndOrTimeout(_ref) {
|
|
|
110
110
|
* `entering`, `open`, `exiting`). The host element is rendered while
|
|
111
111
|
* `phase !== 'closed'`, so the `exiting` phase keeps the host mounted
|
|
112
112
|
* while the CSS exit transition plays (or, with animation disabled,
|
|
113
|
-
* while we wait for the browser `toggle`
|
|
113
|
+
* while we wait for the browser's closing `toggle` event to fire on the
|
|
114
114
|
* still-attached element):
|
|
115
115
|
*
|
|
116
116
|
* ```
|
|
@@ -129,6 +129,7 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
129
129
|
onEnterFinish = _ref2.onEnterFinish,
|
|
130
130
|
onExitFinish = _ref2.onExitFinish;
|
|
131
131
|
var willAnimate = shouldAnimate && !prefersReducedMotion();
|
|
132
|
+
var isNativeClosedRef = useRef(false);
|
|
132
133
|
|
|
133
134
|
// Promote `closed → entering`/`closed → open` synchronously during
|
|
134
135
|
// render so the host element mounts in the same commit as the open
|
|
@@ -142,16 +143,23 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
142
143
|
_useState2 = _slicedToArray(_useState, 2),
|
|
143
144
|
phase = _useState2[0],
|
|
144
145
|
setPhase = _useState2[1];
|
|
145
|
-
|
|
146
|
+
|
|
147
|
+
// Rearm only after controlled state and the native lifecycle have both
|
|
148
|
+
// reached closed. Resetting as soon as `isOpen` becomes false can erase a
|
|
149
|
+
// closing toggle that was handled in the same React batch.
|
|
150
|
+
if (!isOpen && phase === 'closed') {
|
|
151
|
+
isNativeClosedRef.current = false;
|
|
152
|
+
}
|
|
153
|
+
if (isOpen && phase === 'closed' && !isNativeClosedRef.current) {
|
|
146
154
|
setPhase(willAnimate ? 'entering' : 'open');
|
|
147
155
|
}
|
|
148
156
|
if (!isOpen && (phase === 'open' || phase === 'entering')) {
|
|
149
157
|
// Close intent. Move to `exiting` to gate the unmount on either
|
|
150
|
-
// the exit transition (animated) or the toggle
|
|
158
|
+
// the exit transition (animated) or the closing toggle event
|
|
151
159
|
// (non-animated). Includes interrupted entry (`entering → exiting`).
|
|
152
160
|
setPhase('exiting');
|
|
153
161
|
}
|
|
154
|
-
if (isOpen && phase === 'exiting') {
|
|
162
|
+
if (isOpen && phase === 'exiting' && !isNativeClosedRef.current) {
|
|
155
163
|
// Reopen mid-exit. With animation, jump back to `entering` and let
|
|
156
164
|
// the entry transition restart from current visual state. Without
|
|
157
165
|
// animation, jump straight to `open` (the handshake we were waiting
|
|
@@ -240,49 +248,37 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
240
248
|
});
|
|
241
249
|
}, [phase, willAnimate, animationKind, elementRef]);
|
|
242
250
|
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
// native focus restoration both run against the still-attached
|
|
247
|
-
// element. A naive `setTimeout(0)` occasionally beats the `toggle`
|
|
248
|
-
// task in Chromium and drops focus restoration.
|
|
249
|
-
//
|
|
250
|
-
// `useLayoutEffect` so the listener binds in the same commit as the
|
|
251
|
-
// show/hide layout effect that calls `hidePopover()` / `dialog.close()`.
|
|
252
|
-
// The toggle/close event is queued as a task and cannot run until the
|
|
253
|
-
// frame completes. SSR-safe: `phase` starts at `'closed'`.
|
|
254
|
-
//
|
|
255
|
-
// `onExitFinish` is fired separately (see effect below) so consumers
|
|
256
|
-
// observe it the moment `isOpen` flips to `false`.
|
|
251
|
+
// Native dismissal is authoritative. Its toggle can arrive while controlled
|
|
252
|
+
// state still says open, so remember that native state and do not
|
|
253
|
+
// reopen until `isOpen` has first moved to false.
|
|
257
254
|
useLayoutEffect(function () {
|
|
258
|
-
if (phase
|
|
259
|
-
|
|
255
|
+
if (phase === 'exiting' && !willAnimate && isNativeClosedRef.current) {
|
|
256
|
+
setPhase('closed');
|
|
260
257
|
}
|
|
261
|
-
|
|
262
|
-
|
|
258
|
+
}, [phase, willAnimate]);
|
|
259
|
+
var onToggle = useCallback(function (event) {
|
|
260
|
+
if (event.newState === 'open') {
|
|
261
|
+
isNativeClosedRef.current = false;
|
|
263
262
|
return;
|
|
264
263
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return current === 'exiting' ? 'closed' : current;
|
|
268
|
-
});
|
|
264
|
+
if (event.newState !== 'closed') {
|
|
265
|
+
return;
|
|
269
266
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
unmount();
|
|
275
|
-
}
|
|
267
|
+
isNativeClosedRef.current = true;
|
|
268
|
+
setPhase(function (current) {
|
|
269
|
+
if (current === 'open' || current === 'entering') {
|
|
270
|
+
return 'exiting';
|
|
276
271
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
272
|
+
if (current === 'exiting' && !willAnimate) {
|
|
273
|
+
return 'closed';
|
|
274
|
+
}
|
|
275
|
+
return current;
|
|
276
|
+
});
|
|
277
|
+
}, [willAnimate]);
|
|
282
278
|
|
|
283
279
|
// Non-animated exit callback. No `transitionend` to wait for, so fire
|
|
284
280
|
// `onExitFinish` on the transition into `exiting` (the unmount itself
|
|
285
|
-
// is gated by the toggle
|
|
281
|
+
// is gated by the closing toggle handshake above).
|
|
286
282
|
var prevPhaseForExitRef = useRef('closed');
|
|
287
283
|
useEffect(function () {
|
|
288
284
|
var prevPhase = prevPhaseForExitRef.current;
|
|
@@ -297,6 +293,7 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
297
293
|
}
|
|
298
294
|
}, [phase, willAnimate]);
|
|
299
295
|
return {
|
|
300
|
-
phase: phase
|
|
296
|
+
phase: phase,
|
|
297
|
+
onToggle: onToggle
|
|
301
298
|
};
|
|
302
299
|
}
|
|
@@ -8,7 +8,7 @@ import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
|
8
8
|
import once from '@atlaskit/ds-lib/once';
|
|
9
9
|
import { useNotifyOpenLayerObserver } from '@atlaskit/layering/use-notify-open-layer-observer';
|
|
10
10
|
import { resolvePlacement } from '../internal/resolve-placement';
|
|
11
|
-
import { shouldFocusIntoPopover } from '../internal/
|
|
11
|
+
import { shouldFocusIntoPopover } from '../internal/should-focus-into-popover';
|
|
12
12
|
import { useAnimatedVisibility } from '../internal/use-animated-visibility';
|
|
13
13
|
import { useFocusWrap } from '../internal/use-focus-wrap';
|
|
14
14
|
import { useInitialFocus } from '../internal/use-initial-focus';
|
|
@@ -88,11 +88,7 @@ var POPUP_ROLES = new Set(['menu', 'listbox', 'dialog', 'alertdialog', 'tree', '
|
|
|
88
88
|
* ### 📜 Browser dismiss is non-cancellable
|
|
89
89
|
*
|
|
90
90
|
* Per the popover spec, `beforetoggle` is cancellable on open transitions
|
|
91
|
-
* only, not on close.
|
|
92
|
-
* (e.g. a click on a logically-associated external trigger) must reconcile
|
|
93
|
-
* inside their own `onClose` handler by calling `showPopover()` again.
|
|
94
|
-
* `Popover` does not self-heal so consumers do not pay a reconcile cost
|
|
95
|
-
* by default.
|
|
91
|
+
* only, not on close.
|
|
96
92
|
*
|
|
97
93
|
* - Spec: <https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute>
|
|
98
94
|
* - <https://github.com/whatwg/html/issues/8973>
|
|
@@ -139,7 +135,8 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
|
|
|
139
135
|
onEnterFinish: onEnterFinish,
|
|
140
136
|
onExitFinish: onExitFinish
|
|
141
137
|
}),
|
|
142
|
-
phase = _useAnimatedVisibilit.phase
|
|
138
|
+
phase = _useAnimatedVisibilit.phase,
|
|
139
|
+
onToggle = _useAnimatedVisibilit.onToggle;
|
|
143
140
|
|
|
144
141
|
// True while the host element is mounted (any phase except `closed`).
|
|
145
142
|
// Used as a dep so listener-rebind effects re-attach after a remount.
|
|
@@ -212,6 +209,7 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
|
|
|
212
209
|
var handleToggle = useCallback(function (event) {
|
|
213
210
|
var element = ownRef.current;
|
|
214
211
|
if (event.newState === 'open') {
|
|
212
|
+
onToggle(event);
|
|
215
213
|
return;
|
|
216
214
|
}
|
|
217
215
|
if (event.newState === 'closed') {
|
|
@@ -223,11 +221,11 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
|
|
|
223
221
|
// click target keeps focus per HTML spec
|
|
224
222
|
// (`focusPreviousElement=false`).
|
|
225
223
|
var previouslyFocused = previouslyFocusedElementRef.current;
|
|
226
|
-
var
|
|
224
|
+
var reason = closeReasonRef.current;
|
|
227
225
|
var isProgrammatic = programmaticCloseRef.current;
|
|
228
226
|
var shouldRestore = previouslyFocused !== null && previouslyFocused.isConnected && shouldFocusIntoPopover({
|
|
229
227
|
role: role
|
|
230
|
-
}) && (
|
|
228
|
+
}) && (reason === 'escape' || isProgrammatic);
|
|
231
229
|
if (shouldRestore && previouslyFocused) {
|
|
232
230
|
previouslyFocused.focus({
|
|
233
231
|
preventScroll: true
|
|
@@ -241,18 +239,18 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
|
|
|
241
239
|
// Reset reason so a stale 'escape' from a race with Escape keydown
|
|
242
240
|
// does not corrupt the next browser-dismiss cycle.
|
|
243
241
|
closeReasonRef.current = 'light-dismiss';
|
|
244
|
-
|
|
242
|
+
} else {
|
|
243
|
+
// Browser dismiss (Escape/click-outside).
|
|
244
|
+
var _reason = closeReasonRef.current;
|
|
245
|
+
closeReasonRef.current = 'light-dismiss';
|
|
246
|
+
// Optional: `manual` mode has no `onClose` in the forwarded type.
|
|
247
|
+
onClose === null || onClose === void 0 || onClose({
|
|
248
|
+
reason: _reason
|
|
249
|
+
});
|
|
245
250
|
}
|
|
246
|
-
|
|
247
|
-
// Browser dismiss (Escape/click-outside).
|
|
248
|
-
var reason = closeReasonRef.current;
|
|
249
|
-
closeReasonRef.current = 'light-dismiss';
|
|
250
|
-
// Optional: `manual` mode has no `onClose` in the forwarded type.
|
|
251
|
-
onClose === null || onClose === void 0 || onClose({
|
|
252
|
-
reason: reason
|
|
253
|
-
});
|
|
254
251
|
}
|
|
255
|
-
|
|
252
|
+
onToggle(event);
|
|
253
|
+
}, [onClose, onToggle, role]);
|
|
256
254
|
|
|
257
255
|
// Bind via `useLayoutEffect`, and BEFORE the show/hide layout effect
|
|
258
256
|
// below, so listeners are attached when `showPopover()` synchronously
|
|
@@ -14,8 +14,8 @@ import { type TDialogProps } from './types';
|
|
|
14
14
|
*
|
|
15
15
|
* Handles native `cancel` event (Escape) and backdrop click detection.
|
|
16
16
|
*
|
|
17
|
-
* Close flow:
|
|
18
|
-
*
|
|
17
|
+
* Close flow: allowed user dismissal closes the native dialog before `onClose`
|
|
18
|
+
* notifies the consumer. The consumer must synchronize `isOpen` in response.
|
|
19
19
|
*
|
|
20
20
|
* Accessibility: render at least one focusable element (typically a close
|
|
21
21
|
* button) when `isOpen` becomes `true`. Tab is always trapped inside the
|
|
@@ -11,6 +11,10 @@ import type { StrictXCSSProp } from '@atlaskit/css';
|
|
|
11
11
|
* produces. The consumer simply sets `isOpen={false}`.
|
|
12
12
|
*/
|
|
13
13
|
export type TDialogCloseReason = 'escape' | 'overlay-click';
|
|
14
|
+
/**
|
|
15
|
+
* User interactions that are allowed to dismiss the dialog.
|
|
16
|
+
*/
|
|
17
|
+
export type TDialogDismissedBy = 'none' | 'escape' | 'escape-and-outside-click';
|
|
14
18
|
/**
|
|
15
19
|
* Props shared across `TDialogProps` variants.
|
|
16
20
|
*
|
|
@@ -40,14 +44,24 @@ type TDialogBaseProps = {
|
|
|
40
44
|
*/
|
|
41
45
|
isOpen: boolean;
|
|
42
46
|
/**
|
|
43
|
-
* Called
|
|
47
|
+
* Called after the dialog closes through an allowed Escape or backdrop interaction.
|
|
44
48
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
49
|
+
* User dismissal cannot be rejected from this callback. The consumer must set
|
|
50
|
+
* `isOpen={false}` in response to synchronize controlled state with the native dialog.
|
|
47
51
|
*/
|
|
48
52
|
onClose: (args: {
|
|
49
53
|
reason: TDialogCloseReason;
|
|
50
54
|
}) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Controls which user actions can dismiss the dialog.
|
|
57
|
+
*
|
|
58
|
+
* - `'escape-and-outside-click'`: Escape and outside click.
|
|
59
|
+
* - `'escape'`: Escape only.
|
|
60
|
+
* - `'none'`: no user action.
|
|
61
|
+
*
|
|
62
|
+
* Defaults to `'escape-and-outside-click'`.
|
|
63
|
+
*/
|
|
64
|
+
dismissedBy?: TDialogDismissedBy;
|
|
51
65
|
/**
|
|
52
66
|
* Whether the dialog should animate in and out.
|
|
53
67
|
*
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { createCloseEvent, Dialog } from '../dialog';
|
|
2
|
-
export type { TDialogCloseReason, TDialogProps } from '../dialog';
|
|
2
|
+
export type { TDialogCloseReason, TDialogDismissedBy, TDialogProps } from '../dialog';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type TRoleRequiringAccessibleName } from './role-types';
|
|
2
2
|
/**
|
|
3
3
|
* Roles accepted by `getAriaForTrigger`.
|
|
4
4
|
*
|
|
@@ -8,6 +8,26 @@ import { type TAriaHasPopupValue, type TRoleRequiringAccessibleName } from './ro
|
|
|
8
8
|
* manually for tooltip triggers.
|
|
9
9
|
*/
|
|
10
10
|
type TAriaForTriggerRole = TRoleRequiringAccessibleName | 'listbox' | 'tree' | 'grid';
|
|
11
|
+
/**
|
|
12
|
+
* Valid values for the `aria-haspopup` attribute.
|
|
13
|
+
*
|
|
14
|
+
* Derived from the HTML spec - maps popover roles to what the trigger
|
|
15
|
+
* announces. `undefined` means the attribute is omitted entirely (used for
|
|
16
|
+
* non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
|
|
17
|
+
* `aria-haspopup` would be misleading).
|
|
18
|
+
*
|
|
19
|
+
* `true` is intentionally NOT in the union - the runtime only ever produces
|
|
20
|
+
* the explicit string forms, so widening the type would invite consumers to
|
|
21
|
+
* pass `true` and get an `aria-haspopup="true"` serialisation that the
|
|
22
|
+
* runtime no longer emits.
|
|
23
|
+
*/
|
|
24
|
+
type TAriaHasPopupValue = 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Non-undefined subset of `TAriaHasPopupValue`. Every role accepted by
|
|
27
|
+
* `getAriaForTrigger` maps to a concrete `aria-haspopup` string, so the
|
|
28
|
+
* trigger always receives a defined attribute value.
|
|
29
|
+
*/
|
|
30
|
+
type TAriaHasPopupForTrigger = Exclude<TAriaHasPopupValue, undefined>;
|
|
11
31
|
type TGetAriaForTriggerOptions = {
|
|
12
32
|
/**
|
|
13
33
|
* The `role` that will be set on the `<Popover>` element. Used to derive
|
|
@@ -24,12 +44,6 @@ type TGetAriaForTriggerOptions = {
|
|
|
24
44
|
*/
|
|
25
45
|
popoverId: string;
|
|
26
46
|
};
|
|
27
|
-
/**
|
|
28
|
-
* Non-undefined subset of `TAriaHasPopupValue`. Every role accepted by
|
|
29
|
-
* `getAriaForTrigger` maps to a concrete `aria-haspopup` string, so the
|
|
30
|
-
* trigger always receives a defined attribute value.
|
|
31
|
-
*/
|
|
32
|
-
type TAriaHasPopupForTrigger = Exclude<TAriaHasPopupValue, undefined>;
|
|
33
47
|
type TAriaForTrigger = {
|
|
34
48
|
'aria-haspopup': TAriaHasPopupForTrigger;
|
|
35
49
|
'aria-expanded': boolean;
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Valid values for the `aria-haspopup` attribute.
|
|
3
|
-
*
|
|
4
|
-
* Derived from the HTML spec - maps popover roles to what the trigger
|
|
5
|
-
* announces. `undefined` means the attribute is omitted entirely (used for
|
|
6
|
-
* non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
|
|
7
|
-
* `aria-haspopup` would be misleading).
|
|
8
|
-
*
|
|
9
|
-
* `true` is intentionally NOT in the union - the runtime only ever produces
|
|
10
|
-
* the explicit string forms, so widening the type would invite consumers to
|
|
11
|
-
* pass `true` and get an `aria-haspopup="true"` serialisation that the
|
|
12
|
-
* runtime no longer emits.
|
|
13
|
-
*/
|
|
14
|
-
export type TAriaHasPopupValue = 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | undefined;
|
|
15
1
|
/**
|
|
16
2
|
* Roles that require an accessible name (`label` or `labelledBy`) per WCAG 4.1.2.
|
|
17
3
|
* Without a name, assistive technology cannot identify these landmarks.
|
|
@@ -50,22 +36,4 @@ export type TAriaRoleRequired = ({
|
|
|
50
36
|
} & TAccessibleNameRequired) | ({
|
|
51
37
|
role: TRoleWithImplicitName;
|
|
52
38
|
} & TAccessibleNameOptional);
|
|
53
|
-
/**
|
|
54
|
-
* ARIA role props where a role is optional.
|
|
55
|
-
*
|
|
56
|
-
* Used by `TPopoverProps`. The low-level primitive allows omitting
|
|
57
|
-
* the role for cases where it is set externally or not needed.
|
|
58
|
-
*/
|
|
59
|
-
export type TAriaRoleOptional = ({
|
|
60
|
-
role: TRoleRequiringAccessibleName;
|
|
61
|
-
} & TAccessibleNameRequired) | ({
|
|
62
|
-
role?: TRoleWithImplicitName;
|
|
63
|
-
} & TAccessibleNameOptional);
|
|
64
|
-
/**
|
|
65
|
-
* Returns `true` when the given popover role moves focus into the popover
|
|
66
|
-
* on open, and `false` otherwise (including when `role` is `undefined`).
|
|
67
|
-
*/
|
|
68
|
-
export declare function shouldFocusIntoPopover({ role, }: {
|
|
69
|
-
role: TRoleRequiringAccessibleName | TRoleWithImplicitName | undefined;
|
|
70
|
-
}): boolean;
|
|
71
39
|
export {};
|
|
@@ -42,6 +42,12 @@ type TUseAnimatedVisibilityResult = {
|
|
|
42
42
|
* trapping, listeners that must outlive the exit).
|
|
43
43
|
*/
|
|
44
44
|
phase: TPhase;
|
|
45
|
+
/**
|
|
46
|
+
* Notifies the visibility lifecycle after the primitive has finished its
|
|
47
|
+
* own native toggle handling. Closing toggles make native dismissal
|
|
48
|
+
* authoritative while preserving primitive-specific handler ordering.
|
|
49
|
+
*/
|
|
50
|
+
onToggle: (event: ToggleEvent) => void;
|
|
45
51
|
};
|
|
46
52
|
/**
|
|
47
53
|
* Visibility lifecycle phase shared between `useAnimatedVisibility`,
|
|
@@ -93,7 +99,7 @@ export type TPhase = 'closed' | 'entering' | 'open' | 'exiting';
|
|
|
93
99
|
* `entering`, `open`, `exiting`). The host element is rendered while
|
|
94
100
|
* `phase !== 'closed'`, so the `exiting` phase keeps the host mounted
|
|
95
101
|
* while the CSS exit transition plays (or, with animation disabled,
|
|
96
|
-
* while we wait for the browser `toggle`
|
|
102
|
+
* while we wait for the browser's closing `toggle` event to fire on the
|
|
97
103
|
* still-attached element):
|
|
98
104
|
*
|
|
99
105
|
* ```
|
|
@@ -16,11 +16,7 @@ import { type TPopoverForwardedProps } from './types';
|
|
|
16
16
|
* ### 📜 Browser dismiss is non-cancellable
|
|
17
17
|
*
|
|
18
18
|
* Per the popover spec, `beforetoggle` is cancellable on open transitions
|
|
19
|
-
* only, not on close.
|
|
20
|
-
* (e.g. a click on a logically-associated external trigger) must reconcile
|
|
21
|
-
* inside their own `onClose` handler by calling `showPopover()` again.
|
|
22
|
-
* `Popover` does not self-heal so consumers do not pay a reconcile cost
|
|
23
|
-
* by default.
|
|
19
|
+
* only, not on close.
|
|
24
20
|
*
|
|
25
21
|
* - Spec: <https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute>
|
|
26
22
|
* - <https://github.com/whatwg/html/issues/8973>
|
|
@@ -92,7 +92,7 @@ type TPopoverBaseProps = {
|
|
|
92
92
|
*/
|
|
93
93
|
isOpen: boolean;
|
|
94
94
|
/**
|
|
95
|
-
* Placement hint for
|
|
95
|
+
* Placement hint for the default directional animation.
|
|
96
96
|
*
|
|
97
97
|
* When `shouldAnimate` is `true`, placement is used to set CSS custom
|
|
98
98
|
* properties (like `--ds-popover-tx`, `--ds-popover-ty`) that control the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/top-layer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
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",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"@atlaskit/ds-lib": "^8.0.0",
|
|
41
41
|
"@atlaskit/layering": "^5.0.0",
|
|
42
42
|
"@atlaskit/react-compiler-gating": "^0.2.0",
|
|
43
|
-
"@atlaskit/tokens": "^16.
|
|
43
|
+
"@atlaskit/tokens": "^16.7.0",
|
|
44
44
|
"@babel/runtime": "^7.0.0",
|
|
45
|
-
"@compiled/react": "^1.0.
|
|
45
|
+
"@compiled/react": "^1.0.2",
|
|
46
46
|
"bind-event-listener": "^3.0.0",
|
|
47
47
|
"raf-schd": "^4.0.3"
|
|
48
48
|
},
|
|
@@ -68,33 +68,6 @@
|
|
|
68
68
|
"react-dom": "^19.0.0",
|
|
69
69
|
"tiny-invariant": "^1.2.0"
|
|
70
70
|
},
|
|
71
|
-
"techstack": {
|
|
72
|
-
"@atlassian/frontend": {
|
|
73
|
-
"import-structure": [
|
|
74
|
-
"atlassian-conventions"
|
|
75
|
-
],
|
|
76
|
-
"circular-dependencies": [
|
|
77
|
-
"file-and-folder-level"
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
|
-
"@repo/internal": {
|
|
81
|
-
"dom-events": "use-bind-event-listener",
|
|
82
|
-
"ui-components": [
|
|
83
|
-
"lite-mode"
|
|
84
|
-
],
|
|
85
|
-
"design-tokens": [
|
|
86
|
-
"color",
|
|
87
|
-
"spacing"
|
|
88
|
-
],
|
|
89
|
-
"deprecation": [
|
|
90
|
-
"no-deprecated-imports"
|
|
91
|
-
],
|
|
92
|
-
"styling": [
|
|
93
|
-
"static",
|
|
94
|
-
"compiled"
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
71
|
"publishConfig": {
|
|
99
72
|
"registry": "https://registry.npmjs.org/"
|
|
100
73
|
}
|
package/animations/package.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@atlaskit/top-layer/animations",
|
|
3
|
-
"main": "../dist/cjs/entry-points/animations.js",
|
|
4
|
-
"module": "../dist/esm/entry-points/animations.js",
|
|
5
|
-
"module:es2019": "../dist/es2019/entry-points/animations.js",
|
|
6
|
-
"sideEffects": [
|
|
7
|
-
"*.compiled.css"
|
|
8
|
-
],
|
|
9
|
-
"types": "../dist/types/entry-points/animations.d.ts"
|
|
10
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.roleToAriaHasPopup = roleToAriaHasPopup;
|
|
7
|
-
/**
|
|
8
|
-
* Maps a popover content role to the correct `aria-haspopup` value
|
|
9
|
-
* for its trigger element.
|
|
10
|
-
*
|
|
11
|
-
* Returns `undefined` for roles that are not popups in the ARIA sense
|
|
12
|
-
* (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
|
|
13
|
-
* should not announce a popup at all - emitting `aria-haspopup="dialog"`
|
|
14
|
-
* on a tooltip trigger would mislead assistive technology.
|
|
15
|
-
*/
|
|
16
|
-
function roleToAriaHasPopup(_ref) {
|
|
17
|
-
var role = _ref.role;
|
|
18
|
-
if (role === 'menu') {
|
|
19
|
-
return 'menu';
|
|
20
|
-
}
|
|
21
|
-
if (role === 'listbox') {
|
|
22
|
-
return 'listbox';
|
|
23
|
-
}
|
|
24
|
-
if (role === 'tree') {
|
|
25
|
-
return 'tree';
|
|
26
|
-
}
|
|
27
|
-
if (role === 'grid') {
|
|
28
|
-
return 'grid';
|
|
29
|
-
}
|
|
30
|
-
if (role === 'dialog' || role === 'alertdialog') {
|
|
31
|
-
return 'dialog';
|
|
32
|
-
}
|
|
33
|
-
// Non-popup roles (tooltip, note, status, alert, log) and the
|
|
34
|
-
// no-role case: omit `aria-haspopup` entirely. React drops the
|
|
35
|
-
// attribute when the value is `undefined`.
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|