@atlaskit/top-layer 0.13.0 → 0.14.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 +15 -0
- package/README.md +61 -34
- package/dist/cjs/animations/presets.js +4 -4
- package/dist/cjs/entry-points/get-aria-for-trigger.js +12 -0
- package/dist/cjs/entry-points/popover-surface.js +12 -0
- package/dist/cjs/entry-points/use-popover-id.js +21 -0
- package/dist/cjs/internal/get-aria-for-trigger.js +88 -0
- package/dist/cjs/internal/role-types.js +64 -1
- package/dist/cjs/internal/slot.js +1 -1
- package/dist/cjs/internal/use-anchor-position.js +30 -14
- package/dist/cjs/popover/popover.js +79 -80
- package/dist/cjs/popover-surface/index.js +12 -0
- package/dist/cjs/{popup/popup-surface.js → popover-surface/popover-surface.js} +9 -12
- package/dist/cjs/testing/polyfill.js +9 -6
- package/dist/es2019/animations/presets.js +4 -4
- package/dist/es2019/entry-points/get-aria-for-trigger.js +1 -0
- package/dist/es2019/entry-points/popover-surface.js +1 -0
- package/dist/es2019/entry-points/use-popover-id.js +15 -0
- package/dist/es2019/internal/get-aria-for-trigger.js +84 -0
- package/dist/es2019/internal/role-types.js +59 -0
- package/dist/es2019/internal/slot.js +1 -1
- package/dist/es2019/internal/use-anchor-position.js +16 -12
- package/dist/es2019/popover/popover.js +79 -80
- package/dist/es2019/popover-surface/index.js +1 -0
- package/dist/es2019/popover-surface/popover-surface.js +27 -0
- package/dist/es2019/testing/polyfill.js +9 -6
- package/dist/esm/animations/presets.js +4 -4
- package/dist/esm/entry-points/get-aria-for-trigger.js +1 -0
- package/dist/esm/entry-points/popover-surface.js +1 -0
- package/dist/esm/entry-points/use-popover-id.js +15 -0
- package/dist/esm/internal/get-aria-for-trigger.js +83 -0
- package/dist/esm/internal/role-types.js +58 -0
- package/dist/esm/internal/slot.js +1 -1
- package/dist/esm/internal/use-anchor-position.js +30 -14
- package/dist/esm/popover/popover.js +79 -80
- package/dist/esm/popover-surface/index.js +1 -0
- package/dist/esm/popover-surface/popover-surface.js +26 -0
- package/dist/esm/testing/polyfill.js +9 -6
- package/dist/types/animations/presets.d.ts +4 -4
- package/dist/types/animations/types.d.ts +1 -1
- package/dist/types/dialog/types.d.ts +0 -8
- package/dist/types/entry-points/get-aria-for-trigger.d.ts +1 -0
- package/dist/types/entry-points/popover-surface.d.ts +2 -0
- package/dist/types/entry-points/popover.d.ts +1 -1
- package/dist/types/entry-points/use-popover-id.d.ts +11 -0
- package/dist/types/internal/get-aria-for-trigger.d.ts +86 -0
- package/dist/types/internal/role-types.d.ts +7 -0
- package/dist/types/internal/slot.d.ts +2 -2
- package/dist/types/popover/index.d.ts +1 -1
- package/dist/types/popover/popover.d.ts +6 -13
- package/dist/types/popover/types.d.ts +6 -5
- package/dist/types/popover-surface/index.d.ts +2 -0
- package/dist/types/popover-surface/popover-surface.d.ts +21 -0
- package/dist/types-ts4.5/animations/presets.d.ts +4 -4
- package/dist/types-ts4.5/animations/types.d.ts +1 -1
- package/dist/types-ts4.5/dialog/types.d.ts +0 -8
- package/dist/types-ts4.5/entry-points/get-aria-for-trigger.d.ts +1 -0
- package/dist/types-ts4.5/entry-points/popover-surface.d.ts +2 -0
- package/dist/types-ts4.5/entry-points/popover.d.ts +1 -1
- package/dist/types-ts4.5/entry-points/use-popover-id.d.ts +11 -0
- package/dist/types-ts4.5/internal/get-aria-for-trigger.d.ts +86 -0
- package/dist/types-ts4.5/internal/role-types.d.ts +7 -0
- package/dist/types-ts4.5/internal/slot.d.ts +2 -2
- package/dist/types-ts4.5/popover/index.d.ts +1 -1
- package/dist/types-ts4.5/popover/popover.d.ts +6 -13
- package/dist/types-ts4.5/popover/types.d.ts +6 -5
- package/dist/types-ts4.5/popover-surface/index.d.ts +2 -0
- package/dist/types-ts4.5/popover-surface/popover-surface.d.ts +21 -0
- package/get-aria-for-trigger/package.json +17 -0
- package/package.json +3 -3
- package/popover-surface/package.json +17 -0
- package/use-popover-id/package.json +17 -0
- package/dist/cjs/entry-points/popup-surface.js +0 -12
- package/dist/cjs/entry-points/popup.js +0 -12
- package/dist/cjs/popup/index.js +0 -37
- package/dist/cjs/popup/popup-content.js +0 -215
- package/dist/cjs/popup/popup-context.js +0 -25
- package/dist/cjs/popup/popup-provider.js +0 -15
- package/dist/cjs/popup/popup-trigger-function.js +0 -80
- package/dist/cjs/popup/popup-trigger.js +0 -66
- package/dist/cjs/popup/popup.js +0 -81
- package/dist/cjs/popup/types.js +0 -5
- package/dist/cjs/popup/use-maybe-popup-context.js +0 -17
- package/dist/cjs/popup/use-popup-context.js +0 -20
- package/dist/es2019/entry-points/popup-surface.js +0 -1
- package/dist/es2019/entry-points/popup.js +0 -1
- package/dist/es2019/popup/index.js +0 -33
- package/dist/es2019/popup/popup-content.js +0 -207
- package/dist/es2019/popup/popup-context.js +0 -20
- package/dist/es2019/popup/popup-provider.js +0 -10
- package/dist/es2019/popup/popup-surface.js +0 -30
- package/dist/es2019/popup/popup-trigger-function.js +0 -75
- package/dist/es2019/popup/popup-trigger.js +0 -61
- package/dist/es2019/popup/popup.js +0 -67
- package/dist/es2019/popup/types.js +0 -1
- package/dist/es2019/popup/use-maybe-popup-context.js +0 -12
- package/dist/es2019/popup/use-popup-context.js +0 -15
- package/dist/esm/entry-points/popup-surface.js +0 -1
- package/dist/esm/entry-points/popup.js +0 -1
- package/dist/esm/popup/index.js +0 -33
- package/dist/esm/popup/popup-content.js +0 -207
- package/dist/esm/popup/popup-context.js +0 -20
- package/dist/esm/popup/popup-provider.js +0 -10
- package/dist/esm/popup/popup-surface.js +0 -29
- package/dist/esm/popup/popup-trigger-function.js +0 -75
- package/dist/esm/popup/popup-trigger.js +0 -59
- package/dist/esm/popup/popup.js +0 -72
- package/dist/esm/popup/types.js +0 -1
- package/dist/esm/popup/use-maybe-popup-context.js +0 -12
- package/dist/esm/popup/use-popup-context.js +0 -15
- package/dist/types/entry-points/popup-surface.d.ts +0 -2
- package/dist/types/entry-points/popup.d.ts +0 -2
- package/dist/types/popup/index.d.ts +0 -34
- package/dist/types/popup/popup-content.d.ts +0 -14
- package/dist/types/popup/popup-context.d.ts +0 -96
- package/dist/types/popup/popup-provider.d.ts +0 -10
- package/dist/types/popup/popup-surface.d.ts +0 -22
- package/dist/types/popup/popup-trigger-function.d.ts +0 -112
- package/dist/types/popup/popup-trigger.d.ts +0 -14
- package/dist/types/popup/popup.d.ts +0 -14
- package/dist/types/popup/types.d.ts +0 -321
- package/dist/types/popup/use-maybe-popup-context.d.ts +0 -8
- package/dist/types/popup/use-popup-context.d.ts +0 -7
- package/dist/types-ts4.5/entry-points/popup-surface.d.ts +0 -2
- package/dist/types-ts4.5/entry-points/popup.d.ts +0 -2
- package/dist/types-ts4.5/popup/index.d.ts +0 -34
- package/dist/types-ts4.5/popup/popup-content.d.ts +0 -14
- package/dist/types-ts4.5/popup/popup-context.d.ts +0 -96
- package/dist/types-ts4.5/popup/popup-provider.d.ts +0 -10
- package/dist/types-ts4.5/popup/popup-surface.d.ts +0 -22
- package/dist/types-ts4.5/popup/popup-trigger-function.d.ts +0 -112
- package/dist/types-ts4.5/popup/popup-trigger.d.ts +0 -14
- package/dist/types-ts4.5/popup/popup.d.ts +0 -14
- package/dist/types-ts4.5/popup/types.d.ts +0 -321
- package/dist/types-ts4.5/popup/use-maybe-popup-context.d.ts +0 -8
- package/dist/types-ts4.5/popup/use-popup-context.d.ts +0 -7
- package/popup/package.json +0 -17
- package/popup-surface/package.json +0 -17
- /package/dist/cjs/{popup/popup-surface.compiled.css → popover-surface/popover-surface.compiled.css} +0 -0
- /package/dist/es2019/{popup/popup-surface.compiled.css → popover-surface/popover-surface.compiled.css} +0 -0
- /package/dist/esm/{popup/popup-surface.compiled.css → popover-surface/popover-surface.compiled.css} +0 -0
|
@@ -17,14 +17,13 @@ var _bindEventListener = require("bind-event-listener");
|
|
|
17
17
|
var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
|
|
18
18
|
var _once = _interopRequireDefault(require("@atlaskit/ds-lib/once"));
|
|
19
19
|
var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
|
|
20
|
+
var _roleTypes = require("../internal/role-types");
|
|
20
21
|
var _useAnimatedVisibility = require("../internal/use-animated-visibility");
|
|
21
22
|
var _useFocusWrap = require("../internal/use-focus-wrap");
|
|
22
23
|
var _useInitialFocus = require("../internal/use-initial-focus");
|
|
23
24
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
24
25
|
/**
|
|
25
|
-
* Detects
|
|
26
|
-
* Uses DOM reflection: unsupported browsers reflect the invalid value default ("manual").
|
|
27
|
-
* Safe for SSR. Result is cached via `once`.
|
|
26
|
+
* Detects `popover="hint"` support via DOM reflection. SSR-safe, cached.
|
|
28
27
|
*/
|
|
29
28
|
var supportsPopoverHint = (0, _once.default)(function () {
|
|
30
29
|
if (typeof document === 'undefined') {
|
|
@@ -41,33 +40,20 @@ var styles = {
|
|
|
41
40
|
root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28"
|
|
42
41
|
};
|
|
43
42
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* and dismissed when closeLayers() is called (e.g. when the side nav resizes).
|
|
47
|
-
*
|
|
48
|
-
* Note on open layer observer types not included here:
|
|
49
|
-
* - 'modal' only ever comes from the Dialog primitive, not Popover.
|
|
50
|
-
* - Other layer types (e.g. 'tooltip') do not yet exist in LayerType because
|
|
51
|
-
* there is no current use case for distinguishing them in the observer.
|
|
43
|
+
* Roles registered with the open layer observer as `popup`, so `closeLayers()`
|
|
44
|
+
* dismisses them. `modal` comes from Dialog only; other layer types are unused.
|
|
52
45
|
*/
|
|
53
46
|
var POPUP_ROLES = new Set(['menu', 'listbox', 'dialog', 'alertdialog', 'tree', 'grid']);
|
|
54
47
|
|
|
55
48
|
/**
|
|
56
|
-
* Unopinionated top-layer primitive.
|
|
57
|
-
*
|
|
58
|
-
* Manages only visibility (isOpen) and animation (animate). Has no knowledge
|
|
59
|
-
* of positioning - compose with `useAnchorPosition` for anchor positioning
|
|
60
|
-
* and optionally `useWidthFromAnchor` for setting the width of a popover element
|
|
61
|
-
* relative to its anchor trigger.
|
|
49
|
+
* Unopinionated top-layer primitive. Manages visibility and animation only;
|
|
50
|
+
* compose with `useAnchorPosition` / `useWidthFromAnchor` for positioning.
|
|
62
51
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* - `isOpen={false}` → `hidePopover()`, exit animation via `allow-discrete`
|
|
52
|
+
* `isOpen={true}` calls `showPopover()` (entry via `@starting-style`);
|
|
53
|
+
* `isOpen={false}` calls `hidePopover()` (exit via `allow-discrete`).
|
|
66
54
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* consumer must set `isOpen` to `false`. The DOM owns the dismiss; React
|
|
70
|
-
* must follow.
|
|
55
|
+
* In `mode="auto"`, the browser can light-dismiss (Escape, click outside).
|
|
56
|
+
* `onClose` fires; the consumer must then set `isOpen` to `false`.
|
|
71
57
|
*/
|
|
72
58
|
var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Popover(_ref, ref) {
|
|
73
59
|
var children = _ref.children,
|
|
@@ -89,11 +75,9 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
89
75
|
var autoId = (0, _react.useId)();
|
|
90
76
|
var ownRef = (0, _react.useRef)(null);
|
|
91
77
|
var combinedRef = (0, _mergeRefs.default)([ownRef, ref].filter(Boolean));
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// (
|
|
95
|
-
// attributes. React 19.2.0 replaced colons with underscores, making this
|
|
96
|
-
// stripping unnecessary once React 18 support is dropped.
|
|
78
|
+
// React 18 `useId()` returns IDs with colons (invalid in CSS selectors
|
|
79
|
+
// and popover target attributes). Strip them; remove once React 18 is
|
|
80
|
+
// dropped (React 19.2 uses underscores).
|
|
97
81
|
var popoverId = idProp !== null && idProp !== void 0 ? idProp : "popover-".concat(autoId.replace(/:/g, ''));
|
|
98
82
|
var _useAnimatedVisibilit = (0, _useAnimatedVisibility.useAnimatedVisibility)({
|
|
99
83
|
isOpen: isOpen,
|
|
@@ -105,12 +89,10 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
105
89
|
showChildren = _useAnimatedVisibilit.showChildren,
|
|
106
90
|
preset = _useAnimatedVisibilit.preset;
|
|
107
91
|
|
|
108
|
-
// Focus management
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// tracks `previouslyFocusedElement` on show and restores it on hide.
|
|
113
|
-
// No custom restoration hook is needed. See: notes/architecture/focus-restoration.md
|
|
92
|
+
// Focus management: initial focus on open (role-dependent), Tab cycling
|
|
93
|
+
// for dialog roles. Restoration is native for outermost popovers; nested
|
|
94
|
+
// focus-capturing roles are handled below via `beforetoggle` snapshot.
|
|
95
|
+
// See: notes/architecture/focus-restoration.md
|
|
114
96
|
(0, _useFocusWrap.useFocusWrap)({
|
|
115
97
|
elementRef: ownRef,
|
|
116
98
|
role: role
|
|
@@ -121,13 +103,9 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
121
103
|
role: role
|
|
122
104
|
});
|
|
123
105
|
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
// (
|
|
127
|
-
//
|
|
128
|
-
// Only set type: 'popup' for roles that are interactive overlay popups.
|
|
129
|
-
// Passive/informational roles (tooltip, note, status, alert, log) and
|
|
130
|
-
// no-role cases do not map to LayerType 'popup', so type is left undefined.
|
|
106
|
+
// Register with open layer observer so `closeLayers()` and open-count
|
|
107
|
+
// subscriptions work. Only popup-like roles register as `popup`; passive
|
|
108
|
+
// roles (tooltip, status, etc.) leave `type` undefined.
|
|
131
109
|
var handleObserverClose = (0, _react.useCallback)(function () {
|
|
132
110
|
onClose === null || onClose === void 0 || onClose({
|
|
133
111
|
reason: 'programmatic'
|
|
@@ -139,25 +117,29 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
139
117
|
onClose: handleObserverClose
|
|
140
118
|
});
|
|
141
119
|
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// However, there is a behavioral difference: "auto" participates in the
|
|
147
|
-
// auto-dismiss stack and WILL close other "auto" popovers when it opens
|
|
148
|
-
// (e.g. a tooltip opening will close an open dropdown). "hint" does not
|
|
149
|
-
// have this side effect. This difference only affects browsers without
|
|
150
|
-
// popover="hint" support, and that set is shrinking rapidly.
|
|
120
|
+
// `hint` falls back to `auto` when unsupported: closest behavior (light
|
|
121
|
+
// dismiss works), but `auto` participates in the auto-dismiss stack so it
|
|
122
|
+
// will close other `auto` popovers when opened. Acceptable trade-off given
|
|
123
|
+
// shrinking browser set without `hint` support.
|
|
151
124
|
var mode = modeProp === 'hint' && !supportsPopoverHint() ? 'auto' : modeProp;
|
|
152
125
|
|
|
153
|
-
//
|
|
154
|
-
// When we call hidePopover() ourselves the beforetoggle event fires synchronously,
|
|
155
|
-
// so this ref prevents the toggle handler from calling onClose redundantly.
|
|
126
|
+
// Prevents toggle handler from calling onClose for our own hidePopover() calls.
|
|
156
127
|
var programmaticCloseRef = (0, _react.useRef)(false);
|
|
157
128
|
|
|
158
|
-
//
|
|
159
|
-
// the browser processes light dismiss), read in the toggle handler.
|
|
129
|
+
// Set in capture-phase keydown so the toggle handler knows close reason.
|
|
160
130
|
var closeReasonRef = (0, _react.useRef)('light-dismiss');
|
|
131
|
+
|
|
132
|
+
// Snapshot of pre-open focus, used to restore focus for nested popovers
|
|
133
|
+
// with focus-capturing roles (browser only restores the outermost).
|
|
134
|
+
var previouslyFocusedElementRef = (0, _react.useRef)(null);
|
|
135
|
+
var handleBeforeToggle = (0, _react.useCallback)(function (event) {
|
|
136
|
+
if (event.newState !== 'open') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- need active element snapshot
|
|
140
|
+
var active = document.activeElement;
|
|
141
|
+
previouslyFocusedElementRef.current = active instanceof HTMLElement ? active : null;
|
|
142
|
+
}, []);
|
|
161
143
|
var handleToggle = (0, _react.useCallback)(function (event) {
|
|
162
144
|
var element = ownRef.current;
|
|
163
145
|
if (event.newState === 'open') {
|
|
@@ -175,32 +157,52 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
175
157
|
isOpen: false,
|
|
176
158
|
element: element
|
|
177
159
|
});
|
|
160
|
+
|
|
161
|
+
// Nested-popover focus restoration fallback (browser only
|
|
162
|
+
// restores outermost; Firefox skips nested entirely).
|
|
163
|
+
// Restore only when the role moved focus in on open AND the
|
|
164
|
+
// close was Escape/programmatic. For light dismiss, the
|
|
165
|
+
// click target keeps focus per HTML spec
|
|
166
|
+
// (`focusPreviousElement=false`).
|
|
167
|
+
var previouslyFocused = previouslyFocusedElementRef.current;
|
|
168
|
+
var _reason = closeReasonRef.current;
|
|
169
|
+
var isProgrammatic = programmaticCloseRef.current;
|
|
170
|
+
var shouldRestore = previouslyFocused !== null && previouslyFocused.isConnected && (0, _roleTypes.shouldFocusIntoPopover)({
|
|
171
|
+
role: role
|
|
172
|
+
}) && (_reason === 'escape' || isProgrammatic);
|
|
173
|
+
if (shouldRestore && previouslyFocused) {
|
|
174
|
+
previouslyFocused.focus({
|
|
175
|
+
preventScroll: true
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
previouslyFocusedElementRef.current = null;
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
// Programmatic close
|
|
181
|
+
// Programmatic close: consumer already knows.
|
|
181
182
|
if (programmaticCloseRef.current) {
|
|
183
|
+
// Reset reason so a stale 'escape' from a race with Escape keydown
|
|
184
|
+
// does not corrupt the next browser-dismiss cycle.
|
|
185
|
+
closeReasonRef.current = 'light-dismiss';
|
|
182
186
|
return;
|
|
183
187
|
}
|
|
184
188
|
|
|
185
|
-
// Browser dismiss (Escape
|
|
189
|
+
// Browser dismiss (Escape/click-outside).
|
|
186
190
|
var reason = closeReasonRef.current;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// `manual
|
|
190
|
-
// internally by `PopupContent` - the optional check guards
|
|
191
|
-
// the manual-mode forwarded path where the field is undefined.
|
|
191
|
+
// reset for next dismiss
|
|
192
|
+
closeReasonRef.current = 'light-dismiss';
|
|
193
|
+
// Optional: `manual` mode has no `onClose` in the forwarded type.
|
|
192
194
|
onClose === null || onClose === void 0 || onClose({
|
|
193
195
|
reason: reason
|
|
194
196
|
});
|
|
195
197
|
}
|
|
196
|
-
}, [onClose, onOpenChange]);
|
|
198
|
+
}, [onClose, onOpenChange, role]);
|
|
197
199
|
(0, _react.useEffect)(function () {
|
|
198
200
|
var element = ownRef.current;
|
|
199
201
|
if (!element) {
|
|
200
202
|
return;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
|
-
//
|
|
205
|
+
// Tag Escape before the browser processes light dismiss.
|
|
204
206
|
var unbindEscape = (0, _bindEventListener.bind)(element, {
|
|
205
207
|
type: 'keydown',
|
|
206
208
|
listener: function listener(event) {
|
|
@@ -216,15 +218,20 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
216
218
|
type: 'toggle',
|
|
217
219
|
listener: handleToggle
|
|
218
220
|
});
|
|
221
|
+
// Snapshot `document.activeElement` before `useInitialFocus` moves it.
|
|
222
|
+
var unbindBeforeToggle = (0, _bindEventListener.bind)(element, {
|
|
223
|
+
type: 'beforetoggle',
|
|
224
|
+
listener: handleBeforeToggle
|
|
225
|
+
});
|
|
219
226
|
return function () {
|
|
220
227
|
unbindEscape();
|
|
221
228
|
unbindToggle();
|
|
229
|
+
unbindBeforeToggle();
|
|
222
230
|
};
|
|
223
|
-
}, [handleToggle]);
|
|
231
|
+
}, [handleToggle, handleBeforeToggle]);
|
|
224
232
|
|
|
225
|
-
// Placement-dependent animation CSS vars
|
|
226
|
-
//
|
|
227
|
-
// (e.g. inline `animate={slideAndFade()}`) do not re-trigger visibility.
|
|
233
|
+
// Placement-dependent animation CSS vars. Kept separate from show/hide
|
|
234
|
+
// so preset reference changes do not re-trigger visibility.
|
|
228
235
|
(0, _react.useLayoutEffect)(function () {
|
|
229
236
|
var el = ownRef.current;
|
|
230
237
|
if (!el || !(preset !== null && preset !== void 0 && preset.getProperties) || !placement) {
|
|
@@ -241,14 +248,9 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
241
248
|
});
|
|
242
249
|
}, [preset, placement]);
|
|
243
250
|
|
|
244
|
-
// Show/hide based on isOpen.
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
// effect ran).
|
|
248
|
-
// Defensive try/catch: these APIs throw InvalidStateError if the element
|
|
249
|
-
// is disconnected or missing the popover attribute. While we do not expect
|
|
250
|
-
// that to happen in normal usage, it can occur in edge cases (e.g. React
|
|
251
|
-
// concurrent mode, StrictMode double-effects, or unmount races).
|
|
251
|
+
// Show/hide based on isOpen. `showPopover`/`hidePopover` are no-ops when
|
|
252
|
+
// already in the target state. Try/catch guards `InvalidStateError` on
|
|
253
|
+
// disconnected elements (StrictMode/concurrent/unmount edge cases).
|
|
252
254
|
(0, _react.useLayoutEffect)(function () {
|
|
253
255
|
var el = ownRef.current;
|
|
254
256
|
if (!el) {
|
|
@@ -256,20 +258,17 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
|
|
|
256
258
|
}
|
|
257
259
|
if (isOpen) {
|
|
258
260
|
programmaticCloseRef.current = false;
|
|
259
|
-
// Defensive: element may be disconnected or in an unexpected state.
|
|
260
261
|
try {
|
|
261
262
|
el.showPopover();
|
|
262
263
|
} catch (_unused) {}
|
|
263
264
|
return function () {
|
|
264
265
|
programmaticCloseRef.current = true;
|
|
265
|
-
// Defensive: element may be disconnected or in an unexpected state.
|
|
266
266
|
try {
|
|
267
267
|
el.hidePopover();
|
|
268
268
|
} catch (_unused2) {}
|
|
269
269
|
};
|
|
270
270
|
}
|
|
271
271
|
programmaticCloseRef.current = true;
|
|
272
|
-
// Defensive: element may be disconnected or in an unexpected state.
|
|
273
272
|
try {
|
|
274
273
|
el.hidePopover();
|
|
275
274
|
} catch (_unused3) {}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "PopoverSurface", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _popoverSurface.PopoverSurface;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _popoverSurface = require("./popover-surface");
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* popover-surface.tsx generated by @compiled/babel-plugin v0.39.1 */
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.
|
|
9
|
-
require("./
|
|
8
|
+
exports.PopoverSurface = PopoverSurface;
|
|
9
|
+
require("./popover-surface.compiled.css");
|
|
10
10
|
var _runtime = require("@compiled/react/runtime");
|
|
11
11
|
var _react = _interopRequireDefault(require("react"));
|
|
12
12
|
var styles = {
|
|
@@ -14,21 +14,18 @@ var styles = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Optional styled wrapper for
|
|
17
|
+
* Optional styled wrapper for popover content.
|
|
18
18
|
*
|
|
19
19
|
* Adds default visual styling (background, border-radius, box-shadow)
|
|
20
|
-
* using design tokens.
|
|
21
|
-
* should use `Popup.Content` without wrapping in `PopupSurface`.
|
|
20
|
+
* using design tokens.
|
|
22
21
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* **Presentational primitive - `children`-only by design.** This component
|
|
22
|
+
* Presentational primitive - `children`-only by design. This component
|
|
26
23
|
* intentionally exposes no `style`, `className`, `xcss`, `id`, or `ref`
|
|
27
24
|
* props. If you need any of those, render your own wrapper element inside
|
|
28
|
-
* `
|
|
29
|
-
*
|
|
25
|
+
* `Popover` instead. `PopoverSurface` is the no-config "give me the default
|
|
26
|
+
* ADS overlay look" surface, nothing more.
|
|
30
27
|
*/
|
|
31
|
-
function
|
|
28
|
+
function PopoverSurface(_ref) {
|
|
32
29
|
var children = _ref.children;
|
|
33
30
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
34
31
|
className: (0, _runtime.ax)([styles.root])
|
|
@@ -104,7 +104,7 @@ function createToggleScheduler() {
|
|
|
104
104
|
newState: newState
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
// Task queue (setTimeout)
|
|
107
|
+
// Task queue (setTimeout): browser fires toggle after microtasks drain, not during
|
|
108
108
|
setTimeout(function () {
|
|
109
109
|
var entry = pending.get(element);
|
|
110
110
|
if (!entry) {
|
|
@@ -534,10 +534,11 @@ if (typeof HTMLElement !== 'undefined') {
|
|
|
534
534
|
element: this
|
|
535
535
|
}), " is not connected to the document. Ensure the element has been added to the DOM (e.g. via render() or document.body.appendChild()) before calling showPopover()."), 'InvalidStateError');
|
|
536
536
|
}
|
|
537
|
+
// No-op: already showing.
|
|
537
538
|
if (isPopoverOpen({
|
|
538
539
|
element: this
|
|
539
540
|
})) {
|
|
540
|
-
return;
|
|
541
|
+
return;
|
|
541
542
|
}
|
|
542
543
|
|
|
543
544
|
// sync, cancelable on show only
|
|
@@ -731,8 +732,9 @@ if (typeof HTMLDialogElement !== 'undefined') {
|
|
|
731
732
|
}), " is not connected to the document. Ensure the element has been added to the DOM (e.g. via render() or document.body.appendChild()) before calling showModal()."), 'InvalidStateError');
|
|
732
733
|
}
|
|
733
734
|
if (this.hasAttribute('open')) {
|
|
735
|
+
// No-op: already modal.
|
|
734
736
|
if (this._isModal) {
|
|
735
|
-
return;
|
|
737
|
+
return;
|
|
736
738
|
}
|
|
737
739
|
throw new DOMException("Failed to execute 'showModal' on 'HTMLDialogElement': ".concat(describeElement({
|
|
738
740
|
element: this
|
|
@@ -756,7 +758,7 @@ if (typeof HTMLDialogElement !== 'undefined') {
|
|
|
756
758
|
}
|
|
757
759
|
if (typeof proto.show !== 'function') {
|
|
758
760
|
proto.show = function show() {
|
|
759
|
-
// Browser does not throw for disconnected elements on show
|
|
761
|
+
// Browser does not throw for disconnected elements on show; no-op.
|
|
760
762
|
if (!this.isConnected || this.hasAttribute('open')) {
|
|
761
763
|
return;
|
|
762
764
|
}
|
|
@@ -778,11 +780,12 @@ if (typeof HTMLDialogElement !== 'undefined') {
|
|
|
778
780
|
}
|
|
779
781
|
if (typeof proto.close !== 'function') {
|
|
780
782
|
proto.close = function close(returnValue) {
|
|
783
|
+
// No-op: already closed.
|
|
781
784
|
if (!this.hasAttribute('open')) {
|
|
782
|
-
return;
|
|
785
|
+
return;
|
|
783
786
|
}
|
|
784
787
|
|
|
785
|
-
// Sync beforetoggle (not cancelable)
|
|
788
|
+
// Sync beforetoggle (not cancelable); matches browser behavior.
|
|
786
789
|
fireToggleEvent({
|
|
787
790
|
element: this,
|
|
788
791
|
type: 'beforetoggle',
|
|
@@ -100,10 +100,10 @@ const SLIDE_AND_FADE_CSS = `
|
|
|
100
100
|
* import { slideAndFade } from '@atlaskit/top-layer/animations';
|
|
101
101
|
*
|
|
102
102
|
* // Default 4px slide (tooltip)
|
|
103
|
-
* <
|
|
103
|
+
* <Popover animate={slideAndFade()} />
|
|
104
104
|
*
|
|
105
105
|
* // Custom 8px slide (popup / dropdown)
|
|
106
|
-
* <
|
|
106
|
+
* <Popover animate={slideAndFade({ distance: 8 })} />
|
|
107
107
|
* ```
|
|
108
108
|
*/
|
|
109
109
|
export function slideAndFade(options) {
|
|
@@ -162,7 +162,7 @@ const FADE_CSS = `
|
|
|
162
162
|
* ```tsx
|
|
163
163
|
* import { fade } from '@atlaskit/top-layer/animations';
|
|
164
164
|
*
|
|
165
|
-
* <
|
|
165
|
+
* <Popover animate={fade()} />
|
|
166
166
|
* ```
|
|
167
167
|
*/
|
|
168
168
|
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
@@ -218,7 +218,7 @@ const SCALE_AND_FADE_CSS = `
|
|
|
218
218
|
* ```tsx
|
|
219
219
|
* import { scaleAndFade } from '@atlaskit/top-layer/animations';
|
|
220
220
|
*
|
|
221
|
-
* <
|
|
221
|
+
* <Popover animate={scaleAndFade()} />
|
|
222
222
|
* ```
|
|
223
223
|
*/
|
|
224
224
|
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getAriaForTrigger } from '../internal/get-aria-for-trigger';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PopoverSurface } from '../popover-surface';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useId } from '@atlaskit/ds-lib/use-id';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a stable HTML `id` used to wire a trigger element to its popover
|
|
4
|
+
* via ARIA (`aria-controls`) and the Popover API (`popovertarget`).
|
|
5
|
+
*
|
|
6
|
+
* The returned value is intended for the `id` attribute on the popover and
|
|
7
|
+
* for the matching `aria-controls` / `popovertarget` references on the
|
|
8
|
+
* trigger. It is NOT a CSS `anchor-name` value: CSS anchor positioning is
|
|
9
|
+
* handled internally by `useAnchorPosition`, which mints and applies its
|
|
10
|
+
* own `anchor-name` on the trigger element.
|
|
11
|
+
*/
|
|
12
|
+
export function usePopoverId() {
|
|
13
|
+
// prefixing for debug clarity in the DOM
|
|
14
|
+
return `popover-${useId()}`;
|
|
15
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { roleToAriaHasPopup } from './role-to-aria-has-popup';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Roles accepted by `getAriaForTrigger`.
|
|
5
|
+
*
|
|
6
|
+
* Excludes tooltip-family roles (`tooltip`, `note`, `status`, `alert`, `log`)
|
|
7
|
+
* because the correct trigger wiring for those is `aria-describedby`, not
|
|
8
|
+
* `aria-haspopup` / `aria-expanded` / `aria-controls`. Use `aria-describedby`
|
|
9
|
+
* manually for tooltip triggers.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Non-undefined subset of `TAriaHasPopupValue`. Every role accepted by
|
|
14
|
+
* `getAriaForTrigger` maps to a concrete `aria-haspopup` string, so the
|
|
15
|
+
* trigger always receives a defined attribute value.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns the ARIA attributes to spread onto a click-activated popover trigger.
|
|
20
|
+
*
|
|
21
|
+
* Centralises three attributes that are easy to forget or get wrong individually:
|
|
22
|
+
*
|
|
23
|
+
* - `aria-haspopup` - derived from the popover's role (e.g. `dialog → "dialog"`,
|
|
24
|
+
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
25
|
+
* the value is `undefined`.
|
|
26
|
+
* - `aria-expanded` - reflects the current open state.
|
|
27
|
+
* - `aria-controls` - references the popover element by its id, even
|
|
28
|
+
* while the popover is closed. The relationship is stable; `aria-expanded`
|
|
29
|
+
* carries the current visibility state.
|
|
30
|
+
*
|
|
31
|
+
* **This function is for click/keyboard-activated popovers only.** For
|
|
32
|
+
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* import { getAriaForTrigger } from '@atlaskit/top-layer/get-aria-for-trigger';
|
|
37
|
+
* import { usePopoverId } from '@atlaskit/top-layer/use-popover-id';
|
|
38
|
+
*
|
|
39
|
+
* const popoverId = usePopoverId();
|
|
40
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
41
|
+
*
|
|
42
|
+
* return (
|
|
43
|
+
* <>
|
|
44
|
+
* <button
|
|
45
|
+
* ref={triggerRef}
|
|
46
|
+
* onClick={() => setIsOpen((previous) => !previous)}
|
|
47
|
+
* {...getAriaForTrigger({ role: 'dialog', isOpen, popoverId })}
|
|
48
|
+
* >
|
|
49
|
+
* Open
|
|
50
|
+
* </button>
|
|
51
|
+
* <Popover
|
|
52
|
+
* id={popoverId}
|
|
53
|
+
* role="dialog"
|
|
54
|
+
* label="My dialog"
|
|
55
|
+
* isOpen={isOpen}
|
|
56
|
+
* triggerRef={triggerRef}
|
|
57
|
+
* onClose={() => setIsOpen(false)}
|
|
58
|
+
* >
|
|
59
|
+
* …
|
|
60
|
+
* </Popover>
|
|
61
|
+
* </>
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function getAriaForTrigger({
|
|
66
|
+
role,
|
|
67
|
+
isOpen,
|
|
68
|
+
popoverId
|
|
69
|
+
}) {
|
|
70
|
+
// `roleToAriaHasPopup` accepts a wider role union (including tooltip-family
|
|
71
|
+
// roles that map to `undefined`). `TAriaForTriggerRole` excludes those, so
|
|
72
|
+
// the returned value is guaranteed to be a defined string. The cast narrows
|
|
73
|
+
// the wider helper signature to match.
|
|
74
|
+
const ariaHasPopup = roleToAriaHasPopup({
|
|
75
|
+
role
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
'aria-haspopup': ariaHasPopup,
|
|
79
|
+
'aria-expanded': isOpen,
|
|
80
|
+
// Keep the trigger-to-popover relationship stable. `aria-expanded`
|
|
81
|
+
// tells assistive technology whether the controlled popover is visible.
|
|
82
|
+
'aria-controls': popoverId
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
const 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({
|
|
53
|
+
role
|
|
54
|
+
}) {
|
|
55
|
+
if (!role) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return rolesThatMoveFocus.has(role);
|
|
59
|
+
}
|
|
@@ -43,7 +43,7 @@ export const Slot = /*#__PURE__*/forwardRef(function Slot(slotProps, ref) {
|
|
|
43
43
|
children: rawChildren,
|
|
44
44
|
...props
|
|
45
45
|
} = slotProps;
|
|
46
|
-
// `
|
|
46
|
+
// `SlotProps` intersects `Record<string, unknown>`, so `children` is typed as `unknown`.
|
|
47
47
|
const children = rawChildren;
|
|
48
48
|
if (!isSlotChild(children)) {
|
|
49
49
|
return children;
|