@canonical/react-components 2.0.0 → 2.1.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.
@@ -7,7 +7,7 @@ exports.default = exports.ConfirmationButton = void 0;
7
7
  var _react = _interopRequireDefault(require("react"));
8
8
  var _ActionButton = _interopRequireDefault(require("../ActionButton"));
9
9
  var _ConfirmationModal = _interopRequireDefault(require("../ConfirmationModal"));
10
- var _reactUseportal = _interopRequireDefault(require("react-useportal"));
10
+ var _external = require("../../external");
11
11
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
12
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
13
13
  const generateTitle = title => {
@@ -35,7 +35,7 @@ const ConfirmationButton = _ref => {
35
35
  closePortal,
36
36
  isOpen,
37
37
  Portal
38
- } = (0, _reactUseportal.default)();
38
+ } = (0, _external.usePortal)();
39
39
  const handleCancelModal = () => {
40
40
  closePortal();
41
41
  if (confirmationModalProps.close) {
@@ -6,10 +6,10 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = exports.Label = void 0;
7
7
  var _classnames = _interopRequireDefault(require("classnames"));
8
8
  var _react = _interopRequireWildcard(require("react"));
9
- var _reactUseportal = _interopRequireDefault(require("react-useportal"));
10
9
  var _hooks = require("../../hooks");
11
10
  var _Button = _interopRequireDefault(require("../Button"));
12
11
  var _ContextualMenuDropdown = _interopRequireDefault(require("./ContextualMenuDropdown"));
12
+ var _external = require("../../external");
13
13
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -100,7 +100,7 @@ const ContextualMenu = _ref => {
100
100
  closePortal,
101
101
  isOpen,
102
102
  Portal
103
- } = (0, _reactUseportal.default)({
103
+ } = (0, _external.usePortal)({
104
104
  closeOnEsc,
105
105
  closeOnOutsideClick,
106
106
  isOpen: visible,
@@ -18,7 +18,7 @@ export type Position = "left" | "center" | "right";
18
18
  export type Props<L = null> = {
19
19
  adjustedPosition?: Position;
20
20
  autoAdjust?: boolean;
21
- handleClose?: (evt?: MouseEvent) => void;
21
+ handleClose?: (evt?: React.MouseEvent<HTMLButtonElement>) => void;
22
22
  constrainPanelWidth?: boolean;
23
23
  dropdownClassName?: string;
24
24
  dropdownContent?: ReactNode | ((close: () => void) => React.JSX.Element);
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.position = exports.default = exports.adjustForWindow = void 0;
7
7
  var _classnames = _interopRequireDefault(require("classnames"));
8
8
  var _react = _interopRequireWildcard(require("react"));
9
- var _reactUseportal = _interopRequireDefault(require("react-useportal"));
10
9
  var _hooks = require("../../hooks");
10
+ var _external = require("../../external");
11
11
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
12
12
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
13
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -142,7 +142,7 @@ const Tooltip = _ref => {
142
142
  closePortal,
143
143
  isOpen,
144
144
  Portal
145
- } = (0, _reactUseportal.default)({
145
+ } = (0, _external.usePortal)({
146
146
  programmaticallyOpen: true
147
147
  });
148
148
  const tooltipId = (0, _react.useId)();
@@ -5,7 +5,7 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
5
5
  import React from "react";
6
6
  import ActionButton from "../ActionButton";
7
7
  import ConfirmationModal from "../ConfirmationModal";
8
- import usePortal from "react-useportal";
8
+ import { usePortal } from "../../external";
9
9
  var generateTitle = title => {
10
10
  if (typeof title === "string") {
11
11
  return title;
@@ -4,10 +4,10 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
4
4
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
5
5
  import classNames from "classnames";
6
6
  import React, { useCallback, useEffect, useId, useRef, useState } from "react";
7
- import usePortal from "react-useportal";
8
7
  import { useListener, usePrevious } from "../../hooks";
9
8
  import Button from "../Button";
10
9
  import ContextualMenuDropdown from "./ContextualMenuDropdown";
10
+ import { usePortal } from "../../external";
11
11
  export var Label = /*#__PURE__*/function (Label) {
12
12
  Label["Toggle"] = "Toggle menu";
13
13
  return Label;
@@ -18,7 +18,7 @@ export type Position = "left" | "center" | "right";
18
18
  export type Props<L = null> = {
19
19
  adjustedPosition?: Position;
20
20
  autoAdjust?: boolean;
21
- handleClose?: (evt?: MouseEvent) => void;
21
+ handleClose?: (evt?: React.MouseEvent<HTMLButtonElement>) => void;
22
22
  constrainPanelWidth?: boolean;
23
23
  dropdownClassName?: string;
24
24
  dropdownContent?: ReactNode | ((close: () => void) => React.JSX.Element);
@@ -1,7 +1,7 @@
1
1
  import classNames from "classnames";
2
2
  import React, { useCallback, useEffect, useId, useRef, useState } from "react";
3
- import usePortal from "react-useportal";
4
3
  import { useWindowFitment, useListener } from "../../hooks";
4
+ import { usePortal } from "../../external";
5
5
  export var position = {
6
6
  btmCenter: "btm-center",
7
7
  btmLeft: "btm-left",
@@ -0,0 +1,2 @@
1
+ export { usePortal } from "./usePortal";
2
+ export type { UsePortalOptions } from "./usePortal";
@@ -0,0 +1 @@
1
+ export { usePortal } from "./usePortal";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This is a reference implementation of the usePortal hook from react-useportal: https://github.com/iamthesiz/react-useportal/blob/master/usePortal.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/react-useportal/blob/master/license.md
4
+ */
5
+ import { ReactNode, SyntheticEvent, RefObject } from "react";
6
+ type CustomEvent<T = HTMLElement> = {
7
+ event?: SyntheticEvent<T, Event>;
8
+ portal: RefObject<HTMLElement>;
9
+ targetEl: RefObject<HTMLElement>;
10
+ } & SyntheticEvent<T, Event>;
11
+ type CustomEventHandler<T = HTMLElement> = (customEvent: CustomEvent<T>) => void;
12
+ export type UsePortalOptions = {
13
+ closeOnOutsideClick?: boolean;
14
+ closeOnEsc?: boolean;
15
+ bindTo?: RefObject<HTMLElement>;
16
+ isOpen?: boolean;
17
+ onOpen?: CustomEventHandler;
18
+ onClose?: CustomEventHandler;
19
+ onPortalClick?: CustomEventHandler;
20
+ programmaticallyOpen?: boolean;
21
+ };
22
+ export declare const errorMessage1 = "You must either add a `ref` to the element you are interacting with or pass an `event` to openPortal(e) or togglePortal(e) when the `programmaticallyOpen` option is not set to `true`.";
23
+ export declare const usePortal: ({ closeOnOutsideClick, closeOnEsc, bindTo, isOpen: defaultIsOpen, onOpen, onClose, onPortalClick, programmaticallyOpen, }?: UsePortalOptions) => (boolean | RefObject<HTMLElement> | ((e?: SyntheticEvent<HTMLElement, Event>) => void) | (({ children }: {
24
+ children: ReactNode;
25
+ }) => import("react").JSX.Element))[] & {
26
+ isOpen: boolean;
27
+ openPortal: (e?: SyntheticEvent<HTMLElement, Event>) => void;
28
+ ref: RefObject<HTMLElement>;
29
+ closePortal: (e?: SyntheticEvent<HTMLElement, Event>) => void;
30
+ togglePortal: (e?: SyntheticEvent<HTMLElement, Event>) => void;
31
+ Portal: ({ children }: {
32
+ children: ReactNode;
33
+ }) => import("react").JSX.Element;
34
+ portalRef: RefObject<HTMLElement>;
35
+ bind: {
36
+ ref: RefObject<HTMLElement>;
37
+ };
38
+ };
39
+ export {};
@@ -0,0 +1,125 @@
1
+ /**
2
+ * This is a reference implementation of the usePortal hook from react-useportal: https://github.com/iamthesiz/react-useportal/blob/master/usePortal.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/react-useportal/blob/master/license.md
4
+ */
5
+ import { useState, useRef, useEffect, useCallback, useMemo } from "react";
6
+ import { createPortal } from "react-dom";
7
+ import { useSSR } from "./useSSR";
8
+ export var errorMessage1 = "You must either add a `ref` to the element you are interacting with or pass an `event` to openPortal(e) or togglePortal(e) when the `programmaticallyOpen` option is not set to `true`.";
9
+ export var usePortal = function usePortal() {
10
+ var {
11
+ closeOnOutsideClick = true,
12
+ closeOnEsc = true,
13
+ bindTo,
14
+ // attach the portal to this node in the DOM
15
+ isOpen: defaultIsOpen = false,
16
+ onOpen,
17
+ onClose,
18
+ onPortalClick,
19
+ programmaticallyOpen = false
20
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
21
+ var {
22
+ isServer,
23
+ isBrowser
24
+ } = useSSR();
25
+ var [isOpen, makeOpen] = useState(defaultIsOpen);
26
+ // we use this ref because `isOpen` is stale for handleOutsideMouseClick
27
+ var open = useRef(isOpen);
28
+ var setOpen = useCallback(v => {
29
+ // workaround to not have stale `isOpen` in the handleOutsideMouseClick
30
+ open.current = v;
31
+ makeOpen(v);
32
+ }, []);
33
+ var targetEl = useRef(null); // this is the element you are clicking/hovering/whatever, to trigger opening the portal
34
+ var portal = useRef(isBrowser ? document.createElement("div") : null);
35
+ useEffect(() => {
36
+ if (isBrowser && !portal.current) portal.current = document.createElement("div");
37
+ }, [isBrowser, portal]);
38
+ var elToMountTo = useMemo(() => {
39
+ if (isServer) return null;
40
+ return bindTo && bindTo.current || document.body;
41
+ }, [isServer, bindTo]);
42
+ var createCustomEvent = e => {
43
+ if (!e) return {
44
+ portal,
45
+ targetEl,
46
+ event: e
47
+ };
48
+ var event = e || {};
49
+ if (event.persist) event.persist();
50
+ event.portal = portal;
51
+ event.targetEl = targetEl;
52
+ event.event = e;
53
+ var {
54
+ currentTarget
55
+ } = e;
56
+ if (!targetEl.current && currentTarget && currentTarget !== document) targetEl.current = event.currentTarget;
57
+ return event;
58
+ };
59
+ var openPortal = useCallback(e => {
60
+ if (isServer) return;
61
+ var customEvent = createCustomEvent(e);
62
+ // for some reason, when we don't have the event argument, there
63
+ // is a weird race condition. Would like to see if we can remove
64
+ // setTimeout, but for now this works
65
+ if (targetEl.current == null && !programmaticallyOpen) {
66
+ setTimeout(() => setOpen(true), 0);
67
+ throw Error(errorMessage1);
68
+ }
69
+ if (onOpen) onOpen(customEvent);
70
+ setOpen(true);
71
+ }, [isServer, portal, setOpen, targetEl, onOpen, programmaticallyOpen]);
72
+ var closePortal = useCallback(e => {
73
+ if (isServer) return;
74
+ var customEvent = createCustomEvent(e);
75
+ if (onClose && open.current) onClose(customEvent);
76
+ if (open.current) setOpen(false);
77
+ }, [isServer, onClose, setOpen]);
78
+ var togglePortal = useCallback(e => open.current ? closePortal(e) : openPortal(e), [closePortal, openPortal]);
79
+ var handleKeydown = useCallback(e => e.key === "Escape" && closeOnEsc ? closePortal(e) : undefined, [closeOnEsc, closePortal]);
80
+ var handleOutsideMouseClick = useCallback(e => {
81
+ var containsTarget = target => target.current.contains(e.target);
82
+ // There might not be a targetEl if the portal was opened programmatically.
83
+ if (containsTarget(portal) || e.button !== 0 || !open.current || targetEl.current && containsTarget(targetEl)) return;
84
+ if (closeOnOutsideClick) closePortal(e);
85
+ }, [isServer, closePortal, closeOnOutsideClick, portal]);
86
+ var handleMouseDown = useCallback(e => {
87
+ if (isServer || !(portal.current instanceof HTMLElement)) return;
88
+ var customEvent = createCustomEvent(e);
89
+ if (portal.current.contains(customEvent.target) && onPortalClick) onPortalClick(customEvent);
90
+ handleOutsideMouseClick(e);
91
+ }, [handleOutsideMouseClick, isServer]);
92
+ useEffect(() => {
93
+ if (isServer) return null;
94
+ if (!(elToMountTo instanceof HTMLElement) || !(portal.current instanceof HTMLElement)) return null;
95
+ var node = portal.current;
96
+ elToMountTo.appendChild(portal.current);
97
+ document.addEventListener("keydown", handleKeydown);
98
+ document.addEventListener("mousedown", handleMouseDown);
99
+ return () => {
100
+ document.removeEventListener("keydown", handleKeydown);
101
+ document.removeEventListener("mousedown", handleMouseDown);
102
+ elToMountTo.removeChild(node);
103
+ };
104
+ }, [isServer, handleOutsideMouseClick, handleKeydown, elToMountTo, portal]);
105
+ var Portal = useCallback(_ref => {
106
+ var {
107
+ children
108
+ } = _ref;
109
+ if (portal.current != null) return /*#__PURE__*/createPortal(children, portal.current);
110
+ return null;
111
+ }, [portal]);
112
+ return Object.assign([openPortal, closePortal, open.current, Portal, togglePortal, targetEl, portal], {
113
+ isOpen: open.current,
114
+ openPortal,
115
+ ref: targetEl,
116
+ closePortal,
117
+ togglePortal,
118
+ Portal,
119
+ portalRef: portal,
120
+ bind: {
121
+ // used if you want to spread all html attributes onto the target element
122
+ ref: targetEl
123
+ }
124
+ });
125
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * This is a reference implementation of the usePortal hook from react-useportal: https://github.com/iamthesiz/react-useportal/blob/master/usePortal.test.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/react-useportal/blob/master/license.md
4
+ */
5
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This is a reference implementation of the useSSR hook from use-ssr: https://github.com/iamthesiz/use-ssr/blob/master/useSSR.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/use-ssr/blob/master/license.md
4
+ */
5
+ interface UseSSRReturn {
6
+ isBrowser: boolean;
7
+ isServer: boolean;
8
+ device: Device;
9
+ canUseWorkers: boolean;
10
+ canUseEventListeners: boolean;
11
+ canUseViewport: boolean;
12
+ }
13
+ export declare enum Device {
14
+ Browser = "browser",
15
+ Server = "server"
16
+ }
17
+ export declare const weAreServer: () => void;
18
+ export declare const useSSR: () => UseSSRReturn;
19
+ export {};
@@ -0,0 +1,43 @@
1
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
5
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6
+ /**
7
+ * This is a reference implementation of the useSSR hook from use-ssr: https://github.com/iamthesiz/use-ssr/blob/master/useSSR.ts
8
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/use-ssr/blob/master/license.md
9
+ */
10
+
11
+ export var Device = /*#__PURE__*/function (Device) {
12
+ Device["Browser"] = "browser";
13
+ Device["Server"] = "server";
14
+ return Device;
15
+ }({});
16
+ var {
17
+ Browser,
18
+ Server
19
+ } = Device;
20
+ var canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement);
21
+ var device = canUseDOM ? Browser : Server;
22
+ var SSRObject = {
23
+ isBrowser: device === Browser,
24
+ isServer: device === Server,
25
+ device,
26
+ canUseWorkers: typeof Worker !== "undefined",
27
+ canUseEventListeners: device === Browser && !!window.addEventListener,
28
+ canUseViewport: device === Browser && !!window.screen
29
+ };
30
+ var assign = function assign() {
31
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
32
+ args[_key] = arguments[_key];
33
+ }
34
+ return args.reduce((acc, obj) => _objectSpread(_objectSpread({}, acc), obj), {});
35
+ };
36
+ var values = obj => Object.keys(obj).map(key => obj[key]);
37
+ var toArrayObject = () => assign((values(SSRObject), SSRObject));
38
+ var useSSRObject = toArrayObject();
39
+ export var weAreServer = () => {
40
+ SSRObject.isServer = true;
41
+ useSSRObject = toArrayObject();
42
+ };
43
+ export var useSSR = () => useSSRObject;
@@ -138,3 +138,5 @@ export type { WindowFitment } from "./hooks";
138
138
  export { isNavigationAnchor, isNavigationButton } from "./utils";
139
139
  export type { ClassName, Headings, PropsWithSpread, SortDirection, SubComponentProps, TSFixMe, ValueOf, } from "./types";
140
140
  export { Theme } from "./enums";
141
+ export type { UsePortalOptions } from "./external";
142
+ export { usePortal } from "./external";
package/dist/esm/index.js CHANGED
@@ -70,4 +70,5 @@ export { default as TablePaginationControls } from "./components/TablePagination
70
70
  export { default as CustomSelect } from "./components/CustomSelect";
71
71
  export { useOnClickOutside, useClickOutside, useId, useListener, useOnEscapePressed, usePagination, usePrevious, useThrottle, useWindowFitment } from "./hooks";
72
72
  export { isNavigationAnchor, isNavigationButton } from "./utils";
73
- export { Theme } from "./enums";
73
+ export { Theme } from "./enums";
74
+ export { usePortal } from "./external";
@@ -0,0 +1,2 @@
1
+ export { usePortal } from "./usePortal";
2
+ export type { UsePortalOptions } from "./usePortal";
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "usePortal", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _usePortal.usePortal;
10
+ }
11
+ });
12
+ var _usePortal = require("./usePortal");
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This is a reference implementation of the usePortal hook from react-useportal: https://github.com/iamthesiz/react-useportal/blob/master/usePortal.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/react-useportal/blob/master/license.md
4
+ */
5
+ import { ReactNode, SyntheticEvent, RefObject } from "react";
6
+ type CustomEvent<T = HTMLElement> = {
7
+ event?: SyntheticEvent<T, Event>;
8
+ portal: RefObject<HTMLElement>;
9
+ targetEl: RefObject<HTMLElement>;
10
+ } & SyntheticEvent<T, Event>;
11
+ type CustomEventHandler<T = HTMLElement> = (customEvent: CustomEvent<T>) => void;
12
+ export type UsePortalOptions = {
13
+ closeOnOutsideClick?: boolean;
14
+ closeOnEsc?: boolean;
15
+ bindTo?: RefObject<HTMLElement>;
16
+ isOpen?: boolean;
17
+ onOpen?: CustomEventHandler;
18
+ onClose?: CustomEventHandler;
19
+ onPortalClick?: CustomEventHandler;
20
+ programmaticallyOpen?: boolean;
21
+ };
22
+ export declare const errorMessage1 = "You must either add a `ref` to the element you are interacting with or pass an `event` to openPortal(e) or togglePortal(e) when the `programmaticallyOpen` option is not set to `true`.";
23
+ export declare const usePortal: ({ closeOnOutsideClick, closeOnEsc, bindTo, isOpen: defaultIsOpen, onOpen, onClose, onPortalClick, programmaticallyOpen, }?: UsePortalOptions) => (boolean | RefObject<HTMLElement> | ((e?: SyntheticEvent<HTMLElement, Event>) => void) | (({ children }: {
24
+ children: ReactNode;
25
+ }) => import("react").JSX.Element))[] & {
26
+ isOpen: boolean;
27
+ openPortal: (e?: SyntheticEvent<HTMLElement, Event>) => void;
28
+ ref: RefObject<HTMLElement>;
29
+ closePortal: (e?: SyntheticEvent<HTMLElement, Event>) => void;
30
+ togglePortal: (e?: SyntheticEvent<HTMLElement, Event>) => void;
31
+ Portal: ({ children }: {
32
+ children: ReactNode;
33
+ }) => import("react").JSX.Element;
34
+ portalRef: RefObject<HTMLElement>;
35
+ bind: {
36
+ ref: RefObject<HTMLElement>;
37
+ };
38
+ };
39
+ export {};
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.usePortal = exports.errorMessage1 = void 0;
7
+ var _react = require("react");
8
+ var _reactDom = require("react-dom");
9
+ var _useSSR = require("./useSSR");
10
+ /**
11
+ * This is a reference implementation of the usePortal hook from react-useportal: https://github.com/iamthesiz/react-useportal/blob/master/usePortal.ts
12
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/react-useportal/blob/master/license.md
13
+ */
14
+
15
+ const errorMessage1 = exports.errorMessage1 = "You must either add a `ref` to the element you are interacting with or pass an `event` to openPortal(e) or togglePortal(e) when the `programmaticallyOpen` option is not set to `true`.";
16
+ const usePortal = function () {
17
+ let {
18
+ closeOnOutsideClick = true,
19
+ closeOnEsc = true,
20
+ bindTo,
21
+ // attach the portal to this node in the DOM
22
+ isOpen: defaultIsOpen = false,
23
+ onOpen,
24
+ onClose,
25
+ onPortalClick,
26
+ programmaticallyOpen = false
27
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
28
+ const {
29
+ isServer,
30
+ isBrowser
31
+ } = (0, _useSSR.useSSR)();
32
+ const [isOpen, makeOpen] = (0, _react.useState)(defaultIsOpen);
33
+ // we use this ref because `isOpen` is stale for handleOutsideMouseClick
34
+ const open = (0, _react.useRef)(isOpen);
35
+ const setOpen = (0, _react.useCallback)(v => {
36
+ // workaround to not have stale `isOpen` in the handleOutsideMouseClick
37
+ open.current = v;
38
+ makeOpen(v);
39
+ }, []);
40
+ const targetEl = (0, _react.useRef)(null); // this is the element you are clicking/hovering/whatever, to trigger opening the portal
41
+ const portal = (0, _react.useRef)(isBrowser ? document.createElement("div") : null);
42
+ (0, _react.useEffect)(() => {
43
+ if (isBrowser && !portal.current) portal.current = document.createElement("div");
44
+ }, [isBrowser, portal]);
45
+ const elToMountTo = (0, _react.useMemo)(() => {
46
+ if (isServer) return null;
47
+ return bindTo && bindTo.current || document.body;
48
+ }, [isServer, bindTo]);
49
+ const createCustomEvent = e => {
50
+ if (!e) return {
51
+ portal,
52
+ targetEl,
53
+ event: e
54
+ };
55
+ const event = e || {};
56
+ if (event.persist) event.persist();
57
+ event.portal = portal;
58
+ event.targetEl = targetEl;
59
+ event.event = e;
60
+ const {
61
+ currentTarget
62
+ } = e;
63
+ if (!targetEl.current && currentTarget && currentTarget !== document) targetEl.current = event.currentTarget;
64
+ return event;
65
+ };
66
+ const openPortal = (0, _react.useCallback)(e => {
67
+ if (isServer) return;
68
+ const customEvent = createCustomEvent(e);
69
+ // for some reason, when we don't have the event argument, there
70
+ // is a weird race condition. Would like to see if we can remove
71
+ // setTimeout, but for now this works
72
+ if (targetEl.current == null && !programmaticallyOpen) {
73
+ setTimeout(() => setOpen(true), 0);
74
+ throw Error(errorMessage1);
75
+ }
76
+ if (onOpen) onOpen(customEvent);
77
+ setOpen(true);
78
+ }, [isServer, portal, setOpen, targetEl, onOpen, programmaticallyOpen]);
79
+ const closePortal = (0, _react.useCallback)(e => {
80
+ if (isServer) return;
81
+ const customEvent = createCustomEvent(e);
82
+ if (onClose && open.current) onClose(customEvent);
83
+ if (open.current) setOpen(false);
84
+ }, [isServer, onClose, setOpen]);
85
+ const togglePortal = (0, _react.useCallback)(e => open.current ? closePortal(e) : openPortal(e), [closePortal, openPortal]);
86
+ const handleKeydown = (0, _react.useCallback)(e => e.key === "Escape" && closeOnEsc ? closePortal(e) : undefined, [closeOnEsc, closePortal]);
87
+ const handleOutsideMouseClick = (0, _react.useCallback)(e => {
88
+ const containsTarget = target => target.current.contains(e.target);
89
+ // There might not be a targetEl if the portal was opened programmatically.
90
+ if (containsTarget(portal) || e.button !== 0 || !open.current || targetEl.current && containsTarget(targetEl)) return;
91
+ if (closeOnOutsideClick) closePortal(e);
92
+ }, [isServer, closePortal, closeOnOutsideClick, portal]);
93
+ const handleMouseDown = (0, _react.useCallback)(e => {
94
+ if (isServer || !(portal.current instanceof HTMLElement)) return;
95
+ const customEvent = createCustomEvent(e);
96
+ if (portal.current.contains(customEvent.target) && onPortalClick) onPortalClick(customEvent);
97
+ handleOutsideMouseClick(e);
98
+ }, [handleOutsideMouseClick, isServer]);
99
+ (0, _react.useEffect)(() => {
100
+ if (isServer) return null;
101
+ if (!(elToMountTo instanceof HTMLElement) || !(portal.current instanceof HTMLElement)) return null;
102
+ const node = portal.current;
103
+ elToMountTo.appendChild(portal.current);
104
+ document.addEventListener("keydown", handleKeydown);
105
+ document.addEventListener("mousedown", handleMouseDown);
106
+ return () => {
107
+ document.removeEventListener("keydown", handleKeydown);
108
+ document.removeEventListener("mousedown", handleMouseDown);
109
+ elToMountTo.removeChild(node);
110
+ };
111
+ }, [isServer, handleOutsideMouseClick, handleKeydown, elToMountTo, portal]);
112
+ const Portal = (0, _react.useCallback)(_ref => {
113
+ let {
114
+ children
115
+ } = _ref;
116
+ if (portal.current != null) return /*#__PURE__*/(0, _reactDom.createPortal)(children, portal.current);
117
+ return null;
118
+ }, [portal]);
119
+ return Object.assign([openPortal, closePortal, open.current, Portal, togglePortal, targetEl, portal], {
120
+ isOpen: open.current,
121
+ openPortal,
122
+ ref: targetEl,
123
+ closePortal,
124
+ togglePortal,
125
+ Portal,
126
+ portalRef: portal,
127
+ bind: {
128
+ // used if you want to spread all html attributes onto the target element
129
+ ref: targetEl
130
+ }
131
+ });
132
+ };
133
+ exports.usePortal = usePortal;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * This is a reference implementation of the usePortal hook from react-useportal: https://github.com/iamthesiz/react-useportal/blob/master/usePortal.test.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/react-useportal/blob/master/license.md
4
+ */
5
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This is a reference implementation of the useSSR hook from use-ssr: https://github.com/iamthesiz/use-ssr/blob/master/useSSR.ts
3
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/use-ssr/blob/master/license.md
4
+ */
5
+ interface UseSSRReturn {
6
+ isBrowser: boolean;
7
+ isServer: boolean;
8
+ device: Device;
9
+ canUseWorkers: boolean;
10
+ canUseEventListeners: boolean;
11
+ canUseViewport: boolean;
12
+ }
13
+ export declare enum Device {
14
+ Browser = "browser",
15
+ Server = "server"
16
+ }
17
+ export declare const weAreServer: () => void;
18
+ export declare const useSSR: () => UseSSRReturn;
19
+ export {};
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.weAreServer = exports.useSSR = exports.Device = void 0;
7
+ /**
8
+ * This is a reference implementation of the useSSR hook from use-ssr: https://github.com/iamthesiz/use-ssr/blob/master/useSSR.ts
9
+ * The license for the content in this file is goverened by the original project's license: https://github.com/iamthesiz/use-ssr/blob/master/license.md
10
+ */
11
+ let Device = exports.Device = /*#__PURE__*/function (Device) {
12
+ Device["Browser"] = "browser";
13
+ Device["Server"] = "server";
14
+ return Device;
15
+ }({});
16
+ const {
17
+ Browser,
18
+ Server
19
+ } = Device;
20
+ const canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement);
21
+ const device = canUseDOM ? Browser : Server;
22
+ const SSRObject = {
23
+ isBrowser: device === Browser,
24
+ isServer: device === Server,
25
+ device,
26
+ canUseWorkers: typeof Worker !== "undefined",
27
+ canUseEventListeners: device === Browser && !!window.addEventListener,
28
+ canUseViewport: device === Browser && !!window.screen
29
+ };
30
+ const assign = function () {
31
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
32
+ args[_key] = arguments[_key];
33
+ }
34
+ return args.reduce((acc, obj) => ({
35
+ ...acc,
36
+ ...obj
37
+ }), {});
38
+ };
39
+ const values = obj => Object.keys(obj).map(key => obj[key]);
40
+ const toArrayObject = () => assign((values(SSRObject), SSRObject));
41
+ let useSSRObject = toArrayObject();
42
+ const weAreServer = () => {
43
+ SSRObject.isServer = true;
44
+ useSSRObject = toArrayObject();
45
+ };
46
+ exports.weAreServer = weAreServer;
47
+ const useSSR = () => useSSRObject;
48
+ exports.useSSR = useSSR;
package/dist/index.d.ts CHANGED
@@ -138,3 +138,5 @@ export type { WindowFitment } from "./hooks";
138
138
  export { isNavigationAnchor, isNavigationButton } from "./utils";
139
139
  export type { ClassName, Headings, PropsWithSpread, SortDirection, SubComponentProps, TSFixMe, ValueOf, } from "./types";
140
140
  export { Theme } from "./enums";
141
+ export type { UsePortalOptions } from "./external";
142
+ export { usePortal } from "./external";
package/dist/index.js CHANGED
@@ -96,7 +96,8 @@ var _exportNames = {
96
96
  useWindowFitment: true,
97
97
  isNavigationAnchor: true,
98
98
  isNavigationButton: true,
99
- Theme: true
99
+ Theme: true,
100
+ usePortal: true
100
101
  };
101
102
  Object.defineProperty(exports, "Accordion", {
102
103
  enumerable: true,
@@ -638,6 +639,12 @@ Object.defineProperty(exports, "usePagination", {
638
639
  return _hooks.usePagination;
639
640
  }
640
641
  });
642
+ Object.defineProperty(exports, "usePortal", {
643
+ enumerable: true,
644
+ get: function () {
645
+ return _external.usePortal;
646
+ }
647
+ });
641
648
  Object.defineProperty(exports, "usePrevious", {
642
649
  enumerable: true,
643
650
  get: function () {
@@ -740,6 +747,7 @@ var _CustomSelect = _interopRequireDefault(require("./components/CustomSelect"))
740
747
  var _hooks = require("./hooks");
741
748
  var _utils = require("./utils");
742
749
  var _enums = require("./enums");
750
+ var _external = require("./external");
743
751
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
744
752
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
745
753
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": {
@@ -108,8 +108,7 @@
108
108
  "jest-environment-jsdom": "29.7.0",
109
109
  "lodash.isequal": "4.5.0",
110
110
  "prop-types": "15.8.1",
111
- "react-table": "7.8.0",
112
- "react-useportal": "1.0.19"
111
+ "react-table": "7.8.0"
113
112
  },
114
113
  "peerDependencies": {
115
114
  "@types/react": "^18.0.0 || ^19.0.0",