@atlaskit/pragmatic-drag-and-drop 1.7.9 → 1.8.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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # @atlaskit/pragmatic-drag-and-drop
2
2
 
3
+ ## 1.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`28c6a19284541`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/28c6a19284541) -
8
+ Moving to `popover="manual"` for "on top" rendering logic. Elements that need to render above all
9
+ other content (honey pot fix, custom native drag previews) now use the browser's top layer via the
10
+ Popover API instead of `z-index: 2147483647`.
11
+
12
+ Falls back to the previous `z-index` approach when the Popover API is not available.
13
+
14
+ Minimum browser versions with `popover="manual"` support:
15
+ - Chrome 114+
16
+ - Edge 114+
17
+ - Firefox 125+
18
+ - Safari 17+
19
+
20
+ ## 1.7.10
21
+
22
+ ### Patch Changes
23
+
24
+ - [`be5ff878b4b9a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/be5ff878b4b9a) -
25
+ Internal changes to remove unnecessary token fallbacks and imports from `@atlaskit/theme`
26
+
3
27
  ## 1.7.9
4
28
 
5
29
  ### Patch Changes
@@ -8,6 +8,8 @@ exports.makeHoneyPotFix = makeHoneyPotFix;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _bindEventListener = require("bind-event-listener");
10
10
  var _maxZIndex = require("../util/max-z-index");
11
+ var _popoverResetStyles = require("../util/popover-reset-styles");
12
+ var _supportsPopover = require("../util/supports-popover");
11
13
  var _honeyPotDataAttribute = require("./honey-pot-data-attribute");
12
14
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
13
15
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -136,33 +138,48 @@ function mountHoneyPot(_ref4) {
136
138
  var element = document.createElement('div');
137
139
  element.setAttribute(_honeyPotDataAttribute.honeyPotDataAttribute, 'true');
138
140
 
141
+ // Using popover="manual" to place the element in the browser's top layer.
142
+ // This ensures the honey pot is visually on top of everything else,
143
+ // including elements using z-index and other stacking contexts.
144
+ // "manual" means no light dismiss — we manage the lifecycle ourselves.
145
+ // Falls back to position:fixed + maxZIndex when the popover API is not available.
146
+ if ((0, _supportsPopover.supportsPopover)()) {
147
+ element.setAttribute('popover', 'manual');
148
+ }
149
+
139
150
  // can shift during the drag thanks to Firefox
140
151
  var clientRect = getHoneyPotRectFor({
141
152
  client: initial
142
153
  });
143
154
  Object.assign(element.style, _objectSpread(_objectSpread({
155
+ position: 'fixed'
156
+ }, (0, _supportsPopover.supportsPopover)() ?
157
+ // needs to come first as it has 'inset: unset' which
158
+ // needs to be overridden by our top / left values
159
+ _popoverResetStyles.popoverResetUserAgentStyles : {
160
+ // Fallback: using maximum possible z-index so that this element
161
+ // will always be on top of other positioned content.
162
+ zIndex: _maxZIndex.maxZIndex
163
+ }), {}, {
144
164
  // Setting a background color explicitly to avoid any inherited styles.
145
165
  // Looks like this could be `opacity: 0`, but worried that _might_
146
166
  // cause the element to be ignored on some platforms.
147
167
  // When debugging, set backgroundColor to something like "red".
148
168
  backgroundColor: 'transparent',
149
- position: 'fixed',
150
169
  // Being explicit to avoid inheriting styles
151
170
  padding: 0,
152
171
  margin: 0,
153
- boxSizing: 'border-box'
154
- }, getRectStyles({
155
- clientRect: clientRect
156
- })), {}, {
172
+ boxSizing: 'border-box',
157
173
  // We want this element to absorb pointer events,
158
174
  // it's kind of the whole point 😉
159
- pointerEvents: 'auto',
160
- // Want to make sure the honey pot is top of everything else.
161
- // Don't need to worry about native drag previews, as they will
162
- // have been rendered (and removed) before the honey pot is rendered
163
- zIndex: _maxZIndex.maxZIndex
164
- }));
175
+ pointerEvents: 'auto'
176
+ }, getRectStyles({
177
+ clientRect: clientRect
178
+ })));
165
179
  document.body.appendChild(element);
180
+ if ((0, _supportsPopover.supportsPopover)()) {
181
+ element.showPopover();
182
+ }
166
183
 
167
184
  /**
168
185
  * 🦊 In firefox we can get `"pointermove"` events after the drag
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.setCustomNativeDragPreview = setCustomNativeDragPreview;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
7
9
  var _elementAdapter = require("../../../adapter/element-adapter");
8
10
  var _isSafari = require("../../../util/is-safari");
9
11
  var _maxZIndex = require("../../../util/max-z-index");
12
+ var _popoverResetStyles = require("../../../util/popover-reset-styles");
13
+ var _supportsPopover = require("../../../util/supports-popover");
14
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
10
16
  /** A function to remove the element that has been added to the `container`.
11
17
  * @example () => ReactDOM.unmountComponentAtNode(container)
12
18
  */
@@ -42,12 +48,33 @@ function setCustomNativeDragPreview(_ref) {
42
48
  _ref$getOffset = _ref.getOffset,
43
49
  getOffset = _ref$getOffset === void 0 ? defaultOffset : _ref$getOffset;
44
50
  var container = document.createElement('div');
45
- Object.assign(container.style, {
51
+
52
+ // Using popover="manual" to place the element in the browser's top layer.
53
+ // This ensures the drag preview container is visually on top of everything else,
54
+ // including elements using z-index and other stacking contexts.
55
+ // "manual" means no light dismiss — we manage the lifecycle ourselves.
56
+ // This element is only created for a single frame for the browser to take a
57
+ // photo of it, and then it is destroyed.
58
+ // Falls back to position:fixed + maxZIndex when the popover API is not available.
59
+ // Note: the popover attribute must be set before the element is in the DOM.
60
+ // `.showPopover()` is called further below after the element is appended to the body.
61
+ if ((0, _supportsPopover.supportsPopover)()) {
62
+ container.setAttribute('popover', 'manual');
63
+ }
64
+ Object.assign(container.style, _objectSpread(_objectSpread({
46
65
  // Ensuring we don't cause reflow when adding the element to the page
47
66
  // Using `position:fixed` rather than `position:absolute` so we are
48
67
  // positioned on the current viewport.
49
68
  // `position:fixed` also creates a new stacking context, so we don't need to do that here
50
- position: 'fixed',
69
+ position: 'fixed'
70
+ }, (0, _supportsPopover.supportsPopover)() ?
71
+ // needs to come first as it has 'inset: unset' which
72
+ // needs to be overridden by our top / left values
73
+ _popoverResetStyles.popoverResetUserAgentStyles : {
74
+ // Fallback: using maximum possible z-index so that this element
75
+ // will always be on top of other positioned content.
76
+ zIndex: _maxZIndex.maxZIndex
77
+ }), {}, {
51
78
  // According to `mdn`, the element can be offscreen:
52
79
  // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage#imgelement
53
80
  //
@@ -57,19 +84,15 @@ function setCustomNativeDragPreview(_ref) {
57
84
  // If the element is _completely_ offscreen, Safari@17.1 will cancel the drag
58
85
  top: 0,
59
86
  left: 0,
60
- // Using maximum possible z-index so that this element will always be on top
61
- // https://stackoverflow.com/questions/491052/minimum-and-maximum-value-of-z-index
62
- // Did not use `layers` in `@atlaskit/theme` because:
63
- // 1. This element is not a 'layer' in the conventional sense, as this element
64
- // is only created for a single frame for the browser to take a photo of it,
65
- // and then it is destroyed
66
- // 2. Did not want to add a dependency onto `@atlaskit/theme`
67
- // 3. Want to always be on top of product UI which might have higher z-index's
68
- zIndex: _maxZIndex.maxZIndex,
69
87
  // Avoiding any additional events caused by the new element (being super safe)
70
88
  pointerEvents: 'none'
71
- });
89
+ }));
72
90
  document.body.append(container);
91
+
92
+ // `.showPopover()` must be called after the element is in the DOM.
93
+ if ((0, _supportsPopover.supportsPopover)()) {
94
+ container.showPopover();
95
+ }
73
96
  var unmount = render({
74
97
  container: container
75
98
  });
@@ -107,6 +130,10 @@ function setCustomNativeDragPreview(_ref) {
107
130
  * Adding a parent element of the `container` with a background color (eg "white")
108
131
  * → Wrecks the opacity of the drag preview element
109
132
  */
133
+ /**
134
+ * Update: this bug fix is no longer needed in `Safari@26.4` (and maybe earlier)
135
+ * We can remove it in a future release
136
+ */
110
137
  if ((0, _isSafari.isSafari)()) {
111
138
  var rect = container.getBoundingClientRect();
112
139
 
@@ -119,6 +146,8 @@ function setCustomNativeDragPreview(_ref) {
119
146
  nativeSetDragImage === null || nativeSetDragImage === void 0 || nativeSetDragImage(container, previewOffset.x, previewOffset.y);
120
147
  });
121
148
  function cleanup() {
149
+ // No need to call `.hidePopover()`.
150
+ // Removing the element from the DOM dismisses the popover
122
151
  unbindMonitor();
123
152
  unmount === null || unmount === void 0 || unmount();
124
153
  document.body.removeChild(container);
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.popoverResetUserAgentStyles = void 0;
7
+ /**
8
+ * Resets user agent styles for `[popover]` elements.
9
+ *
10
+ * Browsers apply these defaults to popovers (per the WHATWG HTML spec rendering section):
11
+ * - `inset: 0` + `margin: auto` → centers the popover in the viewport
12
+ * - `border: solid` → visible border
13
+ * - `padding: 0.25em` → internal spacing
14
+ * - `overflow: auto` → scrollbars when content overflows
15
+ * - `color: CanvasText` + `background-color: Canvas` → system theme colors
16
+ *
17
+ * This object neutralizes those defaults so the popover behaves like a plain
18
+ * positioned element. `width` and `height` (UA default: `fit-content`) are not
19
+ * reset here because consumers set their own dimensions.
20
+ */
21
+ var popoverResetUserAgentStyles = exports.popoverResetUserAgentStyles = {
22
+ inset: 'unset',
23
+ border: 'none',
24
+ padding: 0,
25
+ margin: 0,
26
+ overflow: 'visible',
27
+ color: 'inherit',
28
+ background: 'transparent'
29
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.supportsPopover = void 0;
7
+ var _once = require("../public-utils/once");
8
+ // Using `once` as the value won't change in a browser
9
+
10
+ /**
11
+ * Returns `true` if the browser supports the Popover API (top-layer).
12
+ *
13
+ * When supported, elements with `popover="manual"` can be promoted to the
14
+ * browser's top layer via `element.showPopover()`, which is more reliable
15
+ * than `z-index` for ensuring an element renders above everything else.
16
+ *
17
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
18
+ */
19
+ var supportsPopover = exports.supportsPopover = (0, _once.once)(function supportsPopover() {
20
+ return typeof HTMLElement !== 'undefined' && typeof HTMLElement.prototype.showPopover === 'function';
21
+ });
@@ -1,5 +1,7 @@
1
1
  import { bind, bindAll } from 'bind-event-listener';
2
2
  import { maxZIndex } from '../util/max-z-index';
3
+ import { popoverResetUserAgentStyles } from '../util/popover-reset-styles';
4
+ import { supportsPopover } from '../util/supports-popover';
3
5
  import { honeyPotDataAttribute } from './honey-pot-data-attribute';
4
6
  const honeyPotSize = 2;
5
7
  const halfHoneyPotSize = honeyPotSize / 2;
@@ -130,33 +132,49 @@ function mountHoneyPot({
130
132
  const element = document.createElement('div');
131
133
  element.setAttribute(honeyPotDataAttribute, 'true');
132
134
 
135
+ // Using popover="manual" to place the element in the browser's top layer.
136
+ // This ensures the honey pot is visually on top of everything else,
137
+ // including elements using z-index and other stacking contexts.
138
+ // "manual" means no light dismiss — we manage the lifecycle ourselves.
139
+ // Falls back to position:fixed + maxZIndex when the popover API is not available.
140
+ if (supportsPopover()) {
141
+ element.setAttribute('popover', 'manual');
142
+ }
143
+
133
144
  // can shift during the drag thanks to Firefox
134
145
  let clientRect = getHoneyPotRectFor({
135
146
  client: initial
136
147
  });
137
148
  Object.assign(element.style, {
149
+ position: 'fixed',
150
+ ...(supportsPopover() ?
151
+ // needs to come first as it has 'inset: unset' which
152
+ // needs to be overridden by our top / left values
153
+ popoverResetUserAgentStyles : {
154
+ // Fallback: using maximum possible z-index so that this element
155
+ // will always be on top of other positioned content.
156
+ zIndex: maxZIndex
157
+ }),
138
158
  // Setting a background color explicitly to avoid any inherited styles.
139
159
  // Looks like this could be `opacity: 0`, but worried that _might_
140
160
  // cause the element to be ignored on some platforms.
141
161
  // When debugging, set backgroundColor to something like "red".
142
162
  backgroundColor: 'transparent',
143
- position: 'fixed',
144
163
  // Being explicit to avoid inheriting styles
145
164
  padding: 0,
146
165
  margin: 0,
147
166
  boxSizing: 'border-box',
148
- ...getRectStyles({
149
- clientRect
150
- }),
151
167
  // We want this element to absorb pointer events,
152
168
  // it's kind of the whole point 😉
153
169
  pointerEvents: 'auto',
154
- // Want to make sure the honey pot is top of everything else.
155
- // Don't need to worry about native drag previews, as they will
156
- // have been rendered (and removed) before the honey pot is rendered
157
- zIndex: maxZIndex
170
+ ...getRectStyles({
171
+ clientRect
172
+ })
158
173
  });
159
174
  document.body.appendChild(element);
175
+ if (supportsPopover()) {
176
+ element.showPopover();
177
+ }
160
178
 
161
179
  /**
162
180
  * 🦊 In firefox we can get `"pointermove"` events after the drag
@@ -1,6 +1,8 @@
1
1
  import { monitorForElements } from '../../../adapter/element-adapter';
2
2
  import { isSafari } from '../../../util/is-safari';
3
3
  import { maxZIndex } from '../../../util/max-z-index';
4
+ import { popoverResetUserAgentStyles } from '../../../util/popover-reset-styles';
5
+ import { supportsPopover } from '../../../util/supports-popover';
4
6
 
5
7
  /** A function to remove the element that has been added to the `container`.
6
8
  * @example () => ReactDOM.unmountComponentAtNode(container)
@@ -37,12 +39,33 @@ export function setCustomNativeDragPreview({
37
39
  getOffset = defaultOffset
38
40
  }) {
39
41
  const container = document.createElement('div');
42
+
43
+ // Using popover="manual" to place the element in the browser's top layer.
44
+ // This ensures the drag preview container is visually on top of everything else,
45
+ // including elements using z-index and other stacking contexts.
46
+ // "manual" means no light dismiss — we manage the lifecycle ourselves.
47
+ // This element is only created for a single frame for the browser to take a
48
+ // photo of it, and then it is destroyed.
49
+ // Falls back to position:fixed + maxZIndex when the popover API is not available.
50
+ // Note: the popover attribute must be set before the element is in the DOM.
51
+ // `.showPopover()` is called further below after the element is appended to the body.
52
+ if (supportsPopover()) {
53
+ container.setAttribute('popover', 'manual');
54
+ }
40
55
  Object.assign(container.style, {
41
56
  // Ensuring we don't cause reflow when adding the element to the page
42
57
  // Using `position:fixed` rather than `position:absolute` so we are
43
58
  // positioned on the current viewport.
44
59
  // `position:fixed` also creates a new stacking context, so we don't need to do that here
45
60
  position: 'fixed',
61
+ ...(supportsPopover() ?
62
+ // needs to come first as it has 'inset: unset' which
63
+ // needs to be overridden by our top / left values
64
+ popoverResetUserAgentStyles : {
65
+ // Fallback: using maximum possible z-index so that this element
66
+ // will always be on top of other positioned content.
67
+ zIndex: maxZIndex
68
+ }),
46
69
  // According to `mdn`, the element can be offscreen:
47
70
  // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage#imgelement
48
71
  //
@@ -52,19 +75,15 @@ export function setCustomNativeDragPreview({
52
75
  // If the element is _completely_ offscreen, Safari@17.1 will cancel the drag
53
76
  top: 0,
54
77
  left: 0,
55
- // Using maximum possible z-index so that this element will always be on top
56
- // https://stackoverflow.com/questions/491052/minimum-and-maximum-value-of-z-index
57
- // Did not use `layers` in `@atlaskit/theme` because:
58
- // 1. This element is not a 'layer' in the conventional sense, as this element
59
- // is only created for a single frame for the browser to take a photo of it,
60
- // and then it is destroyed
61
- // 2. Did not want to add a dependency onto `@atlaskit/theme`
62
- // 3. Want to always be on top of product UI which might have higher z-index's
63
- zIndex: maxZIndex,
64
78
  // Avoiding any additional events caused by the new element (being super safe)
65
79
  pointerEvents: 'none'
66
80
  });
67
81
  document.body.append(container);
82
+
83
+ // `.showPopover()` must be called after the element is in the DOM.
84
+ if (supportsPopover()) {
85
+ container.showPopover();
86
+ }
68
87
  const unmount = render({
69
88
  container
70
89
  });
@@ -102,6 +121,10 @@ export function setCustomNativeDragPreview({
102
121
  * Adding a parent element of the `container` with a background color (eg "white")
103
122
  * → Wrecks the opacity of the drag preview element
104
123
  */
124
+ /**
125
+ * Update: this bug fix is no longer needed in `Safari@26.4` (and maybe earlier)
126
+ * We can remove it in a future release
127
+ */
105
128
  if (isSafari()) {
106
129
  const rect = container.getBoundingClientRect();
107
130
 
@@ -114,6 +137,8 @@ export function setCustomNativeDragPreview({
114
137
  nativeSetDragImage === null || nativeSetDragImage === void 0 ? void 0 : nativeSetDragImage(container, previewOffset.x, previewOffset.y);
115
138
  });
116
139
  function cleanup() {
140
+ // No need to call `.hidePopover()`.
141
+ // Removing the element from the DOM dismisses the popover
117
142
  unbindMonitor();
118
143
  unmount === null || unmount === void 0 ? void 0 : unmount();
119
144
  document.body.removeChild(container);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Resets user agent styles for `[popover]` elements.
3
+ *
4
+ * Browsers apply these defaults to popovers (per the WHATWG HTML spec rendering section):
5
+ * - `inset: 0` + `margin: auto` → centers the popover in the viewport
6
+ * - `border: solid` → visible border
7
+ * - `padding: 0.25em` → internal spacing
8
+ * - `overflow: auto` → scrollbars when content overflows
9
+ * - `color: CanvasText` + `background-color: Canvas` → system theme colors
10
+ *
11
+ * This object neutralizes those defaults so the popover behaves like a plain
12
+ * positioned element. `width` and `height` (UA default: `fit-content`) are not
13
+ * reset here because consumers set their own dimensions.
14
+ */
15
+ export const popoverResetUserAgentStyles = {
16
+ inset: 'unset',
17
+ border: 'none',
18
+ padding: 0,
19
+ margin: 0,
20
+ overflow: 'visible',
21
+ color: 'inherit',
22
+ background: 'transparent'
23
+ };
@@ -0,0 +1,16 @@
1
+ import { once } from '../public-utils/once';
2
+
3
+ // Using `once` as the value won't change in a browser
4
+
5
+ /**
6
+ * Returns `true` if the browser supports the Popover API (top-layer).
7
+ *
8
+ * When supported, elements with `popover="manual"` can be promoted to the
9
+ * browser's top layer via `element.showPopover()`, which is more reliable
10
+ * than `z-index` for ensuring an element renders above everything else.
11
+ *
12
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
13
+ */
14
+ export const supportsPopover = once(function supportsPopover() {
15
+ return typeof HTMLElement !== 'undefined' && typeof HTMLElement.prototype.showPopover === 'function';
16
+ });
@@ -3,6 +3,8 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
4
  import { bind, bindAll } from 'bind-event-listener';
5
5
  import { maxZIndex } from '../util/max-z-index';
6
+ import { popoverResetUserAgentStyles } from '../util/popover-reset-styles';
7
+ import { supportsPopover } from '../util/supports-popover';
6
8
  import { honeyPotDataAttribute } from './honey-pot-data-attribute';
7
9
  var honeyPotSize = 2;
8
10
  var halfHoneyPotSize = honeyPotSize / 2;
@@ -129,33 +131,48 @@ function mountHoneyPot(_ref4) {
129
131
  var element = document.createElement('div');
130
132
  element.setAttribute(honeyPotDataAttribute, 'true');
131
133
 
134
+ // Using popover="manual" to place the element in the browser's top layer.
135
+ // This ensures the honey pot is visually on top of everything else,
136
+ // including elements using z-index and other stacking contexts.
137
+ // "manual" means no light dismiss — we manage the lifecycle ourselves.
138
+ // Falls back to position:fixed + maxZIndex when the popover API is not available.
139
+ if (supportsPopover()) {
140
+ element.setAttribute('popover', 'manual');
141
+ }
142
+
132
143
  // can shift during the drag thanks to Firefox
133
144
  var clientRect = getHoneyPotRectFor({
134
145
  client: initial
135
146
  });
136
147
  Object.assign(element.style, _objectSpread(_objectSpread({
148
+ position: 'fixed'
149
+ }, supportsPopover() ?
150
+ // needs to come first as it has 'inset: unset' which
151
+ // needs to be overridden by our top / left values
152
+ popoverResetUserAgentStyles : {
153
+ // Fallback: using maximum possible z-index so that this element
154
+ // will always be on top of other positioned content.
155
+ zIndex: maxZIndex
156
+ }), {}, {
137
157
  // Setting a background color explicitly to avoid any inherited styles.
138
158
  // Looks like this could be `opacity: 0`, but worried that _might_
139
159
  // cause the element to be ignored on some platforms.
140
160
  // When debugging, set backgroundColor to something like "red".
141
161
  backgroundColor: 'transparent',
142
- position: 'fixed',
143
162
  // Being explicit to avoid inheriting styles
144
163
  padding: 0,
145
164
  margin: 0,
146
- boxSizing: 'border-box'
147
- }, getRectStyles({
148
- clientRect: clientRect
149
- })), {}, {
165
+ boxSizing: 'border-box',
150
166
  // We want this element to absorb pointer events,
151
167
  // it's kind of the whole point 😉
152
- pointerEvents: 'auto',
153
- // Want to make sure the honey pot is top of everything else.
154
- // Don't need to worry about native drag previews, as they will
155
- // have been rendered (and removed) before the honey pot is rendered
156
- zIndex: maxZIndex
157
- }));
168
+ pointerEvents: 'auto'
169
+ }, getRectStyles({
170
+ clientRect: clientRect
171
+ })));
158
172
  document.body.appendChild(element);
173
+ if (supportsPopover()) {
174
+ element.showPopover();
175
+ }
159
176
 
160
177
  /**
161
178
  * 🦊 In firefox we can get `"pointermove"` events after the drag
@@ -1,6 +1,11 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
1
4
  import { monitorForElements } from '../../../adapter/element-adapter';
2
5
  import { isSafari } from '../../../util/is-safari';
3
6
  import { maxZIndex } from '../../../util/max-z-index';
7
+ import { popoverResetUserAgentStyles } from '../../../util/popover-reset-styles';
8
+ import { supportsPopover } from '../../../util/supports-popover';
4
9
 
5
10
  /** A function to remove the element that has been added to the `container`.
6
11
  * @example () => ReactDOM.unmountComponentAtNode(container)
@@ -37,12 +42,33 @@ export function setCustomNativeDragPreview(_ref) {
37
42
  _ref$getOffset = _ref.getOffset,
38
43
  getOffset = _ref$getOffset === void 0 ? defaultOffset : _ref$getOffset;
39
44
  var container = document.createElement('div');
40
- Object.assign(container.style, {
45
+
46
+ // Using popover="manual" to place the element in the browser's top layer.
47
+ // This ensures the drag preview container is visually on top of everything else,
48
+ // including elements using z-index and other stacking contexts.
49
+ // "manual" means no light dismiss — we manage the lifecycle ourselves.
50
+ // This element is only created for a single frame for the browser to take a
51
+ // photo of it, and then it is destroyed.
52
+ // Falls back to position:fixed + maxZIndex when the popover API is not available.
53
+ // Note: the popover attribute must be set before the element is in the DOM.
54
+ // `.showPopover()` is called further below after the element is appended to the body.
55
+ if (supportsPopover()) {
56
+ container.setAttribute('popover', 'manual');
57
+ }
58
+ Object.assign(container.style, _objectSpread(_objectSpread({
41
59
  // Ensuring we don't cause reflow when adding the element to the page
42
60
  // Using `position:fixed` rather than `position:absolute` so we are
43
61
  // positioned on the current viewport.
44
62
  // `position:fixed` also creates a new stacking context, so we don't need to do that here
45
- position: 'fixed',
63
+ position: 'fixed'
64
+ }, supportsPopover() ?
65
+ // needs to come first as it has 'inset: unset' which
66
+ // needs to be overridden by our top / left values
67
+ popoverResetUserAgentStyles : {
68
+ // Fallback: using maximum possible z-index so that this element
69
+ // will always be on top of other positioned content.
70
+ zIndex: maxZIndex
71
+ }), {}, {
46
72
  // According to `mdn`, the element can be offscreen:
47
73
  // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage#imgelement
48
74
  //
@@ -52,19 +78,15 @@ export function setCustomNativeDragPreview(_ref) {
52
78
  // If the element is _completely_ offscreen, Safari@17.1 will cancel the drag
53
79
  top: 0,
54
80
  left: 0,
55
- // Using maximum possible z-index so that this element will always be on top
56
- // https://stackoverflow.com/questions/491052/minimum-and-maximum-value-of-z-index
57
- // Did not use `layers` in `@atlaskit/theme` because:
58
- // 1. This element is not a 'layer' in the conventional sense, as this element
59
- // is only created for a single frame for the browser to take a photo of it,
60
- // and then it is destroyed
61
- // 2. Did not want to add a dependency onto `@atlaskit/theme`
62
- // 3. Want to always be on top of product UI which might have higher z-index's
63
- zIndex: maxZIndex,
64
81
  // Avoiding any additional events caused by the new element (being super safe)
65
82
  pointerEvents: 'none'
66
- });
83
+ }));
67
84
  document.body.append(container);
85
+
86
+ // `.showPopover()` must be called after the element is in the DOM.
87
+ if (supportsPopover()) {
88
+ container.showPopover();
89
+ }
68
90
  var unmount = render({
69
91
  container: container
70
92
  });
@@ -102,6 +124,10 @@ export function setCustomNativeDragPreview(_ref) {
102
124
  * Adding a parent element of the `container` with a background color (eg "white")
103
125
  * → Wrecks the opacity of the drag preview element
104
126
  */
127
+ /**
128
+ * Update: this bug fix is no longer needed in `Safari@26.4` (and maybe earlier)
129
+ * We can remove it in a future release
130
+ */
105
131
  if (isSafari()) {
106
132
  var rect = container.getBoundingClientRect();
107
133
 
@@ -114,6 +140,8 @@ export function setCustomNativeDragPreview(_ref) {
114
140
  nativeSetDragImage === null || nativeSetDragImage === void 0 || nativeSetDragImage(container, previewOffset.x, previewOffset.y);
115
141
  });
116
142
  function cleanup() {
143
+ // No need to call `.hidePopover()`.
144
+ // Removing the element from the DOM dismisses the popover
117
145
  unbindMonitor();
118
146
  unmount === null || unmount === void 0 || unmount();
119
147
  document.body.removeChild(container);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Resets user agent styles for `[popover]` elements.
3
+ *
4
+ * Browsers apply these defaults to popovers (per the WHATWG HTML spec rendering section):
5
+ * - `inset: 0` + `margin: auto` → centers the popover in the viewport
6
+ * - `border: solid` → visible border
7
+ * - `padding: 0.25em` → internal spacing
8
+ * - `overflow: auto` → scrollbars when content overflows
9
+ * - `color: CanvasText` + `background-color: Canvas` → system theme colors
10
+ *
11
+ * This object neutralizes those defaults so the popover behaves like a plain
12
+ * positioned element. `width` and `height` (UA default: `fit-content`) are not
13
+ * reset here because consumers set their own dimensions.
14
+ */
15
+ export var popoverResetUserAgentStyles = {
16
+ inset: 'unset',
17
+ border: 'none',
18
+ padding: 0,
19
+ margin: 0,
20
+ overflow: 'visible',
21
+ color: 'inherit',
22
+ background: 'transparent'
23
+ };
@@ -0,0 +1,16 @@
1
+ import { once } from '../public-utils/once';
2
+
3
+ // Using `once` as the value won't change in a browser
4
+
5
+ /**
6
+ * Returns `true` if the browser supports the Popover API (top-layer).
7
+ *
8
+ * When supported, elements with `popover="manual"` can be promoted to the
9
+ * browser's top layer via `element.showPopover()`, which is more reliable
10
+ * than `z-index` for ensuring an element renders above everything else.
11
+ *
12
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
13
+ */
14
+ export var supportsPopover = once(function supportsPopover() {
15
+ return typeof HTMLElement !== 'undefined' && typeof HTMLElement.prototype.showPopover === 'function';
16
+ });
@@ -0,0 +1,16 @@
1
+ import type { CSSProperties } from 'react';
2
+ /**
3
+ * Resets user agent styles for `[popover]` elements.
4
+ *
5
+ * Browsers apply these defaults to popovers (per the WHATWG HTML spec rendering section):
6
+ * - `inset: 0` + `margin: auto` → centers the popover in the viewport
7
+ * - `border: solid` → visible border
8
+ * - `padding: 0.25em` → internal spacing
9
+ * - `overflow: auto` → scrollbars when content overflows
10
+ * - `color: CanvasText` + `background-color: Canvas` → system theme colors
11
+ *
12
+ * This object neutralizes those defaults so the popover behaves like a plain
13
+ * positioned element. `width` and `height` (UA default: `fit-content`) are not
14
+ * reset here because consumers set their own dimensions.
15
+ */
16
+ export declare const popoverResetUserAgentStyles: CSSProperties;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns `true` if the browser supports the Popover API (top-layer).
3
+ *
4
+ * When supported, elements with `popover="manual"` can be promoted to the
5
+ * browser's top layer via `element.showPopover()`, which is more reliable
6
+ * than `z-index` for ensuring an element renders above everything else.
7
+ *
8
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
9
+ */
10
+ export declare const supportsPopover: () => boolean;
@@ -0,0 +1,16 @@
1
+ import type { CSSProperties } from 'react';
2
+ /**
3
+ * Resets user agent styles for `[popover]` elements.
4
+ *
5
+ * Browsers apply these defaults to popovers (per the WHATWG HTML spec rendering section):
6
+ * - `inset: 0` + `margin: auto` → centers the popover in the viewport
7
+ * - `border: solid` → visible border
8
+ * - `padding: 0.25em` → internal spacing
9
+ * - `overflow: auto` → scrollbars when content overflows
10
+ * - `color: CanvasText` + `background-color: Canvas` → system theme colors
11
+ *
12
+ * This object neutralizes those defaults so the popover behaves like a plain
13
+ * positioned element. `width` and `height` (UA default: `fit-content`) are not
14
+ * reset here because consumers set their own dimensions.
15
+ */
16
+ export declare const popoverResetUserAgentStyles: CSSProperties;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns `true` if the browser supports the Popover API (top-layer).
3
+ *
4
+ * When supported, elements with `popover="manual"` can be promoted to the
5
+ * browser's top layer via `element.showPopover()`, which is more reliable
6
+ * than `z-index` for ensuring an element renders above everything else.
7
+ *
8
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
9
+ */
10
+ export declare const supportsPopover: () => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/pragmatic-drag-and-drop",
3
- "version": "1.7.9",
3
+ "version": "1.8.0",
4
4
  "description": "The core package for Pragmatic drag and drop - enabling fast drag and drop for any experience on any tech stack",
5
5
  "repository": "https://github.com/atlassian/pragmatic-drag-and-drop",
6
6
  "author": "Atlassian Pty Ltd",
@@ -42,8 +42,8 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@af/integration-testing": "workspace:^",
45
- "@atlaskit/link": "^3.3.0",
46
- "@atlassian/a11y-jest-testing": "^0.10.0",
45
+ "@atlaskit/link": "^3.4.0",
46
+ "@atlassian/a11y-jest-testing": "^0.11.0",
47
47
  "@emotion/react": "^11.7.1",
48
48
  "@testing-library/dom": "^10.1.0",
49
49
  "@testing-library/react": "^16.3.0",