UNPKG

13.6 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7exports.__esModule = true;
8exports.PopoverCloseButton = exports.PopoverArrow = exports.PopoverContent = exports.PopoverTrigger = exports.Popover = exports.PopoverBody = exports.PopoverFooter = exports.PopoverHeader = void 0;
9
10var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
12var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
13
14var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
15
16var _core = require("@emotion/core");
17
18var _autoId = require("@reach/auto-id");
19
20var _react = require("react");
21
22var _Box = _interopRequireDefault(require("../Box"));
23
24var _CloseButton = _interopRequireDefault(require("../CloseButton"));
25
26var _ColorModeProvider = require("../ColorModeProvider");
27
28var _Popper = _interopRequireWildcard(require("../Popper"));
29
30var _usePrevious = _interopRequireDefault(require("../usePrevious"));
31
32var _utils = require("../utils");
33
34function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
35
36function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
37
38/**
39 * Hook based idea:
40 * const {referenceProps, popoverProps, arrowProps, state, actions} = usePopover(props).
41 *
42 * The popover must meet the AA Success Criterion
43 * https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html
44 * https://www.w3.org/WAI/WCAG21/Techniques/client-side-script/SCR39
45 */
46var PopoverContext = (0, _react.createContext)();
47
48var usePopoverContext = function usePopoverContext() {
49 var context = (0, _react.useContext)(PopoverContext);
50
51 if (context == null) {
52 throw Error("usePopoverContext must be used within <Popover/>");
53 }
54
55 return context;
56}; /////////////////////////////////////////////////////////////////////
57
58
59var PopoverTrigger = function PopoverTrigger(_ref) {
60 var children = _ref.children;
61
62 var _usePopoverContext = usePopoverContext(),
63 referenceRef = _usePopoverContext.referenceRef,
64 popoverId = _usePopoverContext.popoverId,
65 onToggle = _usePopoverContext.onToggle,
66 trigger = _usePopoverContext.trigger,
67 onOpen = _usePopoverContext.onOpen,
68 isOpen = _usePopoverContext.isOpen,
69 onClose = _usePopoverContext.onClose,
70 isHoveringRef = _usePopoverContext.isHoveringRef;
71
72 var child = _react.Children.only(children);
73
74 var eventHandlers = {};
75
76 if (trigger === "click") {
77 eventHandlers = {
78 onClick: (0, _utils.wrapEvent)(child.props.onClick, onToggle)
79 };
80 }
81
82 var openTimeout = (0, _react.useRef)(null);
83
84 if (trigger === "hover") {
85 eventHandlers = {
86 onFocus: (0, _utils.wrapEvent)(child.props.onFocus, onOpen),
87 onKeyDown: (0, _utils.wrapEvent)(child.props.onKeyDown, function (event) {
88 if (event.key === "Escape") {
89 setTimeout(onClose, 300);
90 }
91 }),
92 onBlur: (0, _utils.wrapEvent)(child.props.onBlur, onClose),
93 onMouseEnter: (0, _utils.wrapEvent)(child.props.onMouseEnter, function () {
94 isHoveringRef.current = true;
95 openTimeout.current = setTimeout(onOpen, 300);
96 }),
97 onMouseLeave: (0, _utils.wrapEvent)(child.props.onMouseLeave, function () {
98 isHoveringRef.current = false;
99
100 if (openTimeout.current) {
101 clearTimeout(openTimeout.current);
102 openTimeout.current = null;
103 }
104
105 setTimeout(function () {
106 if (isHoveringRef.current === false) {
107 onClose();
108 }
109 }, 300);
110 })
111 };
112 }
113
114 return (0, _react.cloneElement)(child, _objectSpread({
115 "aria-haspopup": "dialog",
116 "aria-expanded": isOpen,
117 "aria-controls": popoverId,
118 ref: referenceRef
119 }, eventHandlers));
120}; /////////////////////////////////////////////////////////////////////
121
122
123exports.PopoverTrigger = PopoverTrigger;
124
125var PopoverContent = function PopoverContent(_ref2) {
126 var onKeyDown = _ref2.onKeyDown,
127 onBlurProp = _ref2.onBlur,
128 onMouseLeave = _ref2.onMouseLeave,
129 onMouseEnter = _ref2.onMouseEnter,
130 onFocus = _ref2.onFocus,
131 _ref2$gutter = _ref2.gutter,
132 gutter = _ref2$gutter === void 0 ? 4 : _ref2$gutter,
133 ariaLabel = _ref2["aria-label"],
134 props = (0, _objectWithoutPropertiesLoose2["default"])(_ref2, ["onKeyDown", "onBlur", "onMouseLeave", "onMouseEnter", "onFocus", "gutter", "aria-label"]);
135
136 var _usePopoverContext2 = usePopoverContext(),
137 popoverRef = _usePopoverContext2.popoverRef,
138 referenceRef = _usePopoverContext2.referenceRef,
139 placement = _usePopoverContext2.placement,
140 popoverId = _usePopoverContext2.popoverId,
141 isOpen = _usePopoverContext2.isOpen,
142 onBlur = _usePopoverContext2.onBlur,
143 closeOnEsc = _usePopoverContext2.closeOnEsc,
144 onClose = _usePopoverContext2.onClose,
145 isHoveringRef = _usePopoverContext2.isHoveringRef,
146 trigger = _usePopoverContext2.trigger,
147 headerId = _usePopoverContext2.headerId,
148 bodyId = _usePopoverContext2.bodyId,
149 usePortal = _usePopoverContext2.usePortal;
150
151 var _useColorMode = (0, _ColorModeProvider.useColorMode)(),
152 colorMode = _useColorMode.colorMode;
153
154 var bg = colorMode === "light" ? "white" : "gray.700";
155 var eventHandlers = {};
156 var roleProps = {};
157
158 if (trigger === "click") {
159 eventHandlers = {
160 onBlur: (0, _utils.wrapEvent)(onBlurProp, onBlur)
161 };
162 roleProps = {
163 role: "dialog",
164 "aria-modal": "false"
165 };
166 }
167
168 if (trigger === "hover") {
169 eventHandlers = {
170 onMouseEnter: (0, _utils.wrapEvent)(onMouseEnter, function () {
171 isHoveringRef.current = true;
172 }),
173 onMouseLeave: (0, _utils.wrapEvent)(onMouseLeave, function () {
174 isHoveringRef.current = false;
175 setTimeout(onClose, 300);
176 })
177 };
178 roleProps = {
179 role: "tooltip"
180 };
181 }
182
183 eventHandlers = _objectSpread({}, eventHandlers, {
184 onKeyDown: (0, _utils.wrapEvent)(onKeyDown, function (event) {
185 if (event.key === "Escape" && closeOnEsc) {
186 onClose && onClose();
187 }
188 })
189 });
190 return (0, _core.jsx)(_Popper["default"], (0, _extends2["default"])({
191 as: "section",
192 usePortal: usePortal,
193 isOpen: isOpen,
194 placement: placement,
195 "aria-label": ariaLabel,
196 anchorEl: referenceRef.current,
197 ref: popoverRef,
198 bg: bg,
199 id: popoverId,
200 "aria-hidden": !isOpen,
201 tabIndex: "-1",
202 borderWidth: "1px",
203 width: "100%",
204 position: "relative",
205 display: "flex",
206 flexDirection: "column",
207 rounded: "md",
208 shadow: "sm",
209 maxWidth: "xs",
210 modifiers: {
211 offset: {
212 enabled: true,
213 offset: "0, " + gutter
214 }
215 },
216 _focus: {
217 outline: 0,
218 shadow: "outline"
219 },
220 "aria-labelledby": headerId,
221 "aria-describedby": bodyId
222 }, roleProps, eventHandlers, props));
223}; /////////////////////////////////////////////////////////////////////
224
225
226exports.PopoverContent = PopoverContent;
227
228var Popover = function Popover(_ref3) {
229 var id = _ref3.id,
230 isOpenProp = _ref3.isOpen,
231 initialFocusRef = _ref3.initialFocusRef,
232 defaultIsOpen = _ref3.defaultIsOpen,
233 _ref3$usePortal = _ref3.usePortal,
234 usePortal = _ref3$usePortal === void 0 ? false : _ref3$usePortal,
235 _ref3$returnFocusOnCl = _ref3.returnFocusOnClose,
236 returnFocusOnClose = _ref3$returnFocusOnCl === void 0 ? true : _ref3$returnFocusOnCl,
237 _ref3$trigger = _ref3.trigger,
238 trigger = _ref3$trigger === void 0 ? "click" : _ref3$trigger,
239 placement = _ref3.placement,
240 children = _ref3.children,
241 _ref3$closeOnBlur = _ref3.closeOnBlur,
242 closeOnBlur = _ref3$closeOnBlur === void 0 ? true : _ref3$closeOnBlur,
243 _ref3$closeOnEsc = _ref3.closeOnEsc,
244 closeOnEsc = _ref3$closeOnEsc === void 0 ? true : _ref3$closeOnEsc,
245 onOpenProp = _ref3.onOpen,
246 onCloseProp = _ref3.onClose;
247
248 var _useState = (0, _react.useState)(defaultIsOpen || false),
249 isOpen = _useState[0],
250 setIsOpen = _useState[1];
251
252 var _useRef = (0, _react.useRef)(isOpenProp != null),
253 isControlled = _useRef.current;
254
255 var isHoveringRef = (0, _react.useRef)();
256 var referenceRef = (0, _react.useRef)();
257 var popoverRef = (0, _react.useRef)();
258
259 var _isOpen = isControlled ? isOpenProp : isOpen;
260
261 var onToggle = function onToggle() {
262 if (!isControlled) {
263 setIsOpen(!_isOpen);
264 }
265
266 if (!_isOpen === true) {
267 onOpenProp && onOpenProp();
268 } else {
269 onCloseProp && onCloseProp();
270 }
271 };
272
273 var onOpen = function onOpen() {
274 if (!isControlled) {
275 setIsOpen(true);
276 }
277
278 if (onOpenProp) {
279 onOpenProp();
280 }
281 };
282
283 var onClose = function onClose() {
284 if (!isControlled) {
285 setIsOpen(false);
286 }
287
288 if (onCloseProp) {
289 onCloseProp();
290 }
291 };
292
293 var handleBlur = function handleBlur(event) {
294 if (_isOpen && closeOnBlur && popoverRef.current && referenceRef.current && !popoverRef.current.contains(event.relatedTarget) && !referenceRef.current.contains(event.relatedTarget)) {
295 onClose();
296 }
297 }; // A unique fallback id in case the id prop wasn't passed
298
299
300 var fallbackId = "popover-" + (0, _autoId.useId)();
301 var popoverId = id || fallbackId;
302 var headerId = popoverId + "-header";
303 var bodyId = popoverId + "-body";
304 var prevIsOpen = (0, _usePrevious["default"])(_isOpen);
305 (0, _react.useEffect)(function () {
306 if (_isOpen && trigger === "click") {
307 requestAnimationFrame(function () {
308 if (initialFocusRef && initialFocusRef.current) {
309 initialFocusRef.current.focus();
310 } else {
311 if (popoverRef.current) {
312 popoverRef.current.focus();
313 }
314 }
315 });
316 }
317
318 if (!_isOpen && prevIsOpen && trigger === "click" && returnFocusOnClose) {
319 if (referenceRef.current) {
320 referenceRef.current.focus();
321 }
322 }
323 }, [_isOpen, popoverRef, initialFocusRef, trigger, referenceRef, prevIsOpen, returnFocusOnClose]);
324 var context = {
325 popoverRef: popoverRef,
326 placement: placement,
327 referenceRef: referenceRef,
328 headerId: headerId,
329 bodyId: bodyId,
330 popoverId: popoverId,
331 onOpen: onOpen,
332 onClose: onClose,
333 onToggle: onToggle,
334 trigger: trigger,
335 isOpen: _isOpen,
336 onBlur: handleBlur,
337 closeOnEsc: closeOnEsc,
338 initialFocusRef: initialFocusRef,
339 isHoveringRef: isHoveringRef,
340 usePortal: usePortal
341 };
342 return (0, _core.jsx)(PopoverContext.Provider, {
343 value: context
344 }, typeof children === "function" ? children({
345 isOpen: _isOpen,
346 onClose: onClose
347 }) : children);
348}; /////////////////////////////////////////////////////////////////////
349
350
351exports.Popover = Popover;
352
353var PopoverHeader = function PopoverHeader(props) {
354 var _usePopoverContext3 = usePopoverContext(),
355 headerId = _usePopoverContext3.headerId;
356
357 return (0, _core.jsx)(_Box["default"], (0, _extends2["default"])({
358 as: "header",
359 id: headerId,
360 px: 3,
361 py: 2,
362 borderBottomWidth: "1px"
363 }, props));
364}; /////////////////////////////////////////////////////////////////////
365
366
367exports.PopoverHeader = PopoverHeader;
368
369var PopoverFooter = function PopoverFooter(props) {
370 return (0, _core.jsx)(_Box["default"], (0, _extends2["default"])({
371 as: "footer",
372 px: 3,
373 py: 2,
374 borderTopWidth: "1px"
375 }, props));
376}; /////////////////////////////////////////////////////////////////////
377
378
379exports.PopoverFooter = PopoverFooter;
380
381var PopoverBody = function PopoverBody(props) {
382 var _usePopoverContext4 = usePopoverContext(),
383 bodyId = _usePopoverContext4.bodyId;
384
385 return (0, _core.jsx)(_Box["default"], (0, _extends2["default"])({
386 id: bodyId,
387 flex: "1",
388 px: 3,
389 py: 2
390 }, props));
391}; /////////////////////////////////////////////////////////////////////
392
393
394exports.PopoverBody = PopoverBody;
395
396var PopoverArrow = function PopoverArrow(props) {
397 return (0, _core.jsx)(_Popper.PopperArrow, props);
398}; /////////////////////////////////////////////////////////////////////
399
400
401exports.PopoverArrow = PopoverArrow;
402
403var PopoverCloseButton = function PopoverCloseButton(_ref4) {
404 var onClick = _ref4.onClick,
405 props = (0, _objectWithoutPropertiesLoose2["default"])(_ref4, ["onClick"]);
406
407 var _usePopoverContext5 = usePopoverContext(),
408 onClose = _usePopoverContext5.onClose;
409
410 return (0, _core.jsx)(_CloseButton["default"], (0, _extends2["default"])({
411 size: "sm",
412 onClick: (0, _utils.wrapEvent)(onClick, onClose),
413 "aria-label": "Close",
414 pos: "absolute",
415 rounded: "md",
416 top: 1,
417 right: 2,
418 p: 2
419 }, props));
420}; /////////////////////////////////////////////////////////////////////
421
422
423exports.PopoverCloseButton = PopoverCloseButton;
\No newline at end of file