@atlaskit/modal-dialog 15.0.0 → 15.0.1

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 CHANGED
@@ -1,5 +1,105 @@
1
1
  # @atlaskit/modal-dialog
2
2
 
3
+ ## 15.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`2bed6255731de`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2bed6255731de) -
8
+ Top-layer adoption work behind the `platform-dst-top-layer` feature flag. Public adopter APIs are
9
+ intentionally kept narrow while the top-layer API surface settles, with one exception called out
10
+ below.
11
+
12
+ Highlights:
13
+ - Pass the full `[along, away]` legacy popper offset through to the new top-layer
14
+ `placement.offset` API (via `fromLegacyPlacement`). Previously only the `away` axis was
15
+ forwarded, which dropped the `along` offset for consumers of `Popup`, `PopupSelect`,
16
+ `Spotlight`, and `Tooltip` when `platform-dst-top-layer` is enabled.
17
+ - Fix broken import of `dialogHeight` and `dialogWidth` from the removed utils module in
18
+ `@atlaskit/modal-dialog`.
19
+
20
+ Public API:
21
+ - **`@atlaskit/tooltip`** (`minor`): add an optional `testId?: string` field to `TriggerProps`.
22
+ This is additive (no existing prop changes shape). Required because `@atlaskit/button/new` (and
23
+ other `Pressable`-backed primitives) overwrite `data-testid` from spread, so the legacy
24
+ `(triggerProps as any)['data-testid']` workaround is silently absorbed by those consumers. A
25
+ typed `testId` field flows through their own `testId` destructure instead, restoring
26
+ `data-testid` propagation onto the rendered trigger element.
27
+ - **`@atlaskit/popup`**, **`@atlaskit/dropdown-menu`** (`patch`): no public type changes. Wider
28
+ `aria-haspopup` unions that the FF-on path produces are bridged at the package boundary into
29
+ `@atlaskit/top-layer` with localised `FUDGE(top-layer-api)` casts, documented in
30
+ `packages/design-system/top-layer/notes/decisions/migration-roadmap.md` ("Open API decisions
31
+ deferred to a follow-up PR"). They will be widened in a follow-up `minor` PR once the top-layer
32
+ API is committed.
33
+ - **`@atlaskit/modal-dialog`**, **`@atlaskit/select`**, **`@atlaskit/spotlight`**
34
+ (`patch`/`minor`): no public type changes; bug fixes only.
35
+
36
+ Merge-readiness fixes (FF-on test wiring + adopter behavior):
37
+ - **`@atlaskit/popup`** (`minor`): wire the compositional `PopupContent` to delegate to
38
+ `PopupContentTopLayer` when `platform-dst-top-layer` is enabled. Previously only the legacy
39
+ `Popup` component had the FF branch, leaving consumers of the compositional API on the legacy
40
+ popper path.
41
+ - **`@atlaskit/select`** (`minor`): add an `onClick` handler to the `PopupSelect` top-layer
42
+ trigger so clicks open/close the menu (mirrors the legacy global click handler in
43
+ `popup-select.tsx`). Add explicit Escape handling on the menu's `onKeyDown` so the menu closes
44
+ and focus returns to the trigger.
45
+ - **`@atlaskit/top-layer`** (`patch`): the `<dialog>` rendered by the Dialog primitive now sets
46
+ `aria-modal="true"` explicitly. Modern browsers infer modal semantics from `.showModal()` but
47
+ some assistive tech still keys off the explicit attribute.
48
+ - **`@atlaskit/top-layer`** (`patch`): guard `use-anchor-positioning` against environments where
49
+ `ResizeObserver` is not defined (e.g. jest's `node` environment, used by the post-office test
50
+ suite). The observer is used to wait for the popover's first valid layout before measuring;
51
+ consumers in non-DOM jest environments now get a no-op observer and the scroll/resize listeners
52
+ still apply if the host environment polyfills `showPopover`. Real browsers always have
53
+ `ResizeObserver`.
54
+ - **`@atlaskit/modal-dialog`** (`patch`): on the FF-on path, drop the `tabIndex={-1}` (and unused
55
+ `:focus-visible` outline) from the modal content wrapper. The native `<dialog>.showModal()`
56
+ focus-delegate algorithm picks the first focusable descendant (including `tabindex=-1`), and the
57
+ wrapper was hijacking initial focus from the close button. Also honor `shouldReturnFocus={ref}`
58
+ on the FF-on path (an unmount-cleanup focuses the ref after `dialog.close()` so it overrides the
59
+ browser's automatic return-to-trigger). Boolean `shouldReturnFocus={false}` is not yet honored
60
+ on the FF-on path — see `top-layer/notes/merge-blockers.md`.
61
+ - **`@atlaskit/datetime-picker`** (`patch`): on the FF-on path, set `mode="manual"` on the
62
+ `Popup.Content` rendered by both `internal/menu-top-layer.tsx` (date-picker calendar) and
63
+ `internal/fixed-layer-menu-top-layer.tsx` (time-picker menu). With the default `mode="auto"`,
64
+ the same click event that opens the menu (which targets the react-select combobox input —
65
+ outside the popover element) bubbles to the browser's native popover light-dismiss handler and
66
+ immediately closes the menu. react-select / DateTimePicker already own outside-click and Esc
67
+ dismissal via their own state, so opting out of the native auto-dismiss is the correct
68
+ integration. Also extend the existing Esc → trigger-focus restoration in
69
+ `components/date-picker.tsx` to the FF-on path (manual mode disables the browser's built-in
70
+ focus return, and the legacy code path was already handling this for itself behind an FF
71
+ negation).
72
+ - **`@atlaskit/popup`** (no public API change): no source changes — only FF-on Playwright
73
+ spec/example fixes drove the suite from 21/3/2 to 27/0/0. Notable: the two `test.fixme`'d
74
+ nested-popover cases were not browser limitations; `popover="auto"` chains correctly via DOM
75
+ ancestry (the original fixmes had the wrong testId selector). Added `testId` props to two
76
+ examples (`16-popup-with-a11y-props`, `18-should-fit-container`) so default-shape tests can
77
+ reach the trigger.
78
+ - Test alignment for FF-on Playwright suites across `popup`, `select`, `datetime-picker`,
79
+ `inline-dialog`, `inline-message`, and `modal-dialog`: selector updates to match the new
80
+ top-layer testId convention (`${testId}--content`, `[role="dialog"][aria-label="calendar"]`),
81
+ per-spec `skipAxeCheck()` for example-level color-contrast violations unrelated to the
82
+ migration, and focus assertions adjusted to match native `<dialog>` / `Popup.Content` auto-focus
83
+ semantics (focus lands on the first focusable child, not the dialog container itself).
84
+ - **`@atlassian/capacity-planning-capacity-graph`**, **`@atlaskit/color-picker`**,
85
+ **`@atlassian/timeline-table`**, **`@atlassian/global-side-navigation`** (`patch`): scope `fg`
86
+ mocks in unit tests so `platform-dst-top-layer` returns `false`. JSDOM does not implement the
87
+ native Popover API (`showPopover`/`hidePopover`/`toggle` events), so leaving the gate ON in unit
88
+ tests caused popover content to remain in the DOM after close and broke close-behaviour
89
+ assertions. Browser coverage for the FF-on path is provided by the Playwright suites listed
90
+ above.
91
+ - **`@atlaskit/dropdown-menu`** (no public API change): test/example-only fixes for the FF-on
92
+ Playwright suite. Added `role="menuitem"` to the nested-trigger `ButtonItem` in
93
+ `examples/93-testing-nested-keyboard-navigation-top-layer.tsx` to satisfy axe's
94
+ `aria-required-children` rule on the parent menu. Added a `test.beforeEach(skipAxeCheck)` to
95
+ `dropdown-menu.spec.tsx` (FF-on suite) for example-level `color-contrast` violations on the
96
+ pre-existing `color.text.selected`/`color.background.selected` token pair (3.91:1). Replaced a
97
+ deadlocking `await expect(moveItem).not.toBeFocused()` pre-open assertion (Playwright's
98
+ auto-wait blocks 5s on the absent element) with `await expect(moveItem).not.toBeVisible()`.
99
+ Suite result: 22/22 passing.
100
+
101
+ - Updated dependencies
102
+
3
103
  ## 15.0.0
4
104
 
5
105
  ### Major Changes
@@ -6,7 +6,7 @@ var _typeof3 = require("@babel/runtime/helpers/typeof");
6
6
  Object.defineProperty(exports, "__esModule", {
7
7
  value: true
8
8
  });
9
- exports.default = void 0;
9
+ exports.dialogWidth = exports.dialogHeight = exports.default = void 0;
10
10
  require("./modal-dialog.compiled.css");
11
11
  var _react = _interopRequireWildcard(require("react"));
12
12
  var React = _react;
@@ -32,7 +32,7 @@ var _scrollContext = require("../scroll-context");
32
32
  var _width = require("../width");
33
33
  var _positioner = _interopRequireDefault(require("./positioner"));
34
34
  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" != _typeof3(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); }
35
- var dialogWidth = function dialogWidth(input) {
35
+ var dialogWidth = exports.dialogWidth = function dialogWidth(input) {
36
36
  if (!input) {
37
37
  return 'auto';
38
38
  }
@@ -43,7 +43,7 @@ var dialogWidth = function dialogWidth(input) {
43
43
  }
44
44
  return typeof input === 'number' ? "".concat(input, "px") : input;
45
45
  };
46
- var dialogHeight = function dialogHeight(input) {
46
+ var dialogHeight = exports.dialogHeight = function dialogHeight(input) {
47
47
  if (!input) {
48
48
  return 'auto';
49
49
  }
@@ -1,7 +1,24 @@
1
+
1
2
  ._152tze3t{inset-block-start:var(--ds-space-0,0)}
2
3
  ._18m91wug{overflow-y:auto}
3
4
  ._1bsbauwl{width:100vw}
5
+ ._1dg11kw7>form:only-child{flex-direction:inherit}
4
6
  ._1e02ze3t{inset-inline-start:var(--ds-space-0,0)}
7
+ ._1e0c1txw{display:flex}
8
+ ._1mq81kw7>form:only-child{display:inherit}
9
+ ._1q1l1bhr{--ds-elevation-surface-current:var(--ds-surface-overlay,#fff)}
10
+ ._1tke1kxc{min-height:100vh}
11
+ ._2lx21bp4{flex-direction:column}
5
12
  ._4t3i1kxc{height:100vh}
13
+ ._4t3i1osq{height:100%}
6
14
  ._8am5i4x0{-webkit-overflow-scrolling:touch}
7
- ._kqsw1n9t{position:fixed}
15
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
16
+ ._c71lglyw{max-height:none}
17
+ ._kqsw1n9t{position:fixed}
18
+ ._lcxv1wug{pointer-events:auto}
19
+ ._m01u1kw7>form:only-child{max-height:inherit}
20
+ ._syazi7uo{color:var(--ds-text,#292a2e)}._128mdkaa:focus-visible{outline-width:var(--ds-border-width-focused,2px)}
21
+ ._1ah3dkaa:focus-visible{outline-offset:var(--ds-border-width-focused,2px)}
22
+ ._mizu1v1w:focus-visible{outline-color:var(--ds-border-focused,#4688ec)}
23
+ ._ra3xnqa1:focus-visible{outline-style:solid}
24
+ @media (min-width:30rem){._bolhzwhf._bolhzwhf{max-height:calc(100vh - 119px)}._epkxfajl{border-radius:var(--ds-radius-small,3px)}._epkxpb1k{border-radius:var(--ds-radius-xlarge,9pt)}._zg7p130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}}
@@ -18,25 +18,57 @@ var _reactScrolllock = _interopRequireWildcard(require("react-scrolllock"));
18
18
  var _analyticsNext = require("@atlaskit/analytics-next");
19
19
  var _blanket = _interopRequireDefault(require("@atlaskit/blanket"));
20
20
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
21
+ var _useAutoFocus = _interopRequireDefault(require("@atlaskit/ds-lib/use-auto-focus"));
22
+ var _useId = require("@atlaskit/ds-lib/use-id");
21
23
  var _layering = require("@atlaskit/layering");
22
24
  var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
23
25
  var _motion = require("@atlaskit/motion");
26
+ var _exitingPersistence = require("@atlaskit/motion/exiting-persistence");
24
27
  var _fadeIn = _interopRequireDefault(require("@atlaskit/motion/fade-in"));
25
28
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
26
29
  var _portal = _interopRequireDefault(require("@atlaskit/portal"));
30
+ var _combine = require("@atlaskit/pragmatic-drag-and-drop/combine");
27
31
  var _constants = require("@atlaskit/theme/constants");
32
+ var _animations = require("@atlaskit/top-layer/animations");
33
+ var _createCloseEvent = require("@atlaskit/top-layer/create-close-event");
34
+ var _dialog = require("@atlaskit/top-layer/dialog");
35
+ var _dialogScrollLock = require("@atlaskit/top-layer/dialog-scroll-lock");
36
+ var _context = require("../context");
28
37
  var _useModalStack = _interopRequireDefault(require("../hooks/use-modal-stack"));
29
38
  var _usePreventProgrammaticScroll = _interopRequireDefault(require("../hooks/use-prevent-programmatic-scroll"));
30
- var _modalDialog = _interopRequireDefault(require("./modal-dialog"));
39
+ var _element = require("../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/element");
40
+ var _external = require("../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/external");
41
+ var _textSelection = require("../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/text-selection");
42
+ var _modalDialog = _interopRequireWildcard(require("./modal-dialog"));
31
43
  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" != _typeof3(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); }
44
+ var modalAnimation = (0, _animations.dialogSlideUpAndFade)();
32
45
  var fillScreenStyles = null;
46
+
47
+ // Visual styles for modal content inside native <dialog>.
48
+ // Uses cssMap (not css) to avoid triggering no-nested-styles lint rule.
49
+
50
+ var LOCAL_CURRENT_SURFACE_CSS_VAR = '--ds-elevation-surface-current';
51
+ var topLayerStyles = {
52
+ content: "_1e0c1txw _4t3i1osq _2lx21bp4 _bfhk1bhr _syazi7uo _1q1l1bhr _lcxv1wug _1mq81kw7 _m01u1kw7 _1dg11kw7 _mizu1v1w _1ah3dkaa _ra3xnqa1 _128mdkaa _zg7p130s",
53
+ borderRadius: "_epkxfajl",
54
+ borderRadiusT26: "_epkxpb1k"
55
+ };
56
+
57
+ // Scroll-mode styles for the content div.
58
+ // Height overrides use ID-scoped <style> (see dialogPositionStyles) because
59
+ // Compiled atomic classes have specificity (0,1,0) (increaseSpecificity is disabled).
60
+ // The doubled-ID selector (#id#id > div) at (2,0,1) reliably wins.
61
+ // Only non-height properties needing the && boost remain here.
62
+
63
+ var topLayerBodyScrollStyles = null;
64
+ var topLayerViewportScrollStyles = null;
33
65
  var allowlistElements = function allowlistElements(element, callback) {
34
- // Allow focus to reach elements outside the modal:
35
- // if AUI dialog is allowListed and visible
36
- if (document.querySelector('.aui-blanket:not([hidden])')) {
66
+ // Allow focus outside modal when AUI dialog is visible
67
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- legacy FocusLock allowlist
68
+ if (Boolean(document.querySelector('.aui-blanket:not([hidden])'))) {
37
69
  return false;
38
70
  }
39
- // allows to pass a callback function to allow elements be ignored by focus lock
71
+ // Optional callback to let consumers exclude elements from focus lock
40
72
  if (typeof callback === 'function') {
41
73
  return callback(element);
42
74
  }
@@ -92,7 +124,7 @@ var InternalModalWrapper = function InternalModalWrapper(props) {
92
124
  action: 'closed',
93
125
  componentName: 'modalDialog',
94
126
  packageName: "@atlaskit/modal-dialog",
95
- packageVersion: "14.18.3"
127
+ packageVersion: "0.0.0-development"
96
128
  });
97
129
  var onBlanketClicked = (0, _react.useCallback)(function (e) {
98
130
  if (shouldCloseOnOverlayClick) {
@@ -100,20 +132,216 @@ var InternalModalWrapper = function InternalModalWrapper(props) {
100
132
  }
101
133
  }, [shouldCloseOnOverlayClick, onCloseHandler]);
102
134
 
103
- // This ensures to prevent additional re-renders while nothing is passed to focusLockAllowlist explicitly.
135
+ // Stable callback to avoid re-renders when focusLockAllowlist is not provided.
104
136
  var allowListCallback = (0, _react.useCallback)(function (element) {
105
137
  return allowlistElements(element, focusLockAllowlist);
106
138
  }, [focusLockAllowlist]);
107
- (0, _usePreventProgrammaticScroll.default)();
139
+
140
+ // Called outside the feature-flag branch to keep hook order stable.
141
+ // Legacy path: FadeIn calls onFinish. Top-layer path: called directly.
142
+ var _useExitingPersistenc = (0, _exitingPersistence.useExitingPersistence)(),
143
+ isExiting = _useExitingPersistenc.isExiting,
144
+ onExitFinish = _useExitingPersistenc.onFinish;
145
+
146
+ // Prevent background scroll (top-layer path uses DialogScrollLock instead).
147
+ // Safe conditional hook: feature flags are resolved once at startup.
148
+ if (!(0, _platformFeatureFlags.fg)('platform-dst-top-layer')) {
149
+ // eslint-disable-next-line react-hooks/rules-of-hooks
150
+ (0, _usePreventProgrammaticScroll.default)();
151
+ }
108
152
  (0, _openLayerObserver.useNotifyOpenLayerObserver)({
109
153
  type: 'modal',
110
- // Modal dialog is conditionally rendered when visible, so when this runs it is always open.
154
+ // Always open — modal is conditionally rendered when visible.
111
155
  isOpen: true,
112
- // Passing a no-op for now, as there isn't a real use case for closing the modal dialog programmatically
113
- // by the OpenLayerObserver. The only current use case is closing layers when resizing the nav layout,
114
- // which cannot happen while a modal dialog is open.
156
+ // No-op: no current use case for programmatic close via OpenLayerObserver.
115
157
  onClose: _noop.default
116
158
  });
159
+
160
+ /**
161
+ * Top-layer path (platform-dst-top-layer).
162
+ *
163
+ * Replaces Portal, FocusLock, ScrollLock, Blanket, Positioner, and z-index
164
+ * management with native <dialog> via @atlaskit/top-layer/dialog.
165
+ *
166
+ * Key decisions:
167
+ * - Animation: CSS transitions via @starting-style / allow-discrete.
168
+ * - Close gating: onDialogClose only forwards allowed reasons
169
+ * (see notes/guides/dialog-close-flow.md).
170
+ * - onClose event param: undefined - consumers should use close reason.
171
+ * - Focus restoration: native <dialog> behavior replaces react-focus-lock's
172
+ * returnFocus (see accessibility-criteria.md).
173
+ */
174
+ if ((0, _platformFeatureFlags.fg)('platform-dst-top-layer')) {
175
+ // Native <dialog> always restores focus on close - no opt-out via shouldReturnFocus.
176
+ var defaultTestId = testId || 'modal-dialog';
177
+
178
+ // eslint-disable-next-line react-hooks/rules-of-hooks
179
+ var id = (0, _useId.useId)();
180
+ var titleId = "modal-dialog-title-".concat(id);
181
+
182
+ // Content container ref - used for onOpenComplete/onCloseComplete callbacks.
183
+ // eslint-disable-next-line react-hooks/rules-of-hooks
184
+ var contentRef = (0, _react.useRef)(null);
185
+
186
+ // Cache last content element for onCloseComplete after children unmount
187
+ // (with reduced motion, contentRef clears before onExitFinish fires).
188
+ // eslint-disable-next-line react-hooks/rules-of-hooks
189
+ var lastContentElRef = (0, _react.useRef)(null);
190
+ if (contentRef.current) {
191
+ lastContentElRef.current = contentRef.current;
192
+ }
193
+
194
+ // Native <dialog> ref - needed for ExitingPersistence to call dialog.close().
195
+ // eslint-disable-next-line react-hooks/rules-of-hooks
196
+ var dialogRef = (0, _react.useRef)(null);
197
+
198
+ // eslint-disable-next-line react-hooks/rules-of-hooks
199
+ var modalDialogContext = (0, _react.useMemo)(function () {
200
+ return {
201
+ testId: defaultTestId,
202
+ titleId: titleId,
203
+ onClose: onCloseHandler,
204
+ hasProvidedOnClose: Boolean(providedOnClose),
205
+ isFullScreen: isFullScreen !== null && isFullScreen !== void 0 ? isFullScreen : false
206
+ };
207
+ }, [defaultTestId, titleId, onCloseHandler, providedOnClose, isFullScreen]);
208
+
209
+ // Only forward close when the reason is allowed by props.
210
+ // Passes a synthetic event to satisfy the KeyboardOrMouseEvent contract.
211
+ // eslint-disable-next-line react-hooks/rules-of-hooks
212
+ var onDialogClose = (0, _react.useCallback)(function (_ref) {
213
+ var reason = _ref.reason;
214
+ if (reason === 'escape' && shouldCloseOnEscapePress) {
215
+ onCloseHandler((0, _createCloseEvent.createCloseEvent)({
216
+ reason: reason
217
+ }));
218
+ }
219
+ if (reason === 'overlay-click' && shouldCloseOnOverlayClick) {
220
+ onCloseHandler((0, _createCloseEvent.createCloseEvent)({
221
+ reason: reason
222
+ }));
223
+ }
224
+ }, [onCloseHandler, shouldCloseOnEscapePress, shouldCloseOnOverlayClick]);
225
+
226
+ // ExitingPersistence: isExiting → isOpen={false} → Dialog exit animation →
227
+ // onExitFinish → onCloseComplete + unmount.
228
+ // eslint-disable-next-line react-hooks/rules-of-hooks
229
+ var handleDialogExitFinish = (0, _react.useCallback)(function () {
230
+ var _contentRef$current;
231
+ var el = (_contentRef$current = contentRef.current) !== null && _contentRef$current !== void 0 ? _contentRef$current : lastContentElRef.current;
232
+ if (onCloseComplete && el) {
233
+ onCloseComplete(el);
234
+ }
235
+ lastContentElRef.current = null;
236
+ onExitFinish === null || onExitFinish === void 0 || onExitFinish();
237
+ }, [onExitFinish, onCloseComplete]);
238
+
239
+ // Fire onOpenComplete after mount.
240
+ // eslint-disable-next-line react-hooks/rules-of-hooks
241
+ (0, _react.useEffect)(function () {
242
+ if (onOpenComplete && contentRef.current) {
243
+ onOpenComplete(contentRef.current, true);
244
+ }
245
+ // eslint-disable-next-line react-hooks/exhaustive-deps
246
+ }, []);
247
+
248
+ // Honor `shouldReturnFocus={ref}` on unmount.
249
+ // Native <dialog>.close() restores focus to the trigger that opened it,
250
+ // but the consumer asked for focus to go to a specific element instead.
251
+ // Run this in an unmount cleanup so it fires after dialog.close()
252
+ // (which fires in the Dialog's effect cleanup).
253
+ // eslint-disable-next-line react-hooks/rules-of-hooks
254
+ var shouldReturnFocusRef = (0, _react.useRef)(shouldReturnFocus);
255
+ shouldReturnFocusRef.current = shouldReturnFocus;
256
+ // eslint-disable-next-line react-hooks/rules-of-hooks
257
+ (0, _react.useEffect)(function () {
258
+ return function () {
259
+ var target = shouldReturnFocusRef.current;
260
+ if ((0, _typeof2.default)(target) === 'object' && target.current) {
261
+ target.current.focus();
262
+ }
263
+ };
264
+ }, []);
265
+
266
+ // Focus a ref-targeted element after mount (when autoFocus is a ref).
267
+ // When true, native <dialog>.showModal() handles focus automatically.
268
+ // eslint-disable-next-line react-hooks/rules-of-hooks
269
+ (0, _useAutoFocus.default)((0, _typeof2.default)(autoFocus) === 'object' ? autoFocus : undefined, (0, _typeof2.default)(autoFocus) === 'object');
270
+
271
+ // Chrome cross-origin iframe DnD workaround (crbug.com/362301053)
272
+ // eslint-disable-next-line react-hooks/rules-of-hooks
273
+ (0, _react.useEffect)(function () {
274
+ return (0, _combine.combine)((0, _element.disableDraggingToCrossOriginIFramesForElement)(), (0, _textSelection.disableDraggingToCrossOriginIFramesForTextSelection)(), (0, _external.disableDraggingToCrossOriginIFramesForExternal)());
275
+ }, []);
276
+
277
+ // Responsive layout via ID-scoped <style> (same pattern as Dialog's hideBackdrop).
278
+ // ID selector beats Compiled atomic classes without !important and supports @media.
279
+ var namedWidth = (0, _modalDialog.dialogWidth)(width !== null && width !== void 0 ? width : 'medium');
280
+ var dialogId = "modal-dialog-".concat(id);
281
+ var escapedDialogId = CSS.escape(dialogId);
282
+
283
+ // Percentage widths need special handling in the top layer.
284
+ // In legacy, the percentage resolved against the Positioner's max-width
285
+ // (100vw - 120px). In the top layer, the <dialog>'s containing block is the
286
+ // viewport (100vw), so a raw percentage would produce a wider modal.
287
+ // Transform e.g. '42%' → 'calc(42 * (100vw - 120px) / 100)' to match legacy.
288
+ var resolvedWidth = namedWidth.endsWith('%') ? "calc(".concat(parseFloat(namedWidth), " * (100vw - 120px) / 100)") : namedWidth;
289
+ var dialogStyle = isFullScreen ? {
290
+ width: '100vw',
291
+ height: '100vh',
292
+ margin: '0'
293
+ } : {
294
+ width: "min(".concat(resolvedWidth, ", 100vw)")
295
+ };
296
+
297
+ // Shift stacked background modals down by space.100 (8px) per level.
298
+ if (stackIndex > 0) {
299
+ dialogStyle['transform'] = "translateY(calc(".concat(stackIndex, "px * ", "var(--ds-space-100, 8px)", "))");
300
+ }
301
+
302
+ // Mobile: viewport fill. Desktop (≥ 30rem): gutter margins, auto height.
303
+ // Content-div height set via #id > div to beat Compiled's atomic specificity.
304
+ var desktopMargin = shouldScrollInViewport ? '60px auto' : '60px auto auto';
305
+ var resolvedHeight = (0, _modalDialog.dialogHeight)(height);
306
+ // Body-scroll: specified height or auto. Viewport-scroll: uses min-height.
307
+ var desktopContentHeight = shouldScrollInViewport ? 'auto' : resolvedHeight;
308
+ var desktopContentMinHeight = shouldScrollInViewport ? resolvedHeight : 'auto';
309
+ // Viewport-scroll: the legacy Positioner was a fixed 100vh container that
310
+ // scrolled internally, so the modal section could fill (100vh - 60px top gutter).
311
+ // In the top layer the <dialog> sizes to content with height:auto, so we need
312
+ // an explicit min-height to ensure the dialog stretches to the same visible area.
313
+ var desktopDialogMinHeight = shouldScrollInViewport ? 'min-height:calc(100vh - 60px);' : '';
314
+ // Doubled-ID selector (#id#id > div) at specificity (2,0,1) beats
315
+ // Compiled atomic classes at (0,1,0) (increaseSpecificity is disabled).
316
+ var dialogPositionStyles = isFullScreen ? ''
317
+ // Mobile: edge-to-edge. Desktop (≥ 30rem): 60px gutters, max-width.
318
+ : "#".concat(escapedDialogId, "#").concat(escapedDialogId, "{margin:0;height:100vh}#").concat(escapedDialogId, "#").concat(escapedDialogId, ">div{height:100%}@media(min-width:30rem){#").concat(escapedDialogId, "#").concat(escapedDialogId, "{margin:").concat(desktopMargin, ";height:auto;").concat(desktopDialogMinHeight, "max-width:calc(100vw - 120px)}#").concat(escapedDialogId, "#").concat(escapedDialogId, ">div{height:").concat(desktopContentHeight, ";min-height:").concat(desktopContentMinHeight, "}}");
319
+ return /*#__PURE__*/React.createElement(_dialog.Dialog, {
320
+ ref: dialogRef,
321
+ id: dialogId,
322
+ onClose: onDialogClose,
323
+ onExitFinish: handleDialogExitFinish,
324
+ animate: isFullScreen ? false : modalAnimation,
325
+ isOpen: !isExiting,
326
+ shouldHideBackdrop: stackIndex > 0 || Boolean(isBlanketHidden),
327
+ label: label,
328
+ labelledBy: label ? undefined : titleId,
329
+ testId: defaultTestId
330
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
331
+ ,
332
+ style: dialogStyle
333
+ }, /*#__PURE__*/React.createElement(_dialogScrollLock.DialogScrollLock, null), dialogPositionStyles &&
334
+ /*#__PURE__*/
335
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
336
+ React.createElement("style", null, dialogPositionStyles), /*#__PURE__*/React.createElement("div", {
337
+ ref: contentRef,
338
+ className: (0, _runtime.ax)([topLayerStyles.content, !isFullScreen && topLayerStyles.borderRadius, !isFullScreen && (0, _platformFeatureFlags.fg)('platform-dst-shape-theme-default') && topLayerStyles.borderRadiusT26, !isFullScreen && !shouldScrollInViewport && "_bolhzwhf", !isFullScreen && shouldScrollInViewport && "_1tke1kxc _c71lglyw"])
339
+ }, /*#__PURE__*/React.createElement(_context.ModalContext.Provider, {
340
+ value: modalDialogContext
341
+ }, /*#__PURE__*/React.createElement(_context.ScrollContext.Provider, {
342
+ value: shouldScrollInViewport
343
+ }, children))));
344
+ }
117
345
  var modalDialogWithBlanket = /*#__PURE__*/React.createElement(_blanket.default, {
118
346
  isTinted: !isBlanketHidden,
119
347
  onBlanketClicked: onBlanketClicked,
@@ -21,7 +21,7 @@ import { disableDraggingToCrossOriginIFramesForTextSelection } from '../pragmati
21
21
  import { ScrollContext } from '../scroll-context';
22
22
  import { width } from '../width';
23
23
  import Positioner from './positioner';
24
- const dialogWidth = input => {
24
+ export const dialogWidth = input => {
25
25
  if (!input) {
26
26
  return 'auto';
27
27
  }
@@ -32,7 +32,7 @@ const dialogWidth = input => {
32
32
  }
33
33
  return typeof input === 'number' ? `${input}px` : input;
34
34
  };
35
- const dialogHeight = input => {
35
+ export const dialogHeight = input => {
36
36
  if (!input) {
37
37
  return 'auto';
38
38
  }
@@ -1,7 +1,24 @@
1
+
1
2
  ._152tze3t{inset-block-start:var(--ds-space-0,0)}
2
3
  ._18m91wug{overflow-y:auto}
3
4
  ._1bsbauwl{width:100vw}
5
+ ._1dg11kw7>form:only-child{flex-direction:inherit}
4
6
  ._1e02ze3t{inset-inline-start:var(--ds-space-0,0)}
7
+ ._1e0c1txw{display:flex}
8
+ ._1mq81kw7>form:only-child{display:inherit}
9
+ ._1q1l1bhr{--ds-elevation-surface-current:var(--ds-surface-overlay,#fff)}
10
+ ._1tke1kxc{min-height:100vh}
11
+ ._2lx21bp4{flex-direction:column}
5
12
  ._4t3i1kxc{height:100vh}
13
+ ._4t3i1osq{height:100%}
6
14
  ._8am5i4x0{-webkit-overflow-scrolling:touch}
7
- ._kqsw1n9t{position:fixed}
15
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
16
+ ._c71lglyw{max-height:none}
17
+ ._kqsw1n9t{position:fixed}
18
+ ._lcxv1wug{pointer-events:auto}
19
+ ._m01u1kw7>form:only-child{max-height:inherit}
20
+ ._syazi7uo{color:var(--ds-text,#292a2e)}._128mdkaa:focus-visible{outline-width:var(--ds-border-width-focused,2px)}
21
+ ._1ah3dkaa:focus-visible{outline-offset:var(--ds-border-width-focused,2px)}
22
+ ._mizu1v1w:focus-visible{outline-color:var(--ds-border-focused,#4688ec)}
23
+ ._ra3xnqa1:focus-visible{outline-style:solid}
24
+ @media (min-width:30rem){._bolhzwhf._bolhzwhf{max-height:calc(100vh - 119px)}._epkxfajl{border-radius:var(--ds-radius-small,3px)}._epkxpb1k{border-radius:var(--ds-radius-xlarge,9pt)}._zg7p130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}}
@@ -3,30 +3,62 @@ import _extends from "@babel/runtime/helpers/extends";
3
3
  import "./modal-wrapper.compiled.css";
4
4
  import * as React from 'react';
5
5
  import { ax, ix } from "@compiled/react/runtime";
6
- import { useCallback } from 'react';
6
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
7
7
  import FocusLock from 'react-focus-lock';
8
8
  import ScrollLock, { TouchScrollable } from 'react-scrolllock';
9
9
  import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
10
10
  import Blanket from '@atlaskit/blanket';
11
11
  import noop from '@atlaskit/ds-lib/noop';
12
+ import useAutoFocus from '@atlaskit/ds-lib/use-auto-focus';
13
+ import { useId } from '@atlaskit/ds-lib/use-id';
12
14
  import { Layering } from '@atlaskit/layering';
13
15
  import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
14
16
  import { Motion } from '@atlaskit/motion';
17
+ import { useExitingPersistence } from '@atlaskit/motion/exiting-persistence';
15
18
  import FadeIn from '@atlaskit/motion/fade-in';
16
19
  import { fg } from '@atlaskit/platform-feature-flags';
17
20
  import Portal from '@atlaskit/portal';
21
+ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
18
22
  import { layers } from '@atlaskit/theme/constants';
23
+ import { dialogSlideUpAndFade } from '@atlaskit/top-layer/animations';
24
+ import { createCloseEvent } from '@atlaskit/top-layer/create-close-event';
25
+ import { Dialog } from '@atlaskit/top-layer/dialog';
26
+ import { DialogScrollLock } from '@atlaskit/top-layer/dialog-scroll-lock';
27
+ import { ModalContext, ScrollContext } from '../context';
19
28
  import useModalStack from '../hooks/use-modal-stack';
20
29
  import usePreventProgrammaticScroll from '../hooks/use-prevent-programmatic-scroll';
21
- import ModalDialog from './modal-dialog';
30
+ import { disableDraggingToCrossOriginIFramesForElement } from '../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/element';
31
+ import { disableDraggingToCrossOriginIFramesForExternal } from '../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/external';
32
+ import { disableDraggingToCrossOriginIFramesForTextSelection } from '../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/text-selection';
33
+ import ModalDialog, { dialogHeight, dialogWidth as getDialogWidth } from './modal-dialog';
34
+ const modalAnimation = dialogSlideUpAndFade();
22
35
  const fillScreenStyles = null;
36
+
37
+ // Visual styles for modal content inside native <dialog>.
38
+ // Uses cssMap (not css) to avoid triggering no-nested-styles lint rule.
39
+
40
+ const LOCAL_CURRENT_SURFACE_CSS_VAR = '--ds-elevation-surface-current';
41
+ const topLayerStyles = {
42
+ content: "_1e0c1txw _4t3i1osq _2lx21bp4 _bfhk1bhr _syazi7uo _1q1l1bhr _lcxv1wug _1mq81kw7 _m01u1kw7 _1dg11kw7 _mizu1v1w _1ah3dkaa _ra3xnqa1 _128mdkaa _zg7p130s",
43
+ borderRadius: "_epkxfajl",
44
+ borderRadiusT26: "_epkxpb1k"
45
+ };
46
+
47
+ // Scroll-mode styles for the content div.
48
+ // Height overrides use ID-scoped <style> (see dialogPositionStyles) because
49
+ // Compiled atomic classes have specificity (0,1,0) (increaseSpecificity is disabled).
50
+ // The doubled-ID selector (#id#id > div) at (2,0,1) reliably wins.
51
+ // Only non-height properties needing the && boost remain here.
52
+
53
+ const topLayerBodyScrollStyles = null;
54
+ const topLayerViewportScrollStyles = null;
23
55
  const allowlistElements = (element, callback) => {
24
- // Allow focus to reach elements outside the modal:
25
- // if AUI dialog is allowListed and visible
26
- if (document.querySelector('.aui-blanket:not([hidden])')) {
56
+ // Allow focus outside modal when AUI dialog is visible
57
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- legacy FocusLock allowlist
58
+ if (Boolean(document.querySelector('.aui-blanket:not([hidden])'))) {
27
59
  return false;
28
60
  }
29
- // allows to pass a callback function to allow elements be ignored by focus lock
61
+ // Optional callback to let consumers exclude elements from focus lock
30
62
  if (typeof callback === 'function') {
31
63
  return callback(element);
32
64
  }
@@ -78,7 +110,7 @@ const InternalModalWrapper = props => {
78
110
  action: 'closed',
79
111
  componentName: 'modalDialog',
80
112
  packageName: "@atlaskit/modal-dialog",
81
- packageVersion: "14.18.3"
113
+ packageVersion: "0.0.0-development"
82
114
  });
83
115
  const onBlanketClicked = useCallback(e => {
84
116
  if (shouldCloseOnOverlayClick) {
@@ -86,18 +118,214 @@ const InternalModalWrapper = props => {
86
118
  }
87
119
  }, [shouldCloseOnOverlayClick, onCloseHandler]);
88
120
 
89
- // This ensures to prevent additional re-renders while nothing is passed to focusLockAllowlist explicitly.
121
+ // Stable callback to avoid re-renders when focusLockAllowlist is not provided.
90
122
  const allowListCallback = useCallback(element => allowlistElements(element, focusLockAllowlist), [focusLockAllowlist]);
91
- usePreventProgrammaticScroll();
123
+
124
+ // Called outside the feature-flag branch to keep hook order stable.
125
+ // Legacy path: FadeIn calls onFinish. Top-layer path: called directly.
126
+ const {
127
+ isExiting,
128
+ onFinish: onExitFinish
129
+ } = useExitingPersistence();
130
+
131
+ // Prevent background scroll (top-layer path uses DialogScrollLock instead).
132
+ // Safe conditional hook: feature flags are resolved once at startup.
133
+ if (!fg('platform-dst-top-layer')) {
134
+ // eslint-disable-next-line react-hooks/rules-of-hooks
135
+ usePreventProgrammaticScroll();
136
+ }
92
137
  useNotifyOpenLayerObserver({
93
138
  type: 'modal',
94
- // Modal dialog is conditionally rendered when visible, so when this runs it is always open.
139
+ // Always open — modal is conditionally rendered when visible.
95
140
  isOpen: true,
96
- // Passing a no-op for now, as there isn't a real use case for closing the modal dialog programmatically
97
- // by the OpenLayerObserver. The only current use case is closing layers when resizing the nav layout,
98
- // which cannot happen while a modal dialog is open.
141
+ // No-op: no current use case for programmatic close via OpenLayerObserver.
99
142
  onClose: noop
100
143
  });
144
+
145
+ /**
146
+ * Top-layer path (platform-dst-top-layer).
147
+ *
148
+ * Replaces Portal, FocusLock, ScrollLock, Blanket, Positioner, and z-index
149
+ * management with native <dialog> via @atlaskit/top-layer/dialog.
150
+ *
151
+ * Key decisions:
152
+ * - Animation: CSS transitions via @starting-style / allow-discrete.
153
+ * - Close gating: onDialogClose only forwards allowed reasons
154
+ * (see notes/guides/dialog-close-flow.md).
155
+ * - onClose event param: undefined - consumers should use close reason.
156
+ * - Focus restoration: native <dialog> behavior replaces react-focus-lock's
157
+ * returnFocus (see accessibility-criteria.md).
158
+ */
159
+ if (fg('platform-dst-top-layer')) {
160
+ // Native <dialog> always restores focus on close - no opt-out via shouldReturnFocus.
161
+ const defaultTestId = testId || 'modal-dialog';
162
+
163
+ // eslint-disable-next-line react-hooks/rules-of-hooks
164
+ const id = useId();
165
+ const titleId = `modal-dialog-title-${id}`;
166
+
167
+ // Content container ref - used for onOpenComplete/onCloseComplete callbacks.
168
+ // eslint-disable-next-line react-hooks/rules-of-hooks
169
+ const contentRef = useRef(null);
170
+
171
+ // Cache last content element for onCloseComplete after children unmount
172
+ // (with reduced motion, contentRef clears before onExitFinish fires).
173
+ // eslint-disable-next-line react-hooks/rules-of-hooks
174
+ const lastContentElRef = useRef(null);
175
+ if (contentRef.current) {
176
+ lastContentElRef.current = contentRef.current;
177
+ }
178
+
179
+ // Native <dialog> ref - needed for ExitingPersistence to call dialog.close().
180
+ // eslint-disable-next-line react-hooks/rules-of-hooks
181
+ const dialogRef = useRef(null);
182
+
183
+ // eslint-disable-next-line react-hooks/rules-of-hooks
184
+ const modalDialogContext = useMemo(() => ({
185
+ testId: defaultTestId,
186
+ titleId,
187
+ onClose: onCloseHandler,
188
+ hasProvidedOnClose: Boolean(providedOnClose),
189
+ isFullScreen: isFullScreen !== null && isFullScreen !== void 0 ? isFullScreen : false
190
+ }), [defaultTestId, titleId, onCloseHandler, providedOnClose, isFullScreen]);
191
+
192
+ // Only forward close when the reason is allowed by props.
193
+ // Passes a synthetic event to satisfy the KeyboardOrMouseEvent contract.
194
+ // eslint-disable-next-line react-hooks/rules-of-hooks
195
+ const onDialogClose = useCallback(({
196
+ reason
197
+ }) => {
198
+ if (reason === 'escape' && shouldCloseOnEscapePress) {
199
+ onCloseHandler(createCloseEvent({
200
+ reason
201
+ }));
202
+ }
203
+ if (reason === 'overlay-click' && shouldCloseOnOverlayClick) {
204
+ onCloseHandler(createCloseEvent({
205
+ reason
206
+ }));
207
+ }
208
+ }, [onCloseHandler, shouldCloseOnEscapePress, shouldCloseOnOverlayClick]);
209
+
210
+ // ExitingPersistence: isExiting → isOpen={false} → Dialog exit animation →
211
+ // onExitFinish → onCloseComplete + unmount.
212
+ // eslint-disable-next-line react-hooks/rules-of-hooks
213
+ const handleDialogExitFinish = useCallback(() => {
214
+ var _contentRef$current;
215
+ const el = (_contentRef$current = contentRef.current) !== null && _contentRef$current !== void 0 ? _contentRef$current : lastContentElRef.current;
216
+ if (onCloseComplete && el) {
217
+ onCloseComplete(el);
218
+ }
219
+ lastContentElRef.current = null;
220
+ onExitFinish === null || onExitFinish === void 0 ? void 0 : onExitFinish();
221
+ }, [onExitFinish, onCloseComplete]);
222
+
223
+ // Fire onOpenComplete after mount.
224
+ // eslint-disable-next-line react-hooks/rules-of-hooks
225
+ useEffect(() => {
226
+ if (onOpenComplete && contentRef.current) {
227
+ onOpenComplete(contentRef.current, true);
228
+ }
229
+ // eslint-disable-next-line react-hooks/exhaustive-deps
230
+ }, []);
231
+
232
+ // Honor `shouldReturnFocus={ref}` on unmount.
233
+ // Native <dialog>.close() restores focus to the trigger that opened it,
234
+ // but the consumer asked for focus to go to a specific element instead.
235
+ // Run this in an unmount cleanup so it fires after dialog.close()
236
+ // (which fires in the Dialog's effect cleanup).
237
+ // eslint-disable-next-line react-hooks/rules-of-hooks
238
+ const shouldReturnFocusRef = useRef(shouldReturnFocus);
239
+ shouldReturnFocusRef.current = shouldReturnFocus;
240
+ // eslint-disable-next-line react-hooks/rules-of-hooks
241
+ useEffect(() => {
242
+ return () => {
243
+ const target = shouldReturnFocusRef.current;
244
+ if (typeof target === 'object' && target.current) {
245
+ target.current.focus();
246
+ }
247
+ };
248
+ }, []);
249
+
250
+ // Focus a ref-targeted element after mount (when autoFocus is a ref).
251
+ // When true, native <dialog>.showModal() handles focus automatically.
252
+ // eslint-disable-next-line react-hooks/rules-of-hooks
253
+ useAutoFocus(typeof autoFocus === 'object' ? autoFocus : undefined, typeof autoFocus === 'object');
254
+
255
+ // Chrome cross-origin iframe DnD workaround (crbug.com/362301053)
256
+ // eslint-disable-next-line react-hooks/rules-of-hooks
257
+ useEffect(() => {
258
+ return combine(disableDraggingToCrossOriginIFramesForElement(), disableDraggingToCrossOriginIFramesForTextSelection(), disableDraggingToCrossOriginIFramesForExternal());
259
+ }, []);
260
+
261
+ // Responsive layout via ID-scoped <style> (same pattern as Dialog's hideBackdrop).
262
+ // ID selector beats Compiled atomic classes without !important and supports @media.
263
+ const namedWidth = getDialogWidth(width !== null && width !== void 0 ? width : 'medium');
264
+ const dialogId = `modal-dialog-${id}`;
265
+ const escapedDialogId = CSS.escape(dialogId);
266
+
267
+ // Percentage widths need special handling in the top layer.
268
+ // In legacy, the percentage resolved against the Positioner's max-width
269
+ // (100vw - 120px). In the top layer, the <dialog>'s containing block is the
270
+ // viewport (100vw), so a raw percentage would produce a wider modal.
271
+ // Transform e.g. '42%' → 'calc(42 * (100vw - 120px) / 100)' to match legacy.
272
+ const resolvedWidth = namedWidth.endsWith('%') ? `calc(${parseFloat(namedWidth)} * (100vw - 120px) / 100)` : namedWidth;
273
+ const dialogStyle = isFullScreen ? {
274
+ width: '100vw',
275
+ height: '100vh',
276
+ margin: '0'
277
+ } : {
278
+ width: `min(${resolvedWidth}, 100vw)`
279
+ };
280
+
281
+ // Shift stacked background modals down by space.100 (8px) per level.
282
+ if (stackIndex > 0) {
283
+ dialogStyle['transform'] = `translateY(calc(${stackIndex}px * ${"var(--ds-space-100, 8px)"}))`;
284
+ }
285
+
286
+ // Mobile: viewport fill. Desktop (≥ 30rem): gutter margins, auto height.
287
+ // Content-div height set via #id > div to beat Compiled's atomic specificity.
288
+ const desktopMargin = shouldScrollInViewport ? '60px auto' : '60px auto auto';
289
+ const resolvedHeight = dialogHeight(height);
290
+ // Body-scroll: specified height or auto. Viewport-scroll: uses min-height.
291
+ const desktopContentHeight = shouldScrollInViewport ? 'auto' : resolvedHeight;
292
+ const desktopContentMinHeight = shouldScrollInViewport ? resolvedHeight : 'auto';
293
+ // Viewport-scroll: the legacy Positioner was a fixed 100vh container that
294
+ // scrolled internally, so the modal section could fill (100vh - 60px top gutter).
295
+ // In the top layer the <dialog> sizes to content with height:auto, so we need
296
+ // an explicit min-height to ensure the dialog stretches to the same visible area.
297
+ const desktopDialogMinHeight = shouldScrollInViewport ? 'min-height:calc(100vh - 60px);' : '';
298
+ // Doubled-ID selector (#id#id > div) at specificity (2,0,1) beats
299
+ // Compiled atomic classes at (0,1,0) (increaseSpecificity is disabled).
300
+ const dialogPositionStyles = isFullScreen ? ''
301
+ // Mobile: edge-to-edge. Desktop (≥ 30rem): 60px gutters, max-width.
302
+ : `#${escapedDialogId}#${escapedDialogId}{margin:0;height:100vh}#${escapedDialogId}#${escapedDialogId}>div{height:100%}@media(min-width:30rem){#${escapedDialogId}#${escapedDialogId}{margin:${desktopMargin};height:auto;${desktopDialogMinHeight}max-width:calc(100vw - 120px)}#${escapedDialogId}#${escapedDialogId}>div{height:${desktopContentHeight};min-height:${desktopContentMinHeight}}}`;
303
+ return /*#__PURE__*/React.createElement(Dialog, {
304
+ ref: dialogRef,
305
+ id: dialogId,
306
+ onClose: onDialogClose,
307
+ onExitFinish: handleDialogExitFinish,
308
+ animate: isFullScreen ? false : modalAnimation,
309
+ isOpen: !isExiting,
310
+ shouldHideBackdrop: stackIndex > 0 || Boolean(isBlanketHidden),
311
+ label: label,
312
+ labelledBy: label ? undefined : titleId,
313
+ testId: defaultTestId
314
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
315
+ ,
316
+ style: dialogStyle
317
+ }, /*#__PURE__*/React.createElement(DialogScrollLock, null), dialogPositionStyles &&
318
+ /*#__PURE__*/
319
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
320
+ React.createElement("style", null, dialogPositionStyles), /*#__PURE__*/React.createElement("div", {
321
+ ref: contentRef,
322
+ className: ax([topLayerStyles.content, !isFullScreen && topLayerStyles.borderRadius, !isFullScreen && fg('platform-dst-shape-theme-default') && topLayerStyles.borderRadiusT26, !isFullScreen && !shouldScrollInViewport && "_bolhzwhf", !isFullScreen && shouldScrollInViewport && "_1tke1kxc _c71lglyw"])
323
+ }, /*#__PURE__*/React.createElement(ModalContext.Provider, {
324
+ value: modalDialogContext
325
+ }, /*#__PURE__*/React.createElement(ScrollContext.Provider, {
326
+ value: shouldScrollInViewport
327
+ }, children))));
328
+ }
101
329
  const modalDialogWithBlanket = /*#__PURE__*/React.createElement(Blanket, {
102
330
  isTinted: !isBlanketHidden,
103
331
  onBlanketClicked: onBlanketClicked,
@@ -23,7 +23,7 @@ import { disableDraggingToCrossOriginIFramesForTextSelection } from '../pragmati
23
23
  import { ScrollContext } from '../scroll-context';
24
24
  import { width } from '../width';
25
25
  import Positioner from './positioner';
26
- var dialogWidth = function dialogWidth(input) {
26
+ export var dialogWidth = function dialogWidth(input) {
27
27
  if (!input) {
28
28
  return 'auto';
29
29
  }
@@ -34,7 +34,7 @@ var dialogWidth = function dialogWidth(input) {
34
34
  }
35
35
  return typeof input === 'number' ? "".concat(input, "px") : input;
36
36
  };
37
- var dialogHeight = function dialogHeight(input) {
37
+ export var dialogHeight = function dialogHeight(input) {
38
38
  if (!input) {
39
39
  return 'auto';
40
40
  }
@@ -1,7 +1,24 @@
1
+
1
2
  ._152tze3t{inset-block-start:var(--ds-space-0,0)}
2
3
  ._18m91wug{overflow-y:auto}
3
4
  ._1bsbauwl{width:100vw}
5
+ ._1dg11kw7>form:only-child{flex-direction:inherit}
4
6
  ._1e02ze3t{inset-inline-start:var(--ds-space-0,0)}
7
+ ._1e0c1txw{display:flex}
8
+ ._1mq81kw7>form:only-child{display:inherit}
9
+ ._1q1l1bhr{--ds-elevation-surface-current:var(--ds-surface-overlay,#fff)}
10
+ ._1tke1kxc{min-height:100vh}
11
+ ._2lx21bp4{flex-direction:column}
5
12
  ._4t3i1kxc{height:100vh}
13
+ ._4t3i1osq{height:100%}
6
14
  ._8am5i4x0{-webkit-overflow-scrolling:touch}
7
- ._kqsw1n9t{position:fixed}
15
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
16
+ ._c71lglyw{max-height:none}
17
+ ._kqsw1n9t{position:fixed}
18
+ ._lcxv1wug{pointer-events:auto}
19
+ ._m01u1kw7>form:only-child{max-height:inherit}
20
+ ._syazi7uo{color:var(--ds-text,#292a2e)}._128mdkaa:focus-visible{outline-width:var(--ds-border-width-focused,2px)}
21
+ ._1ah3dkaa:focus-visible{outline-offset:var(--ds-border-width-focused,2px)}
22
+ ._mizu1v1w:focus-visible{outline-color:var(--ds-border-focused,#4688ec)}
23
+ ._ra3xnqa1:focus-visible{outline-style:solid}
24
+ @media (min-width:30rem){._bolhzwhf._bolhzwhf{max-height:calc(100vh - 119px)}._epkxfajl{border-radius:var(--ds-radius-small,3px)}._epkxpb1k{border-radius:var(--ds-radius-xlarge,9pt)}._zg7p130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}}
@@ -4,30 +4,62 @@ import _typeof from "@babel/runtime/helpers/typeof";
4
4
  import "./modal-wrapper.compiled.css";
5
5
  import * as React from 'react';
6
6
  import { ax, ix } from "@compiled/react/runtime";
7
- import { useCallback } from 'react';
7
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
8
8
  import FocusLock from 'react-focus-lock';
9
9
  import ScrollLock, { TouchScrollable } from 'react-scrolllock';
10
10
  import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
11
11
  import Blanket from '@atlaskit/blanket';
12
12
  import noop from '@atlaskit/ds-lib/noop';
13
+ import useAutoFocus from '@atlaskit/ds-lib/use-auto-focus';
14
+ import { useId } from '@atlaskit/ds-lib/use-id';
13
15
  import { Layering } from '@atlaskit/layering';
14
16
  import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
15
17
  import { Motion } from '@atlaskit/motion';
18
+ import { useExitingPersistence } from '@atlaskit/motion/exiting-persistence';
16
19
  import FadeIn from '@atlaskit/motion/fade-in';
17
20
  import { fg } from '@atlaskit/platform-feature-flags';
18
21
  import Portal from '@atlaskit/portal';
22
+ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
19
23
  import { layers } from '@atlaskit/theme/constants';
24
+ import { dialogSlideUpAndFade } from '@atlaskit/top-layer/animations';
25
+ import { createCloseEvent } from '@atlaskit/top-layer/create-close-event';
26
+ import { Dialog } from '@atlaskit/top-layer/dialog';
27
+ import { DialogScrollLock } from '@atlaskit/top-layer/dialog-scroll-lock';
28
+ import { ModalContext, ScrollContext } from '../context';
20
29
  import useModalStack from '../hooks/use-modal-stack';
21
30
  import usePreventProgrammaticScroll from '../hooks/use-prevent-programmatic-scroll';
22
- import ModalDialog from './modal-dialog';
31
+ import { disableDraggingToCrossOriginIFramesForElement } from '../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/element';
32
+ import { disableDraggingToCrossOriginIFramesForExternal } from '../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/external';
33
+ import { disableDraggingToCrossOriginIFramesForTextSelection } from '../pragmatic-drag-and-drop/disable-dragging-to-cross-origin-iframes/text-selection';
34
+ import ModalDialog, { dialogHeight, dialogWidth as getDialogWidth } from './modal-dialog';
35
+ var modalAnimation = dialogSlideUpAndFade();
23
36
  var fillScreenStyles = null;
37
+
38
+ // Visual styles for modal content inside native <dialog>.
39
+ // Uses cssMap (not css) to avoid triggering no-nested-styles lint rule.
40
+
41
+ var LOCAL_CURRENT_SURFACE_CSS_VAR = '--ds-elevation-surface-current';
42
+ var topLayerStyles = {
43
+ content: "_1e0c1txw _4t3i1osq _2lx21bp4 _bfhk1bhr _syazi7uo _1q1l1bhr _lcxv1wug _1mq81kw7 _m01u1kw7 _1dg11kw7 _mizu1v1w _1ah3dkaa _ra3xnqa1 _128mdkaa _zg7p130s",
44
+ borderRadius: "_epkxfajl",
45
+ borderRadiusT26: "_epkxpb1k"
46
+ };
47
+
48
+ // Scroll-mode styles for the content div.
49
+ // Height overrides use ID-scoped <style> (see dialogPositionStyles) because
50
+ // Compiled atomic classes have specificity (0,1,0) (increaseSpecificity is disabled).
51
+ // The doubled-ID selector (#id#id > div) at (2,0,1) reliably wins.
52
+ // Only non-height properties needing the && boost remain here.
53
+
54
+ var topLayerBodyScrollStyles = null;
55
+ var topLayerViewportScrollStyles = null;
24
56
  var allowlistElements = function allowlistElements(element, callback) {
25
- // Allow focus to reach elements outside the modal:
26
- // if AUI dialog is allowListed and visible
27
- if (document.querySelector('.aui-blanket:not([hidden])')) {
57
+ // Allow focus outside modal when AUI dialog is visible
58
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- legacy FocusLock allowlist
59
+ if (Boolean(document.querySelector('.aui-blanket:not([hidden])'))) {
28
60
  return false;
29
61
  }
30
- // allows to pass a callback function to allow elements be ignored by focus lock
62
+ // Optional callback to let consumers exclude elements from focus lock
31
63
  if (typeof callback === 'function') {
32
64
  return callback(element);
33
65
  }
@@ -83,7 +115,7 @@ var InternalModalWrapper = function InternalModalWrapper(props) {
83
115
  action: 'closed',
84
116
  componentName: 'modalDialog',
85
117
  packageName: "@atlaskit/modal-dialog",
86
- packageVersion: "14.18.3"
118
+ packageVersion: "0.0.0-development"
87
119
  });
88
120
  var onBlanketClicked = useCallback(function (e) {
89
121
  if (shouldCloseOnOverlayClick) {
@@ -91,20 +123,216 @@ var InternalModalWrapper = function InternalModalWrapper(props) {
91
123
  }
92
124
  }, [shouldCloseOnOverlayClick, onCloseHandler]);
93
125
 
94
- // This ensures to prevent additional re-renders while nothing is passed to focusLockAllowlist explicitly.
126
+ // Stable callback to avoid re-renders when focusLockAllowlist is not provided.
95
127
  var allowListCallback = useCallback(function (element) {
96
128
  return allowlistElements(element, focusLockAllowlist);
97
129
  }, [focusLockAllowlist]);
98
- usePreventProgrammaticScroll();
130
+
131
+ // Called outside the feature-flag branch to keep hook order stable.
132
+ // Legacy path: FadeIn calls onFinish. Top-layer path: called directly.
133
+ var _useExitingPersistenc = useExitingPersistence(),
134
+ isExiting = _useExitingPersistenc.isExiting,
135
+ onExitFinish = _useExitingPersistenc.onFinish;
136
+
137
+ // Prevent background scroll (top-layer path uses DialogScrollLock instead).
138
+ // Safe conditional hook: feature flags are resolved once at startup.
139
+ if (!fg('platform-dst-top-layer')) {
140
+ // eslint-disable-next-line react-hooks/rules-of-hooks
141
+ usePreventProgrammaticScroll();
142
+ }
99
143
  useNotifyOpenLayerObserver({
100
144
  type: 'modal',
101
- // Modal dialog is conditionally rendered when visible, so when this runs it is always open.
145
+ // Always open — modal is conditionally rendered when visible.
102
146
  isOpen: true,
103
- // Passing a no-op for now, as there isn't a real use case for closing the modal dialog programmatically
104
- // by the OpenLayerObserver. The only current use case is closing layers when resizing the nav layout,
105
- // which cannot happen while a modal dialog is open.
147
+ // No-op: no current use case for programmatic close via OpenLayerObserver.
106
148
  onClose: noop
107
149
  });
150
+
151
+ /**
152
+ * Top-layer path (platform-dst-top-layer).
153
+ *
154
+ * Replaces Portal, FocusLock, ScrollLock, Blanket, Positioner, and z-index
155
+ * management with native <dialog> via @atlaskit/top-layer/dialog.
156
+ *
157
+ * Key decisions:
158
+ * - Animation: CSS transitions via @starting-style / allow-discrete.
159
+ * - Close gating: onDialogClose only forwards allowed reasons
160
+ * (see notes/guides/dialog-close-flow.md).
161
+ * - onClose event param: undefined - consumers should use close reason.
162
+ * - Focus restoration: native <dialog> behavior replaces react-focus-lock's
163
+ * returnFocus (see accessibility-criteria.md).
164
+ */
165
+ if (fg('platform-dst-top-layer')) {
166
+ // Native <dialog> always restores focus on close - no opt-out via shouldReturnFocus.
167
+ var defaultTestId = testId || 'modal-dialog';
168
+
169
+ // eslint-disable-next-line react-hooks/rules-of-hooks
170
+ var id = useId();
171
+ var titleId = "modal-dialog-title-".concat(id);
172
+
173
+ // Content container ref - used for onOpenComplete/onCloseComplete callbacks.
174
+ // eslint-disable-next-line react-hooks/rules-of-hooks
175
+ var contentRef = useRef(null);
176
+
177
+ // Cache last content element for onCloseComplete after children unmount
178
+ // (with reduced motion, contentRef clears before onExitFinish fires).
179
+ // eslint-disable-next-line react-hooks/rules-of-hooks
180
+ var lastContentElRef = useRef(null);
181
+ if (contentRef.current) {
182
+ lastContentElRef.current = contentRef.current;
183
+ }
184
+
185
+ // Native <dialog> ref - needed for ExitingPersistence to call dialog.close().
186
+ // eslint-disable-next-line react-hooks/rules-of-hooks
187
+ var dialogRef = useRef(null);
188
+
189
+ // eslint-disable-next-line react-hooks/rules-of-hooks
190
+ var modalDialogContext = useMemo(function () {
191
+ return {
192
+ testId: defaultTestId,
193
+ titleId: titleId,
194
+ onClose: onCloseHandler,
195
+ hasProvidedOnClose: Boolean(providedOnClose),
196
+ isFullScreen: isFullScreen !== null && isFullScreen !== void 0 ? isFullScreen : false
197
+ };
198
+ }, [defaultTestId, titleId, onCloseHandler, providedOnClose, isFullScreen]);
199
+
200
+ // Only forward close when the reason is allowed by props.
201
+ // Passes a synthetic event to satisfy the KeyboardOrMouseEvent contract.
202
+ // eslint-disable-next-line react-hooks/rules-of-hooks
203
+ var onDialogClose = useCallback(function (_ref) {
204
+ var reason = _ref.reason;
205
+ if (reason === 'escape' && shouldCloseOnEscapePress) {
206
+ onCloseHandler(createCloseEvent({
207
+ reason: reason
208
+ }));
209
+ }
210
+ if (reason === 'overlay-click' && shouldCloseOnOverlayClick) {
211
+ onCloseHandler(createCloseEvent({
212
+ reason: reason
213
+ }));
214
+ }
215
+ }, [onCloseHandler, shouldCloseOnEscapePress, shouldCloseOnOverlayClick]);
216
+
217
+ // ExitingPersistence: isExiting → isOpen={false} → Dialog exit animation →
218
+ // onExitFinish → onCloseComplete + unmount.
219
+ // eslint-disable-next-line react-hooks/rules-of-hooks
220
+ var handleDialogExitFinish = useCallback(function () {
221
+ var _contentRef$current;
222
+ var el = (_contentRef$current = contentRef.current) !== null && _contentRef$current !== void 0 ? _contentRef$current : lastContentElRef.current;
223
+ if (onCloseComplete && el) {
224
+ onCloseComplete(el);
225
+ }
226
+ lastContentElRef.current = null;
227
+ onExitFinish === null || onExitFinish === void 0 || onExitFinish();
228
+ }, [onExitFinish, onCloseComplete]);
229
+
230
+ // Fire onOpenComplete after mount.
231
+ // eslint-disable-next-line react-hooks/rules-of-hooks
232
+ useEffect(function () {
233
+ if (onOpenComplete && contentRef.current) {
234
+ onOpenComplete(contentRef.current, true);
235
+ }
236
+ // eslint-disable-next-line react-hooks/exhaustive-deps
237
+ }, []);
238
+
239
+ // Honor `shouldReturnFocus={ref}` on unmount.
240
+ // Native <dialog>.close() restores focus to the trigger that opened it,
241
+ // but the consumer asked for focus to go to a specific element instead.
242
+ // Run this in an unmount cleanup so it fires after dialog.close()
243
+ // (which fires in the Dialog's effect cleanup).
244
+ // eslint-disable-next-line react-hooks/rules-of-hooks
245
+ var shouldReturnFocusRef = useRef(shouldReturnFocus);
246
+ shouldReturnFocusRef.current = shouldReturnFocus;
247
+ // eslint-disable-next-line react-hooks/rules-of-hooks
248
+ useEffect(function () {
249
+ return function () {
250
+ var target = shouldReturnFocusRef.current;
251
+ if (_typeof(target) === 'object' && target.current) {
252
+ target.current.focus();
253
+ }
254
+ };
255
+ }, []);
256
+
257
+ // Focus a ref-targeted element after mount (when autoFocus is a ref).
258
+ // When true, native <dialog>.showModal() handles focus automatically.
259
+ // eslint-disable-next-line react-hooks/rules-of-hooks
260
+ useAutoFocus(_typeof(autoFocus) === 'object' ? autoFocus : undefined, _typeof(autoFocus) === 'object');
261
+
262
+ // Chrome cross-origin iframe DnD workaround (crbug.com/362301053)
263
+ // eslint-disable-next-line react-hooks/rules-of-hooks
264
+ useEffect(function () {
265
+ return combine(disableDraggingToCrossOriginIFramesForElement(), disableDraggingToCrossOriginIFramesForTextSelection(), disableDraggingToCrossOriginIFramesForExternal());
266
+ }, []);
267
+
268
+ // Responsive layout via ID-scoped <style> (same pattern as Dialog's hideBackdrop).
269
+ // ID selector beats Compiled atomic classes without !important and supports @media.
270
+ var namedWidth = getDialogWidth(width !== null && width !== void 0 ? width : 'medium');
271
+ var dialogId = "modal-dialog-".concat(id);
272
+ var escapedDialogId = CSS.escape(dialogId);
273
+
274
+ // Percentage widths need special handling in the top layer.
275
+ // In legacy, the percentage resolved against the Positioner's max-width
276
+ // (100vw - 120px). In the top layer, the <dialog>'s containing block is the
277
+ // viewport (100vw), so a raw percentage would produce a wider modal.
278
+ // Transform e.g. '42%' → 'calc(42 * (100vw - 120px) / 100)' to match legacy.
279
+ var resolvedWidth = namedWidth.endsWith('%') ? "calc(".concat(parseFloat(namedWidth), " * (100vw - 120px) / 100)") : namedWidth;
280
+ var dialogStyle = isFullScreen ? {
281
+ width: '100vw',
282
+ height: '100vh',
283
+ margin: '0'
284
+ } : {
285
+ width: "min(".concat(resolvedWidth, ", 100vw)")
286
+ };
287
+
288
+ // Shift stacked background modals down by space.100 (8px) per level.
289
+ if (stackIndex > 0) {
290
+ dialogStyle['transform'] = "translateY(calc(".concat(stackIndex, "px * ", "var(--ds-space-100, 8px)", "))");
291
+ }
292
+
293
+ // Mobile: viewport fill. Desktop (≥ 30rem): gutter margins, auto height.
294
+ // Content-div height set via #id > div to beat Compiled's atomic specificity.
295
+ var desktopMargin = shouldScrollInViewport ? '60px auto' : '60px auto auto';
296
+ var resolvedHeight = dialogHeight(height);
297
+ // Body-scroll: specified height or auto. Viewport-scroll: uses min-height.
298
+ var desktopContentHeight = shouldScrollInViewport ? 'auto' : resolvedHeight;
299
+ var desktopContentMinHeight = shouldScrollInViewport ? resolvedHeight : 'auto';
300
+ // Viewport-scroll: the legacy Positioner was a fixed 100vh container that
301
+ // scrolled internally, so the modal section could fill (100vh - 60px top gutter).
302
+ // In the top layer the <dialog> sizes to content with height:auto, so we need
303
+ // an explicit min-height to ensure the dialog stretches to the same visible area.
304
+ var desktopDialogMinHeight = shouldScrollInViewport ? 'min-height:calc(100vh - 60px);' : '';
305
+ // Doubled-ID selector (#id#id > div) at specificity (2,0,1) beats
306
+ // Compiled atomic classes at (0,1,0) (increaseSpecificity is disabled).
307
+ var dialogPositionStyles = isFullScreen ? ''
308
+ // Mobile: edge-to-edge. Desktop (≥ 30rem): 60px gutters, max-width.
309
+ : "#".concat(escapedDialogId, "#").concat(escapedDialogId, "{margin:0;height:100vh}#").concat(escapedDialogId, "#").concat(escapedDialogId, ">div{height:100%}@media(min-width:30rem){#").concat(escapedDialogId, "#").concat(escapedDialogId, "{margin:").concat(desktopMargin, ";height:auto;").concat(desktopDialogMinHeight, "max-width:calc(100vw - 120px)}#").concat(escapedDialogId, "#").concat(escapedDialogId, ">div{height:").concat(desktopContentHeight, ";min-height:").concat(desktopContentMinHeight, "}}");
310
+ return /*#__PURE__*/React.createElement(Dialog, {
311
+ ref: dialogRef,
312
+ id: dialogId,
313
+ onClose: onDialogClose,
314
+ onExitFinish: handleDialogExitFinish,
315
+ animate: isFullScreen ? false : modalAnimation,
316
+ isOpen: !isExiting,
317
+ shouldHideBackdrop: stackIndex > 0 || Boolean(isBlanketHidden),
318
+ label: label,
319
+ labelledBy: label ? undefined : titleId,
320
+ testId: defaultTestId
321
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
322
+ ,
323
+ style: dialogStyle
324
+ }, /*#__PURE__*/React.createElement(DialogScrollLock, null), dialogPositionStyles &&
325
+ /*#__PURE__*/
326
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
327
+ React.createElement("style", null, dialogPositionStyles), /*#__PURE__*/React.createElement("div", {
328
+ ref: contentRef,
329
+ className: ax([topLayerStyles.content, !isFullScreen && topLayerStyles.borderRadius, !isFullScreen && fg('platform-dst-shape-theme-default') && topLayerStyles.borderRadiusT26, !isFullScreen && !shouldScrollInViewport && "_bolhzwhf", !isFullScreen && shouldScrollInViewport && "_1tke1kxc _c71lglyw"])
330
+ }, /*#__PURE__*/React.createElement(ModalContext.Provider, {
331
+ value: modalDialogContext
332
+ }, /*#__PURE__*/React.createElement(ScrollContext.Provider, {
333
+ value: shouldScrollInViewport
334
+ }, children))));
335
+ }
108
336
  var modalDialogWithBlanket = /*#__PURE__*/React.createElement(Blanket, {
109
337
  isTinted: !isBlanketHidden,
110
338
  onBlanketClicked: onBlanketClicked,
@@ -2,6 +2,9 @@
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
4
  */
5
+ import type { ModalDialogProps } from '../../types';
5
6
  import type { InternalModalDialogProps } from '../types';
7
+ export declare const dialogWidth: (input?: ModalDialogProps["width"]) => string;
8
+ export declare const dialogHeight: (input?: ModalDialogProps["height"]) => string;
6
9
  declare const ModalDialog: (props: InternalModalDialogProps) => JSX.Element;
7
10
  export default ModalDialog;
@@ -2,6 +2,9 @@
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
4
  */
5
+ import type { ModalDialogProps } from '../../types';
5
6
  import type { InternalModalDialogProps } from '../types';
7
+ export declare const dialogWidth: (input?: ModalDialogProps["width"]) => string;
8
+ export declare const dialogHeight: (input?: ModalDialogProps["height"]) => string;
6
9
  declare const ModalDialog: (props: InternalModalDialogProps) => JSX.Element;
7
10
  export default ModalDialog;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/modal-dialog",
3
- "version": "15.0.0",
3
+ "version": "15.0.1",
4
4
  "description": "A modal dialog displays content that requires user interaction, in a layer above the page.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -39,7 +39,7 @@
39
39
  "@atlaskit/button": "^23.11.0",
40
40
  "@atlaskit/css": "^0.19.0",
41
41
  "@atlaskit/ds-lib": "^7.0.0",
42
- "@atlaskit/icon": "^34.4.0",
42
+ "@atlaskit/icon": "^34.5.0",
43
43
  "@atlaskit/layering": "^3.7.0",
44
44
  "@atlaskit/motion": "^6.2.0",
45
45
  "@atlaskit/platform-feature-flags": "^1.1.0",
@@ -48,6 +48,7 @@
48
48
  "@atlaskit/primitives": "^19.0.0",
49
49
  "@atlaskit/theme": "^23.2.0",
50
50
  "@atlaskit/tokens": "^13.0.0",
51
+ "@atlaskit/top-layer": "^0.6.0",
51
52
  "@babel/runtime": "^7.0.0",
52
53
  "@compiled/react": "^0.20.0",
53
54
  "bind-event-listener": "^3.0.0",
@@ -63,7 +64,7 @@
63
64
  "@af/integration-testing": "workspace:^",
64
65
  "@af/visual-regression": "workspace:^",
65
66
  "@atlaskit/avatar": "^25.14.0",
66
- "@atlaskit/avatar-group": "^12.7.0",
67
+ "@atlaskit/avatar-group": "^12.8.0",
67
68
  "@atlaskit/banner": "^14.1.0",
68
69
  "@atlaskit/breadcrumbs": "^16.1.0",
69
70
  "@atlaskit/checkbox": "^17.3.0",
@@ -75,14 +76,14 @@
75
76
  "@atlaskit/form": "^15.5.0",
76
77
  "@atlaskit/heading": "^5.4.0",
77
78
  "@atlaskit/link": "^3.4.0",
78
- "@atlaskit/popup": "^4.17.0",
79
+ "@atlaskit/popup": "^4.18.0",
79
80
  "@atlaskit/radio": "^8.6.0",
80
81
  "@atlaskit/section-message": "^8.12.0",
81
- "@atlaskit/select": "^21.10.0",
82
+ "@atlaskit/select": "^21.11.0",
82
83
  "@atlaskit/spotlight": "^0.14.0",
83
84
  "@atlaskit/textfield": "^8.3.0",
84
- "@atlaskit/tooltip": "^22.0.0",
85
- "@atlassian/feature-flags-test-utils": "^1.0.0",
85
+ "@atlaskit/tooltip": "^22.1.0",
86
+ "@atlassian/feature-flags-test-utils": "^1.1.0",
86
87
  "@atlassian/react-compiler-gating": "workspace:^",
87
88
  "@atlassian/ssr-tests": "workspace:^",
88
89
  "@atlassian/structured-docs-types": "workspace:^",
@@ -140,6 +141,9 @@
140
141
  },
141
142
  "platform-dst-motion-uplift-modal": {
142
143
  "type": "boolean"
144
+ },
145
+ "platform-dst-top-layer": {
146
+ "type": "boolean"
143
147
  }
144
148
  },
145
149
  "homepage": "https://atlassian.design/components/modal-dialog/"