@atlaskit/top-layer 0.4.0 → 0.5.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,14 @@
1
1
  # @atlaskit/top-layer
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`6e656fce5ceee`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6e656fce5ceee) -
8
+ Fix anchor positioning so multiple popovers can share the same trigger element. Previously the
9
+ second `useAnchorPosition` call on the same trigger would overwrite the first call's `anchor-name`
10
+ style, leaving the first popover unpositioned.
11
+
3
12
  ## 0.4.0
4
13
 
5
14
  ### Minor Changes
@@ -234,7 +234,6 @@ function useAnchorPosition(_ref5) {
234
234
  forceFallbackPositioning = _ref5$forceFallbackPo === void 0 ? false : _ref5$forceFallbackPo,
235
235
  arrow = _ref5.arrow;
236
236
  var id = (0, _react.useId)();
237
- var anchorName = "--anchor-".concat(id.replace(/:/g, ''));
238
237
  (0, _react.useLayoutEffect)(function () {
239
238
  var trigger = anchorRef.current;
240
239
  var popover = popoverRef.current;
@@ -248,6 +247,9 @@ function useAnchorPosition(_ref5) {
248
247
  var crossAxisShiftCssValue = resolvedPlacement.offset.crossAxisShift.value;
249
248
  var shouldForceFallback = Boolean(forceFallbackPositioning);
250
249
  if (supportsAnchorPositioning() && !shouldForceFallback) {
250
+ // Reuse an existing `anchor-name` if the element already has one.
251
+ // Another popover might already be anchored to the old name.
252
+ var anchorName = trigger.style.getPropertyValue('anchor-name') || "--anchor-".concat(id.replace(/:/g, ''));
251
253
  var gap = edgeMargin({
252
254
  placement: resolvedPlacement,
253
255
  offset: gapCssValue
@@ -345,13 +347,26 @@ function useAnchorPosition(_ref5) {
345
347
  // because that's where useAnchorPosition sets the margin gap.
346
348
  popover.setAttribute('data-ds-popover-arrow', '');
347
349
  }
350
+
351
+ /**
352
+ * **We are never cleaning up anchor names**
353
+ *
354
+ * _Rationale_
355
+ *
356
+ * - Multiple popovers can share the same anchor element.
357
+ * - Sometimes the `useAnchorPosition` hook and `Popover`
358
+ * are not in the same component.
359
+ * - There can be times when, even with reference counting, we
360
+ * could remove the anchor-name while it's still being used
361
+ * by something else - especially with async react updates
362
+ * which can cause tearing
363
+ *
364
+ * _More_
365
+ *
366
+ * See `notes/decisions/anchor-name-lifetime.md`
367
+ */
368
+ trigger.style.setProperty('anchor-name', anchorName);
348
369
  var _undoPositioning = (0, _combine.combine)((0, _setStyle.setStyle)({
349
- el: trigger,
350
- styles: [{
351
- property: 'anchor-name',
352
- value: anchorName
353
- }]
354
- }), (0, _setStyle.setStyle)({
355
370
  el: popover,
356
371
  styles: popoverStyles
357
372
  }), arrow ? function () {
@@ -483,5 +498,5 @@ function useAnchorPosition(_ref5) {
483
498
  popover.style.removeProperty('opacity');
484
499
  });
485
500
  return undoPositioning;
486
- }, [anchorRef, popoverRef, placement, anchorName, forceFallbackPositioning, arrow]);
501
+ }, [anchorRef, popoverRef, placement, forceFallbackPositioning, arrow, id]);
487
502
  }
@@ -230,7 +230,6 @@ export function useAnchorPosition({
230
230
  arrow
231
231
  }) {
232
232
  const id = useId();
233
- const anchorName = `--anchor-${id.replace(/:/g, '')}`;
234
233
  useLayoutEffect(() => {
235
234
  const trigger = anchorRef.current;
236
235
  const popover = popoverRef.current;
@@ -244,6 +243,9 @@ export function useAnchorPosition({
244
243
  const crossAxisShiftCssValue = resolvedPlacement.offset.crossAxisShift.value;
245
244
  const shouldForceFallback = Boolean(forceFallbackPositioning);
246
245
  if (supportsAnchorPositioning() && !shouldForceFallback) {
246
+ // Reuse an existing `anchor-name` if the element already has one.
247
+ // Another popover might already be anchored to the old name.
248
+ const anchorName = trigger.style.getPropertyValue('anchor-name') || `--anchor-${id.replace(/:/g, '')}`;
247
249
  const gap = edgeMargin({
248
250
  placement: resolvedPlacement,
249
251
  offset: gapCssValue
@@ -341,13 +343,26 @@ export function useAnchorPosition({
341
343
  // because that's where useAnchorPosition sets the margin gap.
342
344
  popover.setAttribute('data-ds-popover-arrow', '');
343
345
  }
346
+
347
+ /**
348
+ * **We are never cleaning up anchor names**
349
+ *
350
+ * _Rationale_
351
+ *
352
+ * - Multiple popovers can share the same anchor element.
353
+ * - Sometimes the `useAnchorPosition` hook and `Popover`
354
+ * are not in the same component.
355
+ * - There can be times when, even with reference counting, we
356
+ * could remove the anchor-name while it's still being used
357
+ * by something else - especially with async react updates
358
+ * which can cause tearing
359
+ *
360
+ * _More_
361
+ *
362
+ * See `notes/decisions/anchor-name-lifetime.md`
363
+ */
364
+ trigger.style.setProperty('anchor-name', anchorName);
344
365
  const undoPositioning = combine(setStyle({
345
- el: trigger,
346
- styles: [{
347
- property: 'anchor-name',
348
- value: anchorName
349
- }]
350
- }), setStyle({
351
366
  el: popover,
352
367
  styles: popoverStyles
353
368
  }), arrow ? () => popover.removeAttribute('data-ds-popover-arrow') : () => {});
@@ -476,5 +491,5 @@ export function useAnchorPosition({
476
491
  popover.style.removeProperty('opacity');
477
492
  });
478
493
  return undoPositioning;
479
- }, [anchorRef, popoverRef, placement, anchorName, forceFallbackPositioning, arrow]);
494
+ }, [anchorRef, popoverRef, placement, forceFallbackPositioning, arrow, id]);
480
495
  }
@@ -220,7 +220,6 @@ export function useAnchorPosition(_ref5) {
220
220
  forceFallbackPositioning = _ref5$forceFallbackPo === void 0 ? false : _ref5$forceFallbackPo,
221
221
  arrow = _ref5.arrow;
222
222
  var id = useId();
223
- var anchorName = "--anchor-".concat(id.replace(/:/g, ''));
224
223
  useLayoutEffect(function () {
225
224
  var trigger = anchorRef.current;
226
225
  var popover = popoverRef.current;
@@ -234,6 +233,9 @@ export function useAnchorPosition(_ref5) {
234
233
  var crossAxisShiftCssValue = resolvedPlacement.offset.crossAxisShift.value;
235
234
  var shouldForceFallback = Boolean(forceFallbackPositioning);
236
235
  if (supportsAnchorPositioning() && !shouldForceFallback) {
236
+ // Reuse an existing `anchor-name` if the element already has one.
237
+ // Another popover might already be anchored to the old name.
238
+ var anchorName = trigger.style.getPropertyValue('anchor-name') || "--anchor-".concat(id.replace(/:/g, ''));
237
239
  var gap = edgeMargin({
238
240
  placement: resolvedPlacement,
239
241
  offset: gapCssValue
@@ -331,13 +333,26 @@ export function useAnchorPosition(_ref5) {
331
333
  // because that's where useAnchorPosition sets the margin gap.
332
334
  popover.setAttribute('data-ds-popover-arrow', '');
333
335
  }
336
+
337
+ /**
338
+ * **We are never cleaning up anchor names**
339
+ *
340
+ * _Rationale_
341
+ *
342
+ * - Multiple popovers can share the same anchor element.
343
+ * - Sometimes the `useAnchorPosition` hook and `Popover`
344
+ * are not in the same component.
345
+ * - There can be times when, even with reference counting, we
346
+ * could remove the anchor-name while it's still being used
347
+ * by something else - especially with async react updates
348
+ * which can cause tearing
349
+ *
350
+ * _More_
351
+ *
352
+ * See `notes/decisions/anchor-name-lifetime.md`
353
+ */
354
+ trigger.style.setProperty('anchor-name', anchorName);
334
355
  var _undoPositioning = combine(setStyle({
335
- el: trigger,
336
- styles: [{
337
- property: 'anchor-name',
338
- value: anchorName
339
- }]
340
- }), setStyle({
341
356
  el: popover,
342
357
  styles: popoverStyles
343
358
  }), arrow ? function () {
@@ -469,5 +484,5 @@ export function useAnchorPosition(_ref5) {
469
484
  popover.style.removeProperty('opacity');
470
485
  });
471
486
  return undoPositioning;
472
- }, [anchorRef, popoverRef, placement, anchorName, forceFallbackPositioning, arrow]);
487
+ }, [anchorRef, popoverRef, placement, forceFallbackPositioning, arrow, id]);
473
488
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/top-layer",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Low-level layering primitives based on popover and dialog",
5
5
  "homepage": "https://atlassian.design/components/top-layer",
6
6
  "repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",