@canonical/react-components 2.0.0 → 2.2.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.
Files changed (61) hide show
  1. package/dist/components/Card/Card.stories.d.ts +2 -2
  2. package/dist/components/CodeSnippet/CodeSnippet.stories.js +3 -2
  3. package/dist/components/Col/Col.stories.d.ts +2 -1
  4. package/dist/components/ConfirmationButton/ConfirmationButton.d.ts +7 -2
  5. package/dist/components/ConfirmationButton/ConfirmationButton.js +11 -3
  6. package/dist/components/ConfirmationButton/ConfirmationButton.stories.d.ts +1 -0
  7. package/dist/components/ConfirmationButton/ConfirmationButton.stories.js +23 -1
  8. package/dist/components/ContextualMenu/ContextualMenu.js +5 -5
  9. package/dist/components/ContextualMenu/ContextualMenuDropdown/ContextualMenuDropdown.d.ts +1 -1
  10. package/dist/components/ContextualMenu/ContextualMenuDropdown/ContextualMenuDropdown.js +2 -2
  11. package/dist/components/MainTable/MainTable.d.ts +3 -1
  12. package/dist/components/ModularTable/ModularTable.stories.js +2 -2
  13. package/dist/components/Navigation/Navigation.d.ts +1 -1
  14. package/dist/components/Notification/Notification.d.ts +1 -1
  15. package/dist/components/Notification/Notification.js +3 -1
  16. package/dist/components/NotificationProvider/NotificationProvider.js +2 -2
  17. package/dist/components/SearchAndFilter/SearchAndFilter.js +1 -4
  18. package/dist/components/SearchAndFilter/utils.d.ts +3 -2
  19. package/dist/components/SideNavigation/SideNavigation.js +1 -1
  20. package/dist/components/Tooltip/Tooltip.js +3 -3
  21. package/dist/esm/components/Card/Card.stories.d.ts +2 -2
  22. package/dist/esm/components/CodeSnippet/CodeSnippet.stories.js +3 -2
  23. package/dist/esm/components/Col/Col.stories.d.ts +2 -1
  24. package/dist/esm/components/ConfirmationButton/ConfirmationButton.d.ts +7 -2
  25. package/dist/esm/components/ConfirmationButton/ConfirmationButton.js +12 -4
  26. package/dist/esm/components/ConfirmationButton/ConfirmationButton.stories.d.ts +1 -0
  27. package/dist/esm/components/ConfirmationButton/ConfirmationButton.stories.js +22 -0
  28. package/dist/esm/components/ContextualMenu/ContextualMenu.js +4 -4
  29. package/dist/esm/components/ContextualMenu/ContextualMenuDropdown/ContextualMenuDropdown.d.ts +1 -1
  30. package/dist/esm/components/ContextualMenu/ContextualMenuDropdown/ContextualMenuDropdown.js +2 -2
  31. package/dist/esm/components/MainTable/MainTable.d.ts +3 -1
  32. package/dist/esm/components/ModularTable/ModularTable.stories.js +60 -12
  33. package/dist/esm/components/Navigation/Navigation.d.ts +1 -1
  34. package/dist/esm/components/Notification/Notification.d.ts +1 -1
  35. package/dist/esm/components/Notification/Notification.js +4 -2
  36. package/dist/esm/components/NotificationProvider/NotificationProvider.js +2 -2
  37. package/dist/esm/components/SearchAndFilter/SearchAndFilter.js +1 -4
  38. package/dist/esm/components/SearchAndFilter/utils.d.ts +3 -2
  39. package/dist/esm/components/SideNavigation/SideNavigation.js +1 -1
  40. package/dist/esm/components/Tooltip/Tooltip.js +2 -2
  41. package/dist/esm/external/index.d.ts +2 -0
  42. package/dist/esm/external/index.js +1 -0
  43. package/dist/esm/external/usePortal.d.ts +39 -0
  44. package/dist/esm/external/usePortal.js +125 -0
  45. package/dist/esm/external/usePortal.test.d.ts +5 -0
  46. package/dist/esm/external/useSSR.d.ts +19 -0
  47. package/dist/esm/external/useSSR.js +43 -0
  48. package/dist/esm/hooks/useWindowFitment.js +2 -2
  49. package/dist/esm/index.d.ts +2 -0
  50. package/dist/esm/index.js +2 -1
  51. package/dist/external/index.d.ts +2 -0
  52. package/dist/external/index.js +12 -0
  53. package/dist/external/usePortal.d.ts +39 -0
  54. package/dist/external/usePortal.js +133 -0
  55. package/dist/external/usePortal.test.d.ts +5 -0
  56. package/dist/external/useSSR.d.ts +19 -0
  57. package/dist/external/useSSR.js +48 -0
  58. package/dist/hooks/useWindowFitment.js +2 -2
  59. package/dist/index.d.ts +2 -0
  60. package/dist/index.js +9 -1
  61. package/package.json +37 -39
@@ -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;
@@ -29,8 +29,8 @@ const useWindowFitment = function (targetNode, referenceNode, callback) {
29
29
  referenceCoordinates = {
30
30
  // The mouse is a single point so use 0 for the height and width.
31
31
  height: 0,
32
- left: evt.x || 0,
33
- top: evt.y || 0,
32
+ left: ("x" in evt && typeof evt.x === "number" ? evt.x : null) || 0,
33
+ top: ("y" in evt && typeof evt.y === "number" ? evt.y : null) || 0,
34
34
  width: 0
35
35
  };
36
36
  }
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.2.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": {
@@ -26,90 +26,88 @@
26
26
  "homepage": "https://canonical.github.io/react-components",
27
27
  "devDependencies": {
28
28
  "@babel/cli": "7.26.4",
29
- "@babel/eslint-parser": "7.26.5",
30
- "@babel/plugin-proposal-class-properties": "7.18.6",
31
- "@babel/preset-env": "7.26.0",
29
+ "@babel/eslint-parser": "7.26.8",
30
+ "@babel/plugin-transform-class-properties": "7.25.9",
31
+ "@babel/preset-env": "7.26.9",
32
32
  "@babel/preset-react": "7.26.3",
33
33
  "@babel/preset-typescript": "7.26.0",
34
- "@eslint/compat": "1.2.5",
34
+ "@eslint/compat": "1.2.6",
35
35
  "@eslint/eslintrc": "3.2.0",
36
- "@eslint/js": "9.18.0",
37
- "@percy/cli": "1.30.6",
36
+ "@eslint/js": "9.20.0",
37
+ "@percy/cli": "1.30.7",
38
38
  "@percy/storybook": "6.0.3",
39
39
  "@semantic-release/changelog": "6.0.3",
40
40
  "@semantic-release/git": "10.0.1",
41
- "@storybook/addon-a11y": "8.4.7",
42
- "@storybook/addon-essentials": "8.4.7",
43
- "@storybook/addon-interactions": "8.4.7",
44
- "@storybook/addon-links": "8.5.0",
45
- "@storybook/addon-onboarding": "8.5.0",
41
+ "@storybook/addon-a11y": "8.5.6",
42
+ "@storybook/addon-essentials": "8.5.6",
43
+ "@storybook/addon-interactions": "8.5.6",
44
+ "@storybook/addon-links": "8.5.6",
45
+ "@storybook/addon-onboarding": "8.5.6",
46
46
  "@storybook/addon-webpack5-compiler-babel": "3.0.5",
47
- "@storybook/blocks": "8.4.7",
48
- "@storybook/react": "8.4.7",
49
- "@storybook/react-webpack5": "8.5.0",
50
- "@testing-library/cypress": "10.0.2",
47
+ "@storybook/blocks": "8.5.6",
48
+ "@storybook/react": "8.5.6",
49
+ "@storybook/react-webpack5": "8.5.6",
50
+ "@testing-library/cypress": "10.0.3",
51
51
  "@testing-library/dom": "10.4.0",
52
52
  "@testing-library/jest-dom": "6.6.3",
53
- "@testing-library/react": "16.1.0",
54
- "@testing-library/user-event": "14.5.2",
55
- "@types/lodash.isequal": "4",
53
+ "@testing-library/react": "16.2.0",
54
+ "@testing-library/user-event": "14.6.1",
56
55
  "babel-jest": "29.7.0",
57
56
  "babel-loader": "9.2.1",
58
57
  "babel-plugin-module-resolver": "5.0.2",
59
58
  "babel-plugin-typescript-to-proptypes": "2.1.0",
60
59
  "concurrently": "9.1.2",
61
60
  "css-loader": "7.1.2",
62
- "cypress": "14.0.1",
61
+ "cypress": "14.0.3",
63
62
  "deepmerge": "4.3.1",
64
- "eslint": "9.18.0",
63
+ "eslint": "9.20.1",
65
64
  "eslint-config-prettier": "10.0.1",
66
65
  "eslint-plugin-cypress": "4.1.0",
67
66
  "eslint-plugin-flowtype": "8.0.3",
68
67
  "eslint-plugin-import": "2.31.0",
69
68
  "eslint-plugin-jsx-a11y": "6.10.2",
70
- "eslint-plugin-prettier": "5.2.2",
69
+ "eslint-plugin-prettier": "5.2.3",
71
70
  "eslint-plugin-react": "7.37.4",
72
71
  "eslint-plugin-react-hooks": "5.1.0",
73
- "eslint-plugin-storybook": "0.11.2",
72
+ "eslint-plugin-storybook": "0.11.3",
74
73
  "eslint-plugin-testing-library": "7.1.1",
75
74
  "formik": "2.4.6",
76
75
  "jest": "29.7.0",
77
76
  "npm-package-json-lint": "8.0.0",
78
- "prettier": "3.4.2",
77
+ "prettier": "3.5.1",
79
78
  "react": "19.0.0",
80
79
  "react-docgen-typescript-loader": "3.7.2",
81
80
  "react-dom": "19.0.0",
82
- "sass": "1.83.4",
83
- "sass-loader": "16.0.4",
84
- "semantic-release": "24.2.1",
85
- "storybook": "8.4.7",
81
+ "sass": "1.85.0",
82
+ "sass-loader": "16.0.5",
83
+ "semantic-release": "24.2.3",
84
+ "storybook": "8.5.6",
86
85
  "strip-ansi": "7.1.0",
87
86
  "style-loader": "4.0.0",
88
- "stylelint": "16.13.2",
87
+ "stylelint": "16.14.1",
89
88
  "stylelint-config-prettier": "9.0.5",
90
89
  "stylelint-config-recommended-scss": "14.1.0",
91
90
  "stylelint-order": "6.0.4",
92
- "stylelint-prettier": "5.0.2",
91
+ "stylelint-prettier": "5.0.3",
93
92
  "ts-jest": "29.2.5",
94
93
  "tsc-alias": "1.8.10",
95
94
  "typescript": "5.7.3",
96
- "typescript-eslint": "8.20.0",
97
- "vanilla-framework": "4.20.2",
95
+ "typescript-eslint": "8.24.1",
96
+ "vanilla-framework": "4.21.0",
98
97
  "wait-on": "8.0.2",
99
- "webpack": "5.97.1"
98
+ "webpack": "5.98.0"
100
99
  },
101
100
  "dependencies": {
102
101
  "@types/jest": "29.5.14",
103
- "@types/node": "20.17.13",
104
- "@types/react": "19.0.8",
105
- "@types/react-dom": "19.0.3",
102
+ "@types/node": "20.17.19",
103
+ "@types/react": "19.0.10",
104
+ "@types/react-dom": "19.0.4",
106
105
  "@types/react-table": "7.7.20",
107
106
  "classnames": "2.5.1",
107
+ "fast-deep-equal": "3.1.3",
108
108
  "jest-environment-jsdom": "29.7.0",
109
- "lodash.isequal": "4.5.0",
110
109
  "prop-types": "15.8.1",
111
- "react-table": "7.8.0",
112
- "react-useportal": "1.0.19"
110
+ "react-table": "7.8.0"
113
111
  },
114
112
  "peerDependencies": {
115
113
  "@types/react": "^18.0.0 || ^19.0.0",