@atlaskit/spotlight 0.4.1 → 0.5.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 +18 -0
- package/dist/cjs/controllers/context.js +73 -5
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/ui/UNSAFE_update-on-change/index.js +59 -0
- package/dist/cjs/ui/card/caret/index.js +2 -2
- package/dist/cjs/ui/card/index.js +2 -2
- package/dist/cjs/ui/popover-content/index.js +42 -8
- package/dist/cjs/utils/use-focus-within/index.js +51 -0
- package/dist/cjs/utils/use-on-escape/index.js +40 -0
- package/dist/es2019/controllers/context.js +27 -4
- package/dist/es2019/index.js +2 -1
- package/dist/es2019/ui/UNSAFE_update-on-change/index.js +53 -0
- package/dist/es2019/ui/card/caret/index.js +2 -2
- package/dist/es2019/ui/card/index.js +2 -2
- package/dist/es2019/ui/popover-content/index.js +36 -9
- package/dist/es2019/utils/use-focus-within/index.js +40 -0
- package/dist/es2019/utils/use-on-escape/index.js +31 -0
- package/dist/esm/controllers/context.js +73 -5
- package/dist/esm/index.js +2 -1
- package/dist/esm/ui/UNSAFE_update-on-change/index.js +53 -0
- package/dist/esm/ui/card/caret/index.js +2 -2
- package/dist/esm/ui/card/index.js +2 -2
- package/dist/esm/ui/popover-content/index.js +42 -9
- package/dist/esm/utils/use-focus-within/index.js +44 -0
- package/dist/esm/utils/use-on-escape/index.js +34 -0
- package/dist/types/controllers/context.d.ts +14 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/types/ui/UNSAFE_update-on-change/index.d.ts +6 -0
- package/dist/types/ui/popover-content/index.d.ts +2 -1
- package/dist/types/utils/use-focus-within/index.d.ts +2 -0
- package/dist/types/utils/use-on-escape/index.d.ts +1 -0
- package/dist/types-ts4.5/controllers/context.d.ts +14 -5
- package/dist/types-ts4.5/index.d.ts +1 -0
- package/dist/types-ts4.5/ui/UNSAFE_update-on-change/index.d.ts +6 -0
- package/dist/types-ts4.5/ui/popover-content/index.d.ts +2 -1
- package/dist/types-ts4.5/utils/use-focus-within/index.d.ts +2 -0
- package/dist/types-ts4.5/utils/use-on-escape/index.d.ts +1 -0
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atlaskit/spotlight
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`4f9f525caaa13`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4f9f525caaa13) -
|
|
8
|
+
Implement 'dismiss on escape key press' functionality. Escape will cause the current spotlight to
|
|
9
|
+
call the `dismiss` function passed to `PopoverContent`. Implementation of the `dismiss` function
|
|
10
|
+
is up to the consumer of the package.
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
|
|
13
|
+
## 0.5.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- [`72526321aecd0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/72526321aecd0) -
|
|
18
|
+
Create UNSAFE_UpdateOnChange component to test strategies for recalculating PopoverContent
|
|
19
|
+
position when the DOM changes.
|
|
20
|
+
|
|
3
21
|
## 0.4.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -10,21 +10,53 @@ exports.SpotlightContextProvider = exports.SpotlightContext = void 0;
|
|
|
10
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
11
|
var React = _react;
|
|
12
12
|
var _runtime = require("@compiled/react/runtime");
|
|
13
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
14
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
13
15
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
14
16
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
15
17
|
// eslint-disable-next-line @repo/internal/react/consistent-types-definitions
|
|
16
18
|
|
|
17
19
|
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
18
20
|
var SpotlightContext = exports.SpotlightContext = /*#__PURE__*/(0, _react.createContext)({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
card: {
|
|
22
|
+
placement: 'bottom-end',
|
|
23
|
+
setPlacement: function setPlacement() {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
22
26
|
},
|
|
23
27
|
heading: {
|
|
24
28
|
id: '',
|
|
25
29
|
setId: function setId() {
|
|
26
30
|
return undefined;
|
|
27
31
|
}
|
|
32
|
+
},
|
|
33
|
+
popoverContent: {
|
|
34
|
+
ref: undefined,
|
|
35
|
+
setRef: function setRef() {
|
|
36
|
+
return undefined;
|
|
37
|
+
},
|
|
38
|
+
update: function update() {
|
|
39
|
+
return function () {
|
|
40
|
+
return new Promise(function () {
|
|
41
|
+
return null;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
setUpdate: function setUpdate() {
|
|
46
|
+
return function () {
|
|
47
|
+
return new Promise(function () {
|
|
48
|
+
return null;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
dismiss: function dismiss() {
|
|
53
|
+
return undefined;
|
|
54
|
+
},
|
|
55
|
+
setDismiss: function setDismiss() {
|
|
56
|
+
return function () {
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
28
60
|
}
|
|
29
61
|
});
|
|
30
62
|
|
|
@@ -40,13 +72,49 @@ var SpotlightContextProvider = exports.SpotlightContextProvider = function Spotl
|
|
|
40
72
|
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
|
|
41
73
|
headingId = _useState4[0],
|
|
42
74
|
setHeadingId = _useState4[1];
|
|
75
|
+
var _useState5 = (0, _react.useState)(function () {
|
|
76
|
+
return /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
77
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
78
|
+
while (1) switch (_context.prev = _context.next) {
|
|
79
|
+
case 0:
|
|
80
|
+
return _context.abrupt("return", undefined);
|
|
81
|
+
case 1:
|
|
82
|
+
case "end":
|
|
83
|
+
return _context.stop();
|
|
84
|
+
}
|
|
85
|
+
}, _callee);
|
|
86
|
+
}));
|
|
87
|
+
}),
|
|
88
|
+
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
|
|
89
|
+
update = _useState6[0],
|
|
90
|
+
setUpdate = _useState6[1];
|
|
91
|
+
var _useState7 = (0, _react.useState)(),
|
|
92
|
+
_useState8 = (0, _slicedToArray2.default)(_useState7, 2),
|
|
93
|
+
ref = _useState8[0],
|
|
94
|
+
setRef = _useState8[1];
|
|
95
|
+
var _useState9 = (0, _react.useState)(function () {
|
|
96
|
+
return undefined;
|
|
97
|
+
}),
|
|
98
|
+
_useState0 = (0, _slicedToArray2.default)(_useState9, 2),
|
|
99
|
+
dismiss = _useState0[0],
|
|
100
|
+
setDismiss = _useState0[1];
|
|
43
101
|
return /*#__PURE__*/React.createElement(SpotlightContext.Provider, {
|
|
44
102
|
value: {
|
|
45
|
-
|
|
46
|
-
|
|
103
|
+
card: {
|
|
104
|
+
placement: placement,
|
|
105
|
+
setPlacement: setPlacement
|
|
106
|
+
},
|
|
47
107
|
heading: {
|
|
48
108
|
id: headingId,
|
|
49
109
|
setId: setHeadingId
|
|
110
|
+
},
|
|
111
|
+
popoverContent: {
|
|
112
|
+
ref: ref,
|
|
113
|
+
setRef: setRef,
|
|
114
|
+
update: update,
|
|
115
|
+
setUpdate: setUpdate,
|
|
116
|
+
dismiss: dismiss,
|
|
117
|
+
setDismiss: setDismiss
|
|
50
118
|
}
|
|
51
119
|
}
|
|
52
120
|
}, children);
|
package/dist/cjs/index.js
CHANGED
|
@@ -99,6 +99,12 @@ Object.defineProperty(exports, "SpotlightStepCount", {
|
|
|
99
99
|
return _stepCount.SpotlightStepCount;
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
|
+
Object.defineProperty(exports, "UNSAFE_UpdateOnChange", {
|
|
103
|
+
enumerable: true,
|
|
104
|
+
get: function get() {
|
|
105
|
+
return _UNSAFE_updateOnChange.UNSAFE_UpdateOnChange;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
102
108
|
var _card = require("./ui/card");
|
|
103
109
|
var _body = require("./ui/body");
|
|
104
110
|
var _header = require("./ui/header");
|
|
@@ -114,4 +120,5 @@ var _showMoreControl = require("./ui/show-more-control");
|
|
|
114
120
|
var _media = require("./ui/media");
|
|
115
121
|
var _popoverProvider = require("./ui/popover-provider");
|
|
116
122
|
var _popoverContent = require("./ui/popover-content");
|
|
117
|
-
var _popoverTarget = require("./ui/popover-target");
|
|
123
|
+
var _popoverTarget = require("./ui/popover-target");
|
|
124
|
+
var _UNSAFE_updateOnChange = require("./ui/UNSAFE_update-on-change");
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.UNSAFE_UpdateOnChange = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _browserApis = require("@atlaskit/browser-apis");
|
|
9
|
+
var _context = require("../../controllers/context");
|
|
10
|
+
var defaultOptions = {
|
|
11
|
+
childList: true,
|
|
12
|
+
subtree: true
|
|
13
|
+
};
|
|
14
|
+
var UNSAFE_UpdateOnChange = exports.UNSAFE_UpdateOnChange = function UNSAFE_UpdateOnChange(_ref) {
|
|
15
|
+
var _ref$selectors = _ref.selectors,
|
|
16
|
+
selectors = _ref$selectors === void 0 ? ['body'] : _ref$selectors,
|
|
17
|
+
_ref$options = _ref.options,
|
|
18
|
+
options = _ref$options === void 0 ? defaultOptions : _ref$options;
|
|
19
|
+
var _useContext = (0, _react.useContext)(_context.SpotlightContext),
|
|
20
|
+
popoverContent = _useContext.popoverContent;
|
|
21
|
+
var update = popoverContent.update;
|
|
22
|
+
(0, _react.useEffect)(function () {
|
|
23
|
+
if (!update || selectors.length === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
var doc = (0, _browserApis.getDocument)();
|
|
27
|
+
if (!doc) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
var elements = new Set();
|
|
31
|
+
selectors.forEach(function (selector) {
|
|
32
|
+
var element = doc.querySelector(selector);
|
|
33
|
+
if (!element) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
elements.add(element);
|
|
37
|
+
});
|
|
38
|
+
if (elements.size === 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
var observers = [];
|
|
42
|
+
elements.forEach(function (element) {
|
|
43
|
+
var observer = new MutationObserver(function (mutations) {
|
|
44
|
+
if (mutations.length === 0) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
update();
|
|
48
|
+
});
|
|
49
|
+
observer.observe(element, options);
|
|
50
|
+
observers.push(observer);
|
|
51
|
+
});
|
|
52
|
+
return function () {
|
|
53
|
+
observers.forEach(function (observer) {
|
|
54
|
+
return observer.disconnect();
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}, [selectors, options, update]);
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
@@ -34,10 +34,10 @@ var styles = {
|
|
|
34
34
|
var Caret = exports.Caret = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) {
|
|
35
35
|
var testId = _ref.testId;
|
|
36
36
|
var _useContext = (0, _react.useContext)(_context.SpotlightContext),
|
|
37
|
-
|
|
37
|
+
card = _useContext.card;
|
|
38
38
|
return /*#__PURE__*/React.createElement("div", {
|
|
39
39
|
"data-testid": testId,
|
|
40
40
|
ref: ref,
|
|
41
|
-
className: (0, _runtime.ax)([styles.root, styles[placement]])
|
|
41
|
+
className: (0, _runtime.ax)([styles.root, styles[card.placement]])
|
|
42
42
|
});
|
|
43
43
|
});
|
|
@@ -41,13 +41,13 @@ var SpotlightCard = exports.SpotlightCard = /*#__PURE__*/(0, _react.forwardRef)(
|
|
|
41
41
|
var children = _ref.children,
|
|
42
42
|
testId = _ref.testId;
|
|
43
43
|
var _useContext = (0, _react.useContext)(_context.SpotlightContext),
|
|
44
|
-
|
|
44
|
+
card = _useContext.card;
|
|
45
45
|
return /*#__PURE__*/React.createElement("div", {
|
|
46
46
|
"data-testid": testId,
|
|
47
47
|
ref: ref,
|
|
48
48
|
className: (0, _runtime.ax)([styles.root])
|
|
49
49
|
}, /*#__PURE__*/React.createElement(_caret.Caret, null), /*#__PURE__*/React.createElement(_compiled.Box, {
|
|
50
50
|
backgroundColor: "color.background.neutral.bold",
|
|
51
|
-
xcss: (0, _css.cx)(styles.card, placementStyles[placement])
|
|
51
|
+
xcss: (0, _css.cx)(styles.card, placementStyles[card.placement])
|
|
52
52
|
}, children));
|
|
53
53
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* index.tsx generated by @compiled/babel-plugin v0.38.1 */
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
5
|
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
5
6
|
Object.defineProperty(exports, "__esModule", {
|
|
6
7
|
value: true
|
|
@@ -10,8 +11,11 @@ require("./index.compiled.css");
|
|
|
10
11
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
12
|
var React = _react;
|
|
12
13
|
var _runtime = require("@compiled/react/runtime");
|
|
14
|
+
var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
|
|
13
15
|
var _popper = require("@atlaskit/popper");
|
|
14
16
|
var _context = require("../../controllers/context");
|
|
17
|
+
var _useFocusWithin = require("../../utils/use-focus-within");
|
|
18
|
+
var _useOnEscape = require("../../utils/use-on-escape");
|
|
15
19
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
16
20
|
var styles = {
|
|
17
21
|
root: "_1pby1fw0"
|
|
@@ -22,7 +26,7 @@ var styles = {
|
|
|
22
26
|
* the caret extending beyond the bounding box (by roughly 2px). So, apply an offset to ensure
|
|
23
27
|
* the caret points to the very edge of the target element.
|
|
24
28
|
*
|
|
25
|
-
* Note: `@atlaskit/popper` maps the offset to
|
|
29
|
+
* Note: `@atlaskit/popper` maps the offset to the placement, so we only need to define `[0, 2]` and
|
|
26
30
|
* the offset will get correctly rotated depending on the placement.
|
|
27
31
|
*/
|
|
28
32
|
var offset = [0, 2];
|
|
@@ -55,27 +59,57 @@ var PopoverContent = exports.PopoverContent = function PopoverContent(_ref) {
|
|
|
55
59
|
placement = _ref.placement,
|
|
56
60
|
_ref$isVisible = _ref.isVisible,
|
|
57
61
|
isVisible = _ref$isVisible === void 0 ? true : _ref$isVisible,
|
|
62
|
+
dismiss = _ref.dismiss,
|
|
58
63
|
testId = _ref.testId;
|
|
64
|
+
var updateRef = (0, _react.useRef)(function () {
|
|
65
|
+
return new Promise(function () {
|
|
66
|
+
return undefined;
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
var ref = (0, _react.useRef)();
|
|
59
70
|
var _useContext = (0, _react.useContext)(_context.SpotlightContext),
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
heading = _useContext.heading,
|
|
72
|
+
popoverContent = _useContext.popoverContent,
|
|
73
|
+
card = _useContext.card;
|
|
74
|
+
var focusWithin = (0, _useFocusWithin.useFocusWithin)(popoverContent.ref);
|
|
75
|
+
(0, _react.useEffect)(function () {
|
|
76
|
+
popoverContent.setRef(ref);
|
|
77
|
+
}, [ref, popoverContent]);
|
|
78
|
+
(0, _react.useEffect)(function () {
|
|
79
|
+
card.setPlacement(placement);
|
|
80
|
+
}, [placement, card]);
|
|
62
81
|
(0, _react.useEffect)(function () {
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
if (updateRef.current) {
|
|
83
|
+
popoverContent.setUpdate(function () {
|
|
84
|
+
return updateRef.current;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}, [popoverContent]);
|
|
88
|
+
(0, _useOnEscape.useOnEscape)(function (event) {
|
|
89
|
+
if (!focusWithin) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!dismiss) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
dismiss(event);
|
|
96
|
+
});
|
|
65
97
|
return /*#__PURE__*/React.createElement(_popper.Popper, {
|
|
66
98
|
offset: offset,
|
|
67
99
|
placement: popperPlacementMap[placement]
|
|
68
100
|
}, function (_ref2) {
|
|
69
|
-
var
|
|
70
|
-
style = _ref2.style
|
|
101
|
+
var localRef = _ref2.ref,
|
|
102
|
+
style = _ref2.style,
|
|
103
|
+
update = _ref2.update;
|
|
71
104
|
if (!isVisible) {
|
|
72
105
|
return;
|
|
73
106
|
}
|
|
107
|
+
updateRef.current = update;
|
|
74
108
|
return /*#__PURE__*/React.createElement("div", {
|
|
75
109
|
role: "dialog",
|
|
76
110
|
"data-testid": testId,
|
|
77
111
|
"aria-labelledby": heading.id,
|
|
78
|
-
ref: ref
|
|
112
|
+
ref: (0, _mergeRefs.default)([ref, localRef])
|
|
79
113
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
|
|
80
114
|
,
|
|
81
115
|
style: style,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.useFocusWithin = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
var _react = require("react");
|
|
10
|
+
var _bindEventListener = require("bind-event-listener");
|
|
11
|
+
var _browserApis = require("@atlaskit/browser-apis");
|
|
12
|
+
var useFocusWithin = exports.useFocusWithin = function useFocusWithin(ref) {
|
|
13
|
+
var _useState = (0, _react.useState)(null),
|
|
14
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
15
|
+
active = _useState2[0],
|
|
16
|
+
setActive = _useState2[1];
|
|
17
|
+
var updateFocusedElement = (0, _react.useCallback)(function () {
|
|
18
|
+
var doc = (0, _browserApis.getDocument)();
|
|
19
|
+
var activeElement = (doc === null || doc === void 0 ? void 0 : doc.activeElement) || null;
|
|
20
|
+
if (!ref || !ref.current) {
|
|
21
|
+
setActive(null);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!activeElement) {
|
|
25
|
+
setActive(null);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if the focused element is within the ref element
|
|
30
|
+
if (!ref.current.contains(activeElement)) {
|
|
31
|
+
setActive(null);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
setActive(activeElement);
|
|
35
|
+
}, [ref]);
|
|
36
|
+
(0, _react.useEffect)(function () {
|
|
37
|
+
var doc = (0, _browserApis.getDocument)();
|
|
38
|
+
if (!doc) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
updateFocusedElement();
|
|
42
|
+
var unbindFocusIn = (0, _bindEventListener.bind)(doc, {
|
|
43
|
+
type: 'focusin',
|
|
44
|
+
listener: updateFocusedElement
|
|
45
|
+
});
|
|
46
|
+
return function () {
|
|
47
|
+
unbindFocusIn();
|
|
48
|
+
};
|
|
49
|
+
}, [updateFocusedElement]);
|
|
50
|
+
return active;
|
|
51
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useOnEscape = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _bindEventListener = require("bind-event-listener");
|
|
9
|
+
var _browserApis = require("@atlaskit/browser-apis");
|
|
10
|
+
var onKeyDown = function onKeyDown(event, _ref) {
|
|
11
|
+
var onEscape = _ref.onEscape;
|
|
12
|
+
switch (event.key) {
|
|
13
|
+
case 'Escape':
|
|
14
|
+
onEscape();
|
|
15
|
+
break;
|
|
16
|
+
default:
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var useOnEscape = exports.useOnEscape = function useOnEscape(_onEscape) {
|
|
21
|
+
(0, _react.useEffect)(function () {
|
|
22
|
+
var doc = (0, _browserApis.getDocument)();
|
|
23
|
+
if (!doc) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
var unbindFocusIn = (0, _bindEventListener.bind)(doc, {
|
|
27
|
+
type: 'keydown',
|
|
28
|
+
listener: function listener(event) {
|
|
29
|
+
return onKeyDown(event, {
|
|
30
|
+
onEscape: function onEscape() {
|
|
31
|
+
return _onEscape(event);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return function () {
|
|
37
|
+
unbindFocusIn();
|
|
38
|
+
};
|
|
39
|
+
}, [_onEscape]);
|
|
40
|
+
};
|
|
@@ -7,11 +7,21 @@ import { createContext, useId, useState } from 'react';
|
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
9
9
|
export const SpotlightContext = /*#__PURE__*/createContext({
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
card: {
|
|
11
|
+
placement: 'bottom-end',
|
|
12
|
+
setPlacement: () => undefined
|
|
13
|
+
},
|
|
12
14
|
heading: {
|
|
13
15
|
id: '',
|
|
14
16
|
setId: () => undefined
|
|
17
|
+
},
|
|
18
|
+
popoverContent: {
|
|
19
|
+
ref: undefined,
|
|
20
|
+
setRef: () => undefined,
|
|
21
|
+
update: () => () => new Promise(() => null),
|
|
22
|
+
setUpdate: () => () => new Promise(() => null),
|
|
23
|
+
dismiss: () => undefined,
|
|
24
|
+
setDismiss: () => () => undefined
|
|
15
25
|
}
|
|
16
26
|
});
|
|
17
27
|
|
|
@@ -22,13 +32,26 @@ export const SpotlightContextProvider = ({
|
|
|
22
32
|
const id = useId();
|
|
23
33
|
const [placement, setPlacement] = useState('bottom-end');
|
|
24
34
|
const [headingId, setHeadingId] = useState(`${id}-heading`);
|
|
35
|
+
const [update, setUpdate] = useState(() => async () => undefined);
|
|
36
|
+
const [ref, setRef] = useState();
|
|
37
|
+
const [dismiss, setDismiss] = useState(() => undefined);
|
|
25
38
|
return /*#__PURE__*/React.createElement(SpotlightContext.Provider, {
|
|
26
39
|
value: {
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
card: {
|
|
41
|
+
placement,
|
|
42
|
+
setPlacement
|
|
43
|
+
},
|
|
29
44
|
heading: {
|
|
30
45
|
id: headingId,
|
|
31
46
|
setId: setHeadingId
|
|
47
|
+
},
|
|
48
|
+
popoverContent: {
|
|
49
|
+
ref,
|
|
50
|
+
setRef,
|
|
51
|
+
update,
|
|
52
|
+
setUpdate,
|
|
53
|
+
dismiss,
|
|
54
|
+
setDismiss
|
|
32
55
|
}
|
|
33
56
|
}
|
|
34
57
|
}, children);
|
package/dist/es2019/index.js
CHANGED
|
@@ -13,4 +13,5 @@ export { SpotlightShowMoreControl } from './ui/show-more-control';
|
|
|
13
13
|
export { SpotlightMedia } from './ui/media';
|
|
14
14
|
export { PopoverProvider } from './ui/popover-provider';
|
|
15
15
|
export { PopoverContent } from './ui/popover-content';
|
|
16
|
-
export { PopoverTarget } from './ui/popover-target';
|
|
16
|
+
export { PopoverTarget } from './ui/popover-target';
|
|
17
|
+
export { UNSAFE_UpdateOnChange } from './ui/UNSAFE_update-on-change';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useContext, useEffect } from 'react';
|
|
2
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
3
|
+
import { SpotlightContext } from '../../controllers/context';
|
|
4
|
+
const defaultOptions = {
|
|
5
|
+
childList: true,
|
|
6
|
+
subtree: true
|
|
7
|
+
};
|
|
8
|
+
export const UNSAFE_UpdateOnChange = ({
|
|
9
|
+
selectors = ['body'],
|
|
10
|
+
options = defaultOptions
|
|
11
|
+
}) => {
|
|
12
|
+
const {
|
|
13
|
+
popoverContent
|
|
14
|
+
} = useContext(SpotlightContext);
|
|
15
|
+
const {
|
|
16
|
+
update
|
|
17
|
+
} = popoverContent;
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!update || selectors.length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const doc = getDocument();
|
|
23
|
+
if (!doc) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const elements = new Set();
|
|
27
|
+
selectors.forEach(selector => {
|
|
28
|
+
const element = doc.querySelector(selector);
|
|
29
|
+
if (!element) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
elements.add(element);
|
|
33
|
+
});
|
|
34
|
+
if (elements.size === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const observers = [];
|
|
38
|
+
elements.forEach(element => {
|
|
39
|
+
const observer = new MutationObserver(mutations => {
|
|
40
|
+
if (mutations.length === 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
update();
|
|
44
|
+
});
|
|
45
|
+
observer.observe(element, options);
|
|
46
|
+
observers.push(observer);
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
observers.forEach(observer => observer.disconnect());
|
|
50
|
+
};
|
|
51
|
+
}, [selectors, options, update]);
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
@@ -27,11 +27,11 @@ export const Caret = /*#__PURE__*/forwardRef(({
|
|
|
27
27
|
testId
|
|
28
28
|
}, ref) => {
|
|
29
29
|
const {
|
|
30
|
-
|
|
30
|
+
card
|
|
31
31
|
} = useContext(SpotlightContext);
|
|
32
32
|
return /*#__PURE__*/React.createElement("div", {
|
|
33
33
|
"data-testid": testId,
|
|
34
34
|
ref: ref,
|
|
35
|
-
className: ax([styles.root, styles[placement]])
|
|
35
|
+
className: ax([styles.root, styles[card.placement]])
|
|
36
36
|
});
|
|
37
37
|
});
|
|
@@ -34,7 +34,7 @@ export const SpotlightCard = /*#__PURE__*/forwardRef(({
|
|
|
34
34
|
testId
|
|
35
35
|
}, ref) => {
|
|
36
36
|
const {
|
|
37
|
-
|
|
37
|
+
card
|
|
38
38
|
} = useContext(SpotlightContext);
|
|
39
39
|
return /*#__PURE__*/React.createElement("div", {
|
|
40
40
|
"data-testid": testId,
|
|
@@ -42,6 +42,6 @@ export const SpotlightCard = /*#__PURE__*/forwardRef(({
|
|
|
42
42
|
className: ax([styles.root])
|
|
43
43
|
}, /*#__PURE__*/React.createElement(Caret, null), /*#__PURE__*/React.createElement(Box, {
|
|
44
44
|
backgroundColor: "color.background.neutral.bold",
|
|
45
|
-
xcss: cx(styles.card, placementStyles[placement])
|
|
45
|
+
xcss: cx(styles.card, placementStyles[card.placement])
|
|
46
46
|
}, children));
|
|
47
47
|
});
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import "./index.compiled.css";
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { ax, ix } from "@compiled/react/runtime";
|
|
5
|
-
import { useContext, useEffect } from 'react';
|
|
5
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
6
|
+
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
6
7
|
import { Popper } from '@atlaskit/popper';
|
|
7
8
|
import { SpotlightContext } from '../../controllers/context';
|
|
9
|
+
import { useFocusWithin } from '../../utils/use-focus-within';
|
|
10
|
+
import { useOnEscape } from '../../utils/use-on-escape';
|
|
8
11
|
const styles = {
|
|
9
12
|
root: "_1pby1fw0"
|
|
10
13
|
};
|
|
@@ -14,7 +17,7 @@ const styles = {
|
|
|
14
17
|
* the caret extending beyond the bounding box (by roughly 2px). So, apply an offset to ensure
|
|
15
18
|
* the caret points to the very edge of the target element.
|
|
16
19
|
*
|
|
17
|
-
* Note: `@atlaskit/popper` maps the offset to
|
|
20
|
+
* Note: `@atlaskit/popper` maps the offset to the placement, so we only need to define `[0, 2]` and
|
|
18
21
|
* the offset will get correctly rotated depending on the placement.
|
|
19
22
|
*/
|
|
20
23
|
const offset = [0, 2];
|
|
@@ -46,30 +49,54 @@ export const PopoverContent = ({
|
|
|
46
49
|
children,
|
|
47
50
|
placement,
|
|
48
51
|
isVisible = true,
|
|
52
|
+
dismiss,
|
|
49
53
|
testId
|
|
50
54
|
}) => {
|
|
55
|
+
const updateRef = useRef(() => new Promise(() => undefined));
|
|
56
|
+
const ref = useRef();
|
|
51
57
|
const {
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
heading,
|
|
59
|
+
popoverContent,
|
|
60
|
+
card
|
|
54
61
|
} = useContext(SpotlightContext);
|
|
62
|
+
const focusWithin = useFocusWithin(popoverContent.ref);
|
|
55
63
|
useEffect(() => {
|
|
56
|
-
|
|
57
|
-
}, [
|
|
64
|
+
popoverContent.setRef(ref);
|
|
65
|
+
}, [ref, popoverContent]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
card.setPlacement(placement);
|
|
68
|
+
}, [placement, card]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (updateRef.current) {
|
|
71
|
+
popoverContent.setUpdate(() => updateRef.current);
|
|
72
|
+
}
|
|
73
|
+
}, [popoverContent]);
|
|
74
|
+
useOnEscape(event => {
|
|
75
|
+
if (!focusWithin) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!dismiss) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
dismiss(event);
|
|
82
|
+
});
|
|
58
83
|
return /*#__PURE__*/React.createElement(Popper, {
|
|
59
84
|
offset: offset,
|
|
60
85
|
placement: popperPlacementMap[placement]
|
|
61
86
|
}, ({
|
|
62
|
-
ref,
|
|
63
|
-
style
|
|
87
|
+
ref: localRef,
|
|
88
|
+
style,
|
|
89
|
+
update
|
|
64
90
|
}) => {
|
|
65
91
|
if (!isVisible) {
|
|
66
92
|
return;
|
|
67
93
|
}
|
|
94
|
+
updateRef.current = update;
|
|
68
95
|
return /*#__PURE__*/React.createElement("div", {
|
|
69
96
|
role: "dialog",
|
|
70
97
|
"data-testid": testId,
|
|
71
98
|
"aria-labelledby": heading.id,
|
|
72
|
-
ref: ref
|
|
99
|
+
ref: mergeRefs([ref, localRef])
|
|
73
100
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
|
|
74
101
|
,
|
|
75
102
|
style: style,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { bind } from 'bind-event-listener';
|
|
3
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
4
|
+
export const useFocusWithin = ref => {
|
|
5
|
+
const [active, setActive] = useState(null);
|
|
6
|
+
const updateFocusedElement = useCallback(() => {
|
|
7
|
+
const doc = getDocument();
|
|
8
|
+
const activeElement = (doc === null || doc === void 0 ? void 0 : doc.activeElement) || null;
|
|
9
|
+
if (!ref || !ref.current) {
|
|
10
|
+
setActive(null);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (!activeElement) {
|
|
14
|
+
setActive(null);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check if the focused element is within the ref element
|
|
19
|
+
if (!ref.current.contains(activeElement)) {
|
|
20
|
+
setActive(null);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
setActive(activeElement);
|
|
24
|
+
}, [ref]);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const doc = getDocument();
|
|
27
|
+
if (!doc) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
updateFocusedElement();
|
|
31
|
+
const unbindFocusIn = bind(doc, {
|
|
32
|
+
type: 'focusin',
|
|
33
|
+
listener: updateFocusedElement
|
|
34
|
+
});
|
|
35
|
+
return () => {
|
|
36
|
+
unbindFocusIn();
|
|
37
|
+
};
|
|
38
|
+
}, [updateFocusedElement]);
|
|
39
|
+
return active;
|
|
40
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { bind } from 'bind-event-listener';
|
|
3
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
4
|
+
const onKeyDown = (event, {
|
|
5
|
+
onEscape
|
|
6
|
+
}) => {
|
|
7
|
+
switch (event.key) {
|
|
8
|
+
case 'Escape':
|
|
9
|
+
onEscape();
|
|
10
|
+
break;
|
|
11
|
+
default:
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const useOnEscape = onEscape => {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const doc = getDocument();
|
|
18
|
+
if (!doc) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const unbindFocusIn = bind(doc, {
|
|
22
|
+
type: 'keydown',
|
|
23
|
+
listener: event => onKeyDown(event, {
|
|
24
|
+
onEscape: () => onEscape(event)
|
|
25
|
+
})
|
|
26
|
+
});
|
|
27
|
+
return () => {
|
|
28
|
+
unbindFocusIn();
|
|
29
|
+
};
|
|
30
|
+
}, [onEscape]);
|
|
31
|
+
};
|
|
@@ -1,22 +1,54 @@
|
|
|
1
1
|
/* context.tsx generated by @compiled/babel-plugin v0.38.1 */
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
3
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
4
|
import * as React from 'react';
|
|
4
5
|
import { ax, ix } from "@compiled/react/runtime";
|
|
6
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
5
7
|
import { createContext, useId, useState } from 'react';
|
|
6
8
|
|
|
7
9
|
// eslint-disable-next-line @repo/internal/react/consistent-types-definitions
|
|
8
10
|
|
|
9
11
|
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
10
12
|
export var SpotlightContext = /*#__PURE__*/createContext({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
card: {
|
|
14
|
+
placement: 'bottom-end',
|
|
15
|
+
setPlacement: function setPlacement() {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
14
18
|
},
|
|
15
19
|
heading: {
|
|
16
20
|
id: '',
|
|
17
21
|
setId: function setId() {
|
|
18
22
|
return undefined;
|
|
19
23
|
}
|
|
24
|
+
},
|
|
25
|
+
popoverContent: {
|
|
26
|
+
ref: undefined,
|
|
27
|
+
setRef: function setRef() {
|
|
28
|
+
return undefined;
|
|
29
|
+
},
|
|
30
|
+
update: function update() {
|
|
31
|
+
return function () {
|
|
32
|
+
return new Promise(function () {
|
|
33
|
+
return null;
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
setUpdate: function setUpdate() {
|
|
38
|
+
return function () {
|
|
39
|
+
return new Promise(function () {
|
|
40
|
+
return null;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
dismiss: function dismiss() {
|
|
45
|
+
return undefined;
|
|
46
|
+
},
|
|
47
|
+
setDismiss: function setDismiss() {
|
|
48
|
+
return function () {
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
20
52
|
}
|
|
21
53
|
});
|
|
22
54
|
|
|
@@ -32,13 +64,49 @@ export var SpotlightContextProvider = function SpotlightContextProvider(_ref) {
|
|
|
32
64
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
33
65
|
headingId = _useState4[0],
|
|
34
66
|
setHeadingId = _useState4[1];
|
|
67
|
+
var _useState5 = useState(function () {
|
|
68
|
+
return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
69
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
70
|
+
while (1) switch (_context.prev = _context.next) {
|
|
71
|
+
case 0:
|
|
72
|
+
return _context.abrupt("return", undefined);
|
|
73
|
+
case 1:
|
|
74
|
+
case "end":
|
|
75
|
+
return _context.stop();
|
|
76
|
+
}
|
|
77
|
+
}, _callee);
|
|
78
|
+
}));
|
|
79
|
+
}),
|
|
80
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
81
|
+
update = _useState6[0],
|
|
82
|
+
setUpdate = _useState6[1];
|
|
83
|
+
var _useState7 = useState(),
|
|
84
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
85
|
+
ref = _useState8[0],
|
|
86
|
+
setRef = _useState8[1];
|
|
87
|
+
var _useState9 = useState(function () {
|
|
88
|
+
return undefined;
|
|
89
|
+
}),
|
|
90
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
91
|
+
dismiss = _useState0[0],
|
|
92
|
+
setDismiss = _useState0[1];
|
|
35
93
|
return /*#__PURE__*/React.createElement(SpotlightContext.Provider, {
|
|
36
94
|
value: {
|
|
37
|
-
|
|
38
|
-
|
|
95
|
+
card: {
|
|
96
|
+
placement: placement,
|
|
97
|
+
setPlacement: setPlacement
|
|
98
|
+
},
|
|
39
99
|
heading: {
|
|
40
100
|
id: headingId,
|
|
41
101
|
setId: setHeadingId
|
|
102
|
+
},
|
|
103
|
+
popoverContent: {
|
|
104
|
+
ref: ref,
|
|
105
|
+
setRef: setRef,
|
|
106
|
+
update: update,
|
|
107
|
+
setUpdate: setUpdate,
|
|
108
|
+
dismiss: dismiss,
|
|
109
|
+
setDismiss: setDismiss
|
|
42
110
|
}
|
|
43
111
|
}
|
|
44
112
|
}, children);
|
package/dist/esm/index.js
CHANGED
|
@@ -13,4 +13,5 @@ export { SpotlightShowMoreControl } from './ui/show-more-control';
|
|
|
13
13
|
export { SpotlightMedia } from './ui/media';
|
|
14
14
|
export { PopoverProvider } from './ui/popover-provider';
|
|
15
15
|
export { PopoverContent } from './ui/popover-content';
|
|
16
|
-
export { PopoverTarget } from './ui/popover-target';
|
|
16
|
+
export { PopoverTarget } from './ui/popover-target';
|
|
17
|
+
export { UNSAFE_UpdateOnChange } from './ui/UNSAFE_update-on-change';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useContext, useEffect } from 'react';
|
|
2
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
3
|
+
import { SpotlightContext } from '../../controllers/context';
|
|
4
|
+
var defaultOptions = {
|
|
5
|
+
childList: true,
|
|
6
|
+
subtree: true
|
|
7
|
+
};
|
|
8
|
+
export var UNSAFE_UpdateOnChange = function UNSAFE_UpdateOnChange(_ref) {
|
|
9
|
+
var _ref$selectors = _ref.selectors,
|
|
10
|
+
selectors = _ref$selectors === void 0 ? ['body'] : _ref$selectors,
|
|
11
|
+
_ref$options = _ref.options,
|
|
12
|
+
options = _ref$options === void 0 ? defaultOptions : _ref$options;
|
|
13
|
+
var _useContext = useContext(SpotlightContext),
|
|
14
|
+
popoverContent = _useContext.popoverContent;
|
|
15
|
+
var update = popoverContent.update;
|
|
16
|
+
useEffect(function () {
|
|
17
|
+
if (!update || selectors.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
var doc = getDocument();
|
|
21
|
+
if (!doc) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
var elements = new Set();
|
|
25
|
+
selectors.forEach(function (selector) {
|
|
26
|
+
var element = doc.querySelector(selector);
|
|
27
|
+
if (!element) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
elements.add(element);
|
|
31
|
+
});
|
|
32
|
+
if (elements.size === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
var observers = [];
|
|
36
|
+
elements.forEach(function (element) {
|
|
37
|
+
var observer = new MutationObserver(function (mutations) {
|
|
38
|
+
if (mutations.length === 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
update();
|
|
42
|
+
});
|
|
43
|
+
observer.observe(element, options);
|
|
44
|
+
observers.push(observer);
|
|
45
|
+
});
|
|
46
|
+
return function () {
|
|
47
|
+
observers.forEach(function (observer) {
|
|
48
|
+
return observer.disconnect();
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}, [selectors, options, update]);
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
@@ -26,10 +26,10 @@ var styles = {
|
|
|
26
26
|
export var Caret = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
|
27
27
|
var testId = _ref.testId;
|
|
28
28
|
var _useContext = useContext(SpotlightContext),
|
|
29
|
-
|
|
29
|
+
card = _useContext.card;
|
|
30
30
|
return /*#__PURE__*/React.createElement("div", {
|
|
31
31
|
"data-testid": testId,
|
|
32
32
|
ref: ref,
|
|
33
|
-
className: ax([styles.root, styles[placement]])
|
|
33
|
+
className: ax([styles.root, styles[card.placement]])
|
|
34
34
|
});
|
|
35
35
|
});
|
|
@@ -33,13 +33,13 @@ export var SpotlightCard = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
|
|
33
33
|
var children = _ref.children,
|
|
34
34
|
testId = _ref.testId;
|
|
35
35
|
var _useContext = useContext(SpotlightContext),
|
|
36
|
-
|
|
36
|
+
card = _useContext.card;
|
|
37
37
|
return /*#__PURE__*/React.createElement("div", {
|
|
38
38
|
"data-testid": testId,
|
|
39
39
|
ref: ref,
|
|
40
40
|
className: ax([styles.root])
|
|
41
41
|
}, /*#__PURE__*/React.createElement(Caret, null), /*#__PURE__*/React.createElement(Box, {
|
|
42
42
|
backgroundColor: "color.background.neutral.bold",
|
|
43
|
-
xcss: cx(styles.card, placementStyles[placement])
|
|
43
|
+
xcss: cx(styles.card, placementStyles[card.placement])
|
|
44
44
|
}, children));
|
|
45
45
|
});
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import "./index.compiled.css";
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { ax, ix } from "@compiled/react/runtime";
|
|
5
|
-
import { useContext, useEffect } from 'react';
|
|
5
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
6
|
+
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
6
7
|
import { Popper } from '@atlaskit/popper';
|
|
7
8
|
import { SpotlightContext } from '../../controllers/context';
|
|
9
|
+
import { useFocusWithin } from '../../utils/use-focus-within';
|
|
10
|
+
import { useOnEscape } from '../../utils/use-on-escape';
|
|
8
11
|
var styles = {
|
|
9
12
|
root: "_1pby1fw0"
|
|
10
13
|
};
|
|
@@ -14,7 +17,7 @@ var styles = {
|
|
|
14
17
|
* the caret extending beyond the bounding box (by roughly 2px). So, apply an offset to ensure
|
|
15
18
|
* the caret points to the very edge of the target element.
|
|
16
19
|
*
|
|
17
|
-
* Note: `@atlaskit/popper` maps the offset to
|
|
20
|
+
* Note: `@atlaskit/popper` maps the offset to the placement, so we only need to define `[0, 2]` and
|
|
18
21
|
* the offset will get correctly rotated depending on the placement.
|
|
19
22
|
*/
|
|
20
23
|
var offset = [0, 2];
|
|
@@ -47,27 +50,57 @@ export var PopoverContent = function PopoverContent(_ref) {
|
|
|
47
50
|
placement = _ref.placement,
|
|
48
51
|
_ref$isVisible = _ref.isVisible,
|
|
49
52
|
isVisible = _ref$isVisible === void 0 ? true : _ref$isVisible,
|
|
53
|
+
dismiss = _ref.dismiss,
|
|
50
54
|
testId = _ref.testId;
|
|
55
|
+
var updateRef = useRef(function () {
|
|
56
|
+
return new Promise(function () {
|
|
57
|
+
return undefined;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
var ref = useRef();
|
|
51
61
|
var _useContext = useContext(SpotlightContext),
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
heading = _useContext.heading,
|
|
63
|
+
popoverContent = _useContext.popoverContent,
|
|
64
|
+
card = _useContext.card;
|
|
65
|
+
var focusWithin = useFocusWithin(popoverContent.ref);
|
|
66
|
+
useEffect(function () {
|
|
67
|
+
popoverContent.setRef(ref);
|
|
68
|
+
}, [ref, popoverContent]);
|
|
69
|
+
useEffect(function () {
|
|
70
|
+
card.setPlacement(placement);
|
|
71
|
+
}, [placement, card]);
|
|
54
72
|
useEffect(function () {
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
if (updateRef.current) {
|
|
74
|
+
popoverContent.setUpdate(function () {
|
|
75
|
+
return updateRef.current;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}, [popoverContent]);
|
|
79
|
+
useOnEscape(function (event) {
|
|
80
|
+
if (!focusWithin) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!dismiss) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
dismiss(event);
|
|
87
|
+
});
|
|
57
88
|
return /*#__PURE__*/React.createElement(Popper, {
|
|
58
89
|
offset: offset,
|
|
59
90
|
placement: popperPlacementMap[placement]
|
|
60
91
|
}, function (_ref2) {
|
|
61
|
-
var
|
|
62
|
-
style = _ref2.style
|
|
92
|
+
var localRef = _ref2.ref,
|
|
93
|
+
style = _ref2.style,
|
|
94
|
+
update = _ref2.update;
|
|
63
95
|
if (!isVisible) {
|
|
64
96
|
return;
|
|
65
97
|
}
|
|
98
|
+
updateRef.current = update;
|
|
66
99
|
return /*#__PURE__*/React.createElement("div", {
|
|
67
100
|
role: "dialog",
|
|
68
101
|
"data-testid": testId,
|
|
69
102
|
"aria-labelledby": heading.id,
|
|
70
|
-
ref: ref
|
|
103
|
+
ref: mergeRefs([ref, localRef])
|
|
71
104
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
|
|
72
105
|
,
|
|
73
106
|
style: style,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { bind } from 'bind-event-listener';
|
|
4
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
5
|
+
export var useFocusWithin = function useFocusWithin(ref) {
|
|
6
|
+
var _useState = useState(null),
|
|
7
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
8
|
+
active = _useState2[0],
|
|
9
|
+
setActive = _useState2[1];
|
|
10
|
+
var updateFocusedElement = useCallback(function () {
|
|
11
|
+
var doc = getDocument();
|
|
12
|
+
var activeElement = (doc === null || doc === void 0 ? void 0 : doc.activeElement) || null;
|
|
13
|
+
if (!ref || !ref.current) {
|
|
14
|
+
setActive(null);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!activeElement) {
|
|
18
|
+
setActive(null);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if the focused element is within the ref element
|
|
23
|
+
if (!ref.current.contains(activeElement)) {
|
|
24
|
+
setActive(null);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
setActive(activeElement);
|
|
28
|
+
}, [ref]);
|
|
29
|
+
useEffect(function () {
|
|
30
|
+
var doc = getDocument();
|
|
31
|
+
if (!doc) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
updateFocusedElement();
|
|
35
|
+
var unbindFocusIn = bind(doc, {
|
|
36
|
+
type: 'focusin',
|
|
37
|
+
listener: updateFocusedElement
|
|
38
|
+
});
|
|
39
|
+
return function () {
|
|
40
|
+
unbindFocusIn();
|
|
41
|
+
};
|
|
42
|
+
}, [updateFocusedElement]);
|
|
43
|
+
return active;
|
|
44
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { bind } from 'bind-event-listener';
|
|
3
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
4
|
+
var onKeyDown = function onKeyDown(event, _ref) {
|
|
5
|
+
var onEscape = _ref.onEscape;
|
|
6
|
+
switch (event.key) {
|
|
7
|
+
case 'Escape':
|
|
8
|
+
onEscape();
|
|
9
|
+
break;
|
|
10
|
+
default:
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
export var useOnEscape = function useOnEscape(_onEscape) {
|
|
15
|
+
useEffect(function () {
|
|
16
|
+
var doc = getDocument();
|
|
17
|
+
if (!doc) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
var unbindFocusIn = bind(doc, {
|
|
21
|
+
type: 'keydown',
|
|
22
|
+
listener: function listener(event) {
|
|
23
|
+
return onKeyDown(event, {
|
|
24
|
+
onEscape: function onEscape() {
|
|
25
|
+
return _onEscape(event);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return function () {
|
|
31
|
+
unbindFocusIn();
|
|
32
|
+
};
|
|
33
|
+
}, [_onEscape]);
|
|
34
|
+
};
|
|
@@ -2,18 +2,27 @@
|
|
|
2
2
|
* @jsxRuntime classic
|
|
3
3
|
* @jsx jsx
|
|
4
4
|
*/
|
|
5
|
-
import { type Dispatch, type ReactNode, type SetStateAction } from 'react';
|
|
5
|
+
import { type Dispatch, type MutableRefObject, type ReactNode, type SetStateAction } from 'react';
|
|
6
6
|
import type { Placement } from '../types';
|
|
7
|
-
interface SpotlightContextType {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export interface SpotlightContextType {
|
|
8
|
+
card: {
|
|
9
|
+
placement: Placement;
|
|
10
|
+
setPlacement: Dispatch<SetStateAction<Placement>>;
|
|
11
|
+
};
|
|
10
12
|
heading: {
|
|
11
13
|
id: string;
|
|
12
14
|
setId: Dispatch<SetStateAction<string>>;
|
|
13
15
|
};
|
|
16
|
+
popoverContent: {
|
|
17
|
+
ref: MutableRefObject<HTMLDivElement | undefined> | undefined;
|
|
18
|
+
setRef: Dispatch<SetStateAction<MutableRefObject<HTMLDivElement | undefined> | undefined>>;
|
|
19
|
+
update: () => () => Promise<any>;
|
|
20
|
+
setUpdate: Dispatch<SetStateAction<() => () => Promise<any>>>;
|
|
21
|
+
dismiss: () => void;
|
|
22
|
+
setDismiss: Dispatch<SetStateAction<() => void>>;
|
|
23
|
+
};
|
|
14
24
|
}
|
|
15
25
|
export declare const SpotlightContext: import("react").Context<SpotlightContextType>;
|
|
16
26
|
export declare const SpotlightContextProvider: ({ children }: {
|
|
17
27
|
children: ReactNode;
|
|
18
28
|
}) => JSX.Element;
|
|
19
|
-
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -15,3 +15,4 @@ export { PopoverProvider } from './ui/popover-provider';
|
|
|
15
15
|
export { PopoverContent, type PopoverContentProps } from './ui/popover-content';
|
|
16
16
|
export { PopoverTarget } from './ui/popover-target';
|
|
17
17
|
export { type Placement } from './types';
|
|
18
|
+
export { UNSAFE_UpdateOnChange } from './ui/UNSAFE_update-on-change';
|
|
@@ -13,6 +13,7 @@ export interface PopoverContentProps {
|
|
|
13
13
|
testId?: string;
|
|
14
14
|
placement: Placement;
|
|
15
15
|
isVisible?: boolean;
|
|
16
|
+
dismiss?: (event: KeyboardEvent) => void;
|
|
16
17
|
children: ReactNode;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
@@ -20,4 +21,4 @@ export interface PopoverContentProps {
|
|
|
20
21
|
*
|
|
21
22
|
* A `PopoverContent` is the element that is shown as a popover.
|
|
22
23
|
*/
|
|
23
|
-
export declare const PopoverContent: ({ children, placement, isVisible, testId, }: PopoverContentProps) => JSX.Element;
|
|
24
|
+
export declare const PopoverContent: ({ children, placement, isVisible, dismiss, testId, }: PopoverContentProps) => JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useOnEscape: (onEscape: (event: KeyboardEvent) => void) => void;
|
|
@@ -2,18 +2,27 @@
|
|
|
2
2
|
* @jsxRuntime classic
|
|
3
3
|
* @jsx jsx
|
|
4
4
|
*/
|
|
5
|
-
import { type Dispatch, type ReactNode, type SetStateAction } from 'react';
|
|
5
|
+
import { type Dispatch, type MutableRefObject, type ReactNode, type SetStateAction } from 'react';
|
|
6
6
|
import type { Placement } from '../types';
|
|
7
|
-
interface SpotlightContextType {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export interface SpotlightContextType {
|
|
8
|
+
card: {
|
|
9
|
+
placement: Placement;
|
|
10
|
+
setPlacement: Dispatch<SetStateAction<Placement>>;
|
|
11
|
+
};
|
|
10
12
|
heading: {
|
|
11
13
|
id: string;
|
|
12
14
|
setId: Dispatch<SetStateAction<string>>;
|
|
13
15
|
};
|
|
16
|
+
popoverContent: {
|
|
17
|
+
ref: MutableRefObject<HTMLDivElement | undefined> | undefined;
|
|
18
|
+
setRef: Dispatch<SetStateAction<MutableRefObject<HTMLDivElement | undefined> | undefined>>;
|
|
19
|
+
update: () => () => Promise<any>;
|
|
20
|
+
setUpdate: Dispatch<SetStateAction<() => () => Promise<any>>>;
|
|
21
|
+
dismiss: () => void;
|
|
22
|
+
setDismiss: Dispatch<SetStateAction<() => void>>;
|
|
23
|
+
};
|
|
14
24
|
}
|
|
15
25
|
export declare const SpotlightContext: import("react").Context<SpotlightContextType>;
|
|
16
26
|
export declare const SpotlightContextProvider: ({ children }: {
|
|
17
27
|
children: ReactNode;
|
|
18
28
|
}) => JSX.Element;
|
|
19
|
-
export {};
|
|
@@ -15,3 +15,4 @@ export { PopoverProvider } from './ui/popover-provider';
|
|
|
15
15
|
export { PopoverContent, type PopoverContentProps } from './ui/popover-content';
|
|
16
16
|
export { PopoverTarget } from './ui/popover-target';
|
|
17
17
|
export { type Placement } from './types';
|
|
18
|
+
export { UNSAFE_UpdateOnChange } from './ui/UNSAFE_update-on-change';
|
|
@@ -13,6 +13,7 @@ export interface PopoverContentProps {
|
|
|
13
13
|
testId?: string;
|
|
14
14
|
placement: Placement;
|
|
15
15
|
isVisible?: boolean;
|
|
16
|
+
dismiss?: (event: KeyboardEvent) => void;
|
|
16
17
|
children: ReactNode;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
@@ -20,4 +21,4 @@ export interface PopoverContentProps {
|
|
|
20
21
|
*
|
|
21
22
|
* A `PopoverContent` is the element that is shown as a popover.
|
|
22
23
|
*/
|
|
23
|
-
export declare const PopoverContent: ({ children, placement, isVisible, testId, }: PopoverContentProps) => JSX.Element;
|
|
24
|
+
export declare const PopoverContent: ({ children, placement, isVisible, dismiss, testId, }: PopoverContentProps) => JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useOnEscape: (onEscape: (event: KeyboardEvent) => void) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/spotlight",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "A spotlight introduces users to various points of interest across Atlassian through focused messages or multi-step tours.",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -26,17 +26,20 @@
|
|
|
26
26
|
],
|
|
27
27
|
"atlaskit:src": "src/index.tsx",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@atlaskit/
|
|
29
|
+
"@atlaskit/browser-apis": "^0.0.1",
|
|
30
|
+
"@atlaskit/button": "^23.5.0",
|
|
30
31
|
"@atlaskit/css": "^0.14.0",
|
|
32
|
+
"@atlaskit/ds-lib": "^5.1.0",
|
|
31
33
|
"@atlaskit/heading": "^5.2.0",
|
|
32
|
-
"@atlaskit/icon": "^28.
|
|
34
|
+
"@atlaskit/icon": "^28.5.0",
|
|
33
35
|
"@atlaskit/image": "^3.0.0",
|
|
34
36
|
"@atlaskit/popper": "^7.1.0",
|
|
35
37
|
"@atlaskit/primitives": "^14.15.0",
|
|
36
38
|
"@atlaskit/tokens": "^6.4.0",
|
|
37
39
|
"@atlaskit/visually-hidden": "^3.0.0",
|
|
38
40
|
"@babel/runtime": "^7.0.0",
|
|
39
|
-
"@compiled/react": "^0.18.3"
|
|
41
|
+
"@compiled/react": "^0.18.3",
|
|
42
|
+
"bind-event-listener": "^3.0.0"
|
|
40
43
|
},
|
|
41
44
|
"peerDependencies": {
|
|
42
45
|
"react": "^18.2.0"
|