@atlaskit/onboarding 11.7.2 → 11.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,13 @@
1
1
  # @atlaskit/onboarding
2
2
 
3
+ ## 11.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#114901](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/114901)
8
+ [`61f6a496bcddf`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/61f6a496bcddf) -
9
+ Added a possible fix (behind a feature flag) for a positioning bug with the Spotlight component.
10
+
3
11
  ## 11.7.2
4
12
 
5
13
  ### Patch Changes
@@ -27,7 +27,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
27
27
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
28
28
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
29
29
  var packageName = "@atlaskit/onboarding";
30
- var packageVersion = "11.7.2";
30
+ var packageVersion = "11.8.0";
31
31
  var SpotlightDialog = /*#__PURE__*/function (_Component) {
32
32
  (0, _inherits2.default)(SpotlightDialog, _Component);
33
33
  var _super = _createSuper(SpotlightDialog);
@@ -8,6 +8,9 @@ exports.useElementBox = exports.ElementBox = void 0;
8
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
9
  var _react = require("react");
10
10
  var _bindEventListener = require("bind-event-listener");
11
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
+ // The minimum interval between position updates in milliseconds
13
+ var POSITION_UPDATE_INTERVAL = 200;
11
14
  var getElementRect = function getElementRect(element) {
12
15
  var _element$getBoundingC = element.getBoundingClientRect(),
13
16
  height = _element$getBoundingC.height,
@@ -21,13 +24,7 @@ var getElementRect = function getElementRect(element) {
21
24
  width: width
22
25
  };
23
26
  };
24
-
25
- /**
26
- * Will listen to the document resizing to see if an element has moved positions.
27
- * Not using ResizeObserver because of IE11 support.
28
- * @param element HTMLElement to watch when resizing.
29
- */
30
- var useElementBox = exports.useElementBox = function useElementBox(element) {
27
+ var useResizeAwareElementBox = function useResizeAwareElementBox(element, updateMethod) {
31
28
  var _useState = (0, _react.useState)({
32
29
  width: 0,
33
30
  height: 0,
@@ -38,22 +35,112 @@ var useElementBox = exports.useElementBox = function useElementBox(element) {
38
35
  box = _useState2[0],
39
36
  setBox = _useState2[1];
40
37
  (0, _react.useLayoutEffect)(function () {
41
- setBox(getElementRect(element));
42
- }, [element]);
38
+ if (updateMethod === 'resizeListener') {
39
+ setBox(getElementRect(element));
40
+ }
41
+ }, [element, updateMethod]);
43
42
  (0, _react.useEffect)(function () {
44
43
  var onResize = function onResize() {
45
44
  requestAnimationFrame(function () {
46
45
  setBox(getElementRect(element));
47
46
  });
48
47
  };
49
- return (0, _bindEventListener.bind)(window, {
50
- type: 'resize',
51
- listener: onResize
52
- });
48
+ if (updateMethod === 'resizeListener') {
49
+ return (0, _bindEventListener.bind)(window, {
50
+ type: 'resize',
51
+ listener: onResize
52
+ });
53
+ }
54
+ }, [element, updateMethod]);
55
+ return box;
56
+ };
57
+ var usePollingElementBox = function usePollingElementBox(element, updateMethod) {
58
+ // These are intentionally tracked as number primitives rather than as a shared `box` object.
59
+ // Since the requestAnimationFrame code below updates this often, we want to avoid re-renders
60
+ // when the values are the same. React uses `Object.is` to figure out if the state changed after a setState.
61
+ // If we represent this as a shared `box` object, this will re-render even if the two objects have identical contents.
62
+ var _useState3 = (0, _react.useState)(0),
63
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
64
+ width = _useState4[0],
65
+ setWidth = _useState4[1];
66
+ var _useState5 = (0, _react.useState)(0),
67
+ _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
68
+ height = _useState6[0],
69
+ setHeight = _useState6[1];
70
+ var _useState7 = (0, _react.useState)(0),
71
+ _useState8 = (0, _slicedToArray2.default)(_useState7, 2),
72
+ left = _useState8[0],
73
+ setLeft = _useState8[1];
74
+ var _useState9 = (0, _react.useState)(0),
75
+ _useState10 = (0, _slicedToArray2.default)(_useState9, 2),
76
+ top = _useState10[0],
77
+ setTop = _useState10[1];
78
+ (0, _react.useLayoutEffect)(function () {
79
+ if (updateMethod === 'polling') {
80
+ var newBox = getElementRect(element);
81
+ setWidth(newBox.width);
82
+ setHeight(newBox.height);
83
+ setLeft(newBox.left);
84
+ setTop(newBox.top);
85
+ }
86
+ }, [element, updateMethod]);
87
+
88
+ // Souce: https://css-tricks.com/using-requestanimationframe-with-react-hooks/
89
+ // Use useRef for mutable variables that we want to persist
90
+ // without triggering a re-render on their change
91
+ var requestRef = (0, _react.useRef)();
92
+ var previousUpdateTimeRef = (0, _react.useRef)();
93
+ var animate = (0, _react.useCallback)(function (time) {
94
+ if (previousUpdateTimeRef.current !== undefined) {
95
+ var timeSinceLastUpdate = time - previousUpdateTimeRef.current;
96
+ if (timeSinceLastUpdate > POSITION_UPDATE_INTERVAL) {
97
+ var newBox = getElementRect(element);
98
+ setWidth(newBox.width);
99
+ setHeight(newBox.height);
100
+ setLeft(newBox.left);
101
+ setTop(newBox.top);
102
+ previousUpdateTimeRef.current = time;
103
+ }
104
+ } else {
105
+ // Initialize previousUpdateTimeRef
106
+ previousUpdateTimeRef.current = time;
107
+ }
108
+ requestRef.current = requestAnimationFrame(animate);
53
109
  }, [element]);
110
+ (0, _react.useEffect)(function () {
111
+ if (updateMethod === 'polling') {
112
+ requestRef.current = requestAnimationFrame(animate);
113
+ }
114
+ return function () {
115
+ if (requestRef.current !== undefined) {
116
+ cancelAnimationFrame(requestRef.current);
117
+ }
118
+ };
119
+ // This useEffect should only run on mount and when `element` or `updateMethod` changes.
120
+ }, [animate, element, updateMethod]);
121
+ var box = (0, _react.useMemo)(function () {
122
+ return {
123
+ width: width,
124
+ height: height,
125
+ left: left,
126
+ top: top
127
+ };
128
+ }, [width, height, left, top]);
54
129
  return box;
55
130
  };
56
131
 
132
+ /**
133
+ * Will listen to the document resizing to see if an element has moved positions.
134
+ * Not using ResizeObserver because of IE11 support.
135
+ * @param element HTMLElement to watch when resizing.
136
+ */
137
+ var useElementBox = exports.useElementBox = function useElementBox(element) {
138
+ var updateMethod = (0, _platformFeatureFlags.getBooleanFF)('platform.design-system.refresh-spotlight-on-interval') ? 'polling' : 'resizeListener';
139
+ var boxViaResizeListener = useResizeAwareElementBox(element, updateMethod);
140
+ var boxViaPolling = usePollingElementBox(element, updateMethod);
141
+ return updateMethod === 'resizeListener' ? boxViaResizeListener : boxViaPolling;
142
+ };
143
+
57
144
  /**
58
145
  * __Element box__
59
146
  *
@@ -7,7 +7,7 @@ import { DialogImage } from '../styled/dialog';
7
7
  import SpotlightCard from './spotlight-card';
8
8
  import ValueChanged from './value-changed';
9
9
  const packageName = "@atlaskit/onboarding";
10
- const packageVersion = "11.7.2";
10
+ const packageVersion = "11.8.0";
11
11
  class SpotlightDialog extends Component {
12
12
  constructor(...args) {
13
13
  super(...args);
@@ -1,5 +1,8 @@
1
- import { useEffect, useLayoutEffect, useState } from 'react';
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
4
+ // The minimum interval between position updates in milliseconds
5
+ const POSITION_UPDATE_INTERVAL = 200;
3
6
  const getElementRect = element => {
4
7
  const {
5
8
  height,
@@ -14,13 +17,7 @@ const getElementRect = element => {
14
17
  width
15
18
  };
16
19
  };
17
-
18
- /**
19
- * Will listen to the document resizing to see if an element has moved positions.
20
- * Not using ResizeObserver because of IE11 support.
21
- * @param element HTMLElement to watch when resizing.
22
- */
23
- export const useElementBox = element => {
20
+ const useResizeAwareElementBox = (element, updateMethod) => {
24
21
  const [box, setBox] = useState({
25
22
  width: 0,
26
23
  height: 0,
@@ -28,22 +25,98 @@ export const useElementBox = element => {
28
25
  top: 0
29
26
  });
30
27
  useLayoutEffect(() => {
31
- setBox(getElementRect(element));
32
- }, [element]);
28
+ if (updateMethod === 'resizeListener') {
29
+ setBox(getElementRect(element));
30
+ }
31
+ }, [element, updateMethod]);
33
32
  useEffect(() => {
34
33
  const onResize = () => {
35
34
  requestAnimationFrame(() => {
36
35
  setBox(getElementRect(element));
37
36
  });
38
37
  };
39
- return bind(window, {
40
- type: 'resize',
41
- listener: onResize
42
- });
38
+ if (updateMethod === 'resizeListener') {
39
+ return bind(window, {
40
+ type: 'resize',
41
+ listener: onResize
42
+ });
43
+ }
44
+ }, [element, updateMethod]);
45
+ return box;
46
+ };
47
+ const usePollingElementBox = (element, updateMethod) => {
48
+ // These are intentionally tracked as number primitives rather than as a shared `box` object.
49
+ // Since the requestAnimationFrame code below updates this often, we want to avoid re-renders
50
+ // when the values are the same. React uses `Object.is` to figure out if the state changed after a setState.
51
+ // If we represent this as a shared `box` object, this will re-render even if the two objects have identical contents.
52
+ const [width, setWidth] = useState(0);
53
+ const [height, setHeight] = useState(0);
54
+ const [left, setLeft] = useState(0);
55
+ const [top, setTop] = useState(0);
56
+ useLayoutEffect(() => {
57
+ if (updateMethod === 'polling') {
58
+ const newBox = getElementRect(element);
59
+ setWidth(newBox.width);
60
+ setHeight(newBox.height);
61
+ setLeft(newBox.left);
62
+ setTop(newBox.top);
63
+ }
64
+ }, [element, updateMethod]);
65
+
66
+ // Souce: https://css-tricks.com/using-requestanimationframe-with-react-hooks/
67
+ // Use useRef for mutable variables that we want to persist
68
+ // without triggering a re-render on their change
69
+ const requestRef = useRef();
70
+ const previousUpdateTimeRef = useRef();
71
+ const animate = useCallback(time => {
72
+ if (previousUpdateTimeRef.current !== undefined) {
73
+ const timeSinceLastUpdate = time - previousUpdateTimeRef.current;
74
+ if (timeSinceLastUpdate > POSITION_UPDATE_INTERVAL) {
75
+ const newBox = getElementRect(element);
76
+ setWidth(newBox.width);
77
+ setHeight(newBox.height);
78
+ setLeft(newBox.left);
79
+ setTop(newBox.top);
80
+ previousUpdateTimeRef.current = time;
81
+ }
82
+ } else {
83
+ // Initialize previousUpdateTimeRef
84
+ previousUpdateTimeRef.current = time;
85
+ }
86
+ requestRef.current = requestAnimationFrame(animate);
43
87
  }, [element]);
88
+ useEffect(() => {
89
+ if (updateMethod === 'polling') {
90
+ requestRef.current = requestAnimationFrame(animate);
91
+ }
92
+ return () => {
93
+ if (requestRef.current !== undefined) {
94
+ cancelAnimationFrame(requestRef.current);
95
+ }
96
+ };
97
+ // This useEffect should only run on mount and when `element` or `updateMethod` changes.
98
+ }, [animate, element, updateMethod]);
99
+ const box = useMemo(() => ({
100
+ width,
101
+ height,
102
+ left,
103
+ top
104
+ }), [width, height, left, top]);
44
105
  return box;
45
106
  };
46
107
 
108
+ /**
109
+ * Will listen to the document resizing to see if an element has moved positions.
110
+ * Not using ResizeObserver because of IE11 support.
111
+ * @param element HTMLElement to watch when resizing.
112
+ */
113
+ export const useElementBox = element => {
114
+ const updateMethod = getBooleanFF('platform.design-system.refresh-spotlight-on-interval') ? 'polling' : 'resizeListener';
115
+ const boxViaResizeListener = useResizeAwareElementBox(element, updateMethod);
116
+ const boxViaPolling = usePollingElementBox(element, updateMethod);
117
+ return updateMethod === 'resizeListener' ? boxViaResizeListener : boxViaPolling;
118
+ };
119
+
47
120
  /**
48
121
  * __Element box__
49
122
  *
@@ -17,7 +17,7 @@ import { DialogImage } from '../styled/dialog';
17
17
  import SpotlightCard from './spotlight-card';
18
18
  import ValueChanged from './value-changed';
19
19
  var packageName = "@atlaskit/onboarding";
20
- var packageVersion = "11.7.2";
20
+ var packageVersion = "11.8.0";
21
21
  var SpotlightDialog = /*#__PURE__*/function (_Component) {
22
22
  _inherits(SpotlightDialog, _Component);
23
23
  var _super = _createSuper(SpotlightDialog);
@@ -1,6 +1,9 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
- import { useEffect, useLayoutEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
3
  import { bind } from 'bind-event-listener';
4
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
+ // The minimum interval between position updates in milliseconds
6
+ var POSITION_UPDATE_INTERVAL = 200;
4
7
  var getElementRect = function getElementRect(element) {
5
8
  var _element$getBoundingC = element.getBoundingClientRect(),
6
9
  height = _element$getBoundingC.height,
@@ -14,13 +17,7 @@ var getElementRect = function getElementRect(element) {
14
17
  width: width
15
18
  };
16
19
  };
17
-
18
- /**
19
- * Will listen to the document resizing to see if an element has moved positions.
20
- * Not using ResizeObserver because of IE11 support.
21
- * @param element HTMLElement to watch when resizing.
22
- */
23
- export var useElementBox = function useElementBox(element) {
20
+ var useResizeAwareElementBox = function useResizeAwareElementBox(element, updateMethod) {
24
21
  var _useState = useState({
25
22
  width: 0,
26
23
  height: 0,
@@ -31,22 +28,112 @@ export var useElementBox = function useElementBox(element) {
31
28
  box = _useState2[0],
32
29
  setBox = _useState2[1];
33
30
  useLayoutEffect(function () {
34
- setBox(getElementRect(element));
35
- }, [element]);
31
+ if (updateMethod === 'resizeListener') {
32
+ setBox(getElementRect(element));
33
+ }
34
+ }, [element, updateMethod]);
36
35
  useEffect(function () {
37
36
  var onResize = function onResize() {
38
37
  requestAnimationFrame(function () {
39
38
  setBox(getElementRect(element));
40
39
  });
41
40
  };
42
- return bind(window, {
43
- type: 'resize',
44
- listener: onResize
45
- });
41
+ if (updateMethod === 'resizeListener') {
42
+ return bind(window, {
43
+ type: 'resize',
44
+ listener: onResize
45
+ });
46
+ }
47
+ }, [element, updateMethod]);
48
+ return box;
49
+ };
50
+ var usePollingElementBox = function usePollingElementBox(element, updateMethod) {
51
+ // These are intentionally tracked as number primitives rather than as a shared `box` object.
52
+ // Since the requestAnimationFrame code below updates this often, we want to avoid re-renders
53
+ // when the values are the same. React uses `Object.is` to figure out if the state changed after a setState.
54
+ // If we represent this as a shared `box` object, this will re-render even if the two objects have identical contents.
55
+ var _useState3 = useState(0),
56
+ _useState4 = _slicedToArray(_useState3, 2),
57
+ width = _useState4[0],
58
+ setWidth = _useState4[1];
59
+ var _useState5 = useState(0),
60
+ _useState6 = _slicedToArray(_useState5, 2),
61
+ height = _useState6[0],
62
+ setHeight = _useState6[1];
63
+ var _useState7 = useState(0),
64
+ _useState8 = _slicedToArray(_useState7, 2),
65
+ left = _useState8[0],
66
+ setLeft = _useState8[1];
67
+ var _useState9 = useState(0),
68
+ _useState10 = _slicedToArray(_useState9, 2),
69
+ top = _useState10[0],
70
+ setTop = _useState10[1];
71
+ useLayoutEffect(function () {
72
+ if (updateMethod === 'polling') {
73
+ var newBox = getElementRect(element);
74
+ setWidth(newBox.width);
75
+ setHeight(newBox.height);
76
+ setLeft(newBox.left);
77
+ setTop(newBox.top);
78
+ }
79
+ }, [element, updateMethod]);
80
+
81
+ // Souce: https://css-tricks.com/using-requestanimationframe-with-react-hooks/
82
+ // Use useRef for mutable variables that we want to persist
83
+ // without triggering a re-render on their change
84
+ var requestRef = useRef();
85
+ var previousUpdateTimeRef = useRef();
86
+ var animate = useCallback(function (time) {
87
+ if (previousUpdateTimeRef.current !== undefined) {
88
+ var timeSinceLastUpdate = time - previousUpdateTimeRef.current;
89
+ if (timeSinceLastUpdate > POSITION_UPDATE_INTERVAL) {
90
+ var newBox = getElementRect(element);
91
+ setWidth(newBox.width);
92
+ setHeight(newBox.height);
93
+ setLeft(newBox.left);
94
+ setTop(newBox.top);
95
+ previousUpdateTimeRef.current = time;
96
+ }
97
+ } else {
98
+ // Initialize previousUpdateTimeRef
99
+ previousUpdateTimeRef.current = time;
100
+ }
101
+ requestRef.current = requestAnimationFrame(animate);
46
102
  }, [element]);
103
+ useEffect(function () {
104
+ if (updateMethod === 'polling') {
105
+ requestRef.current = requestAnimationFrame(animate);
106
+ }
107
+ return function () {
108
+ if (requestRef.current !== undefined) {
109
+ cancelAnimationFrame(requestRef.current);
110
+ }
111
+ };
112
+ // This useEffect should only run on mount and when `element` or `updateMethod` changes.
113
+ }, [animate, element, updateMethod]);
114
+ var box = useMemo(function () {
115
+ return {
116
+ width: width,
117
+ height: height,
118
+ left: left,
119
+ top: top
120
+ };
121
+ }, [width, height, left, top]);
47
122
  return box;
48
123
  };
49
124
 
125
+ /**
126
+ * Will listen to the document resizing to see if an element has moved positions.
127
+ * Not using ResizeObserver because of IE11 support.
128
+ * @param element HTMLElement to watch when resizing.
129
+ */
130
+ export var useElementBox = function useElementBox(element) {
131
+ var updateMethod = getBooleanFF('platform.design-system.refresh-spotlight-on-interval') ? 'polling' : 'resizeListener';
132
+ var boxViaResizeListener = useResizeAwareElementBox(element, updateMethod);
133
+ var boxViaPolling = usePollingElementBox(element, updateMethod);
134
+ return updateMethod === 'resizeListener' ? boxViaResizeListener : boxViaPolling;
135
+ };
136
+
50
137
  /**
51
138
  * __Element box__
52
139
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/onboarding",
3
- "version": "11.7.2",
3
+ "version": "11.8.0",
4
4
  "description": "An onboarding spotlight introduces new features to users through focused messages or multi-step tours.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -46,11 +46,12 @@
46
46
  "@atlaskit/heading": "^2.4.0",
47
47
  "@atlaskit/modal-dialog": "^12.14.0",
48
48
  "@atlaskit/motion": "^1.7.0",
49
+ "@atlaskit/platform-feature-flags": "^0.2.0",
49
50
  "@atlaskit/popper": "^6.1.0",
50
51
  "@atlaskit/portal": "^4.6.0",
51
- "@atlaskit/primitives": "^8.0.0",
52
+ "@atlaskit/primitives": "^8.2.0",
52
53
  "@atlaskit/theme": "^12.11.0",
53
- "@atlaskit/tokens": "^1.52.0",
54
+ "@atlaskit/tokens": "^1.53.0",
54
55
  "@babel/runtime": "^7.0.0",
55
56
  "@emotion/react": "^11.7.1",
56
57
  "bind-event-listener": "^3.0.0",
@@ -82,6 +83,11 @@
82
83
  "react-lorem-component": "^0.13.0",
83
84
  "typescript": "~5.4.2"
84
85
  },
86
+ "platform-feature-flags": {
87
+ "platform.design-system.refresh-spotlight-on-interval": {
88
+ "type": "boolean"
89
+ }
90
+ },
85
91
  "techstack": {
86
92
  "@atlassian/frontend": {
87
93
  "import-structure": "atlassian-conventions"