@trackunit/react-components 0.0.172 → 0.0.174

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.
@@ -9,6 +9,10 @@ export interface PageHeaderProps extends CommonProps {
9
9
  * The description to be displayed inside the tip.
10
10
  */
11
11
  description?: string | React.ReactNode;
12
+ /**
13
+ * The link to which user is redirected to if user clicks on the tip.
14
+ */
15
+ link?: string;
12
16
  /**
13
17
  * Additional actions to be rendered along with the header.
14
18
  */
@@ -40,4 +44,4 @@ export interface PageHeaderProps extends CommonProps {
40
44
  * @param {PageHeaderProps} props - The props for the PageHeader component
41
45
  * @returns {JSX.Element} PageHeader component
42
46
  */
43
- export declare const PageHeader: ({ className, dataTestId, action, showLoading, description, title, tag, backTo, children, }: PageHeaderProps) => JSX.Element;
47
+ export declare const PageHeader: ({ className, dataTestId, action, showLoading, description, link, title, tag, backTo, children, }: PageHeaderProps) => JSX.Element;
@@ -0,0 +1,71 @@
1
+ /// <reference types="react" />
2
+ export interface IPaginationProps {
3
+ /**
4
+ * A go to page handler
5
+ */
6
+ gotoPage: (index: number) => void;
7
+ /**
8
+ * A go to previous page handler
9
+ */
10
+ previousPage: () => void;
11
+ /**
12
+ * A a go to next page handler
13
+ */
14
+ nextPage: () => void;
15
+ /**
16
+ * A current page index
17
+ */
18
+ pageIndex?: number;
19
+ /**
20
+ * Amount of pages
21
+ */
22
+ pageCount?: number | null;
23
+ /**
24
+ * A flag to enable previous page button
25
+ */
26
+ canPreviousPage?: boolean;
27
+ /**
28
+ * A flag to enable next page button
29
+ */
30
+ canNextPage?: boolean;
31
+ /**
32
+ * A parent name
33
+ */
34
+ parentName: string;
35
+ /**
36
+ * A event name used for logging
37
+ */
38
+ eventName?: string;
39
+ /**
40
+ * A flag indicating loading state
41
+ */
42
+ loading?: boolean;
43
+ /**
44
+ * A custom class to override styles
45
+ */
46
+ className?: string;
47
+ /**
48
+ * A data test id
49
+ */
50
+ dataTestId?: string;
51
+ /**
52
+ * You can attach logging handler for page change
53
+ */
54
+ logPageChange?: (eventName: string, event: EventProperties) => void;
55
+ /**
56
+ * An helper to get translation for count.
57
+ */
58
+ getTranslatedCount: (count: number) => string;
59
+ /**
60
+ * An helper to get translation for page.
61
+ */
62
+ getTranslatedPage: () => string;
63
+ }
64
+ export declare type EventProperties = Record<string, string | number | boolean | null | undefined | (string | number)[]>;
65
+ /**
66
+ * Pagination Description. It could be used when you need navigation for your paging feature.
67
+ *
68
+ * @param {IPaginationProps} props - The props for the Pagination component
69
+ * @returns {JSX.Element} Pagination component
70
+ */
71
+ export declare const Pagination: ({ gotoPage, previousPage, nextPage, pageCount, pageIndex, canPreviousPage, canNextPage, loading, eventName, parentName, className, dataTestId, logPageChange, getTranslatedCount, getTranslatedPage, }: IPaginationProps) => JSX.Element;
@@ -22,7 +22,7 @@ export interface TipProps extends CommonProps {
22
22
  /**
23
23
  * The href attribute if the tip is rendered as an anchor tag.
24
24
  */
25
- link?: string | React.ReactNode;
25
+ link?: string;
26
26
  }
27
27
  /**
28
28
  * The Tip component is used to show helpful tips or contextual information to the users.
@@ -22,6 +22,7 @@ export * from "./Modal";
22
22
  export * from "./NumberField/NumberField";
23
23
  export * from "./NumberInput/NumberInput";
24
24
  export * from "./PageHeader";
25
+ export * from "./Pagination/Pagination";
25
26
  export * from "./Popover";
26
27
  export * from "./Prompt";
27
28
  export * from "./RadioGroup";
package/index.css CHANGED
@@ -20,6 +20,7 @@
20
20
  px-2.5
21
21
  h-min
22
22
  w-min
23
+ max-h-8
23
24
  focus:ring
24
25
  focus:ring-offset-1
25
26
  focus:outline-none
@@ -46,15 +47,20 @@
46
47
  }
47
48
 
48
49
  &--full-width {
49
- @apply w-full flex-auto;
50
+ @apply w-full
51
+ flex-auto;
50
52
  }
51
53
 
52
54
  &--small {
53
- @apply py-1 px-2;
55
+ @apply py-1
56
+ px-2
57
+ max-h-7;
54
58
  }
55
59
 
56
60
  &--large {
57
- @apply py-2 px-4;
61
+ @apply py-2
62
+ px-4
63
+ max-h-9;
58
64
  }
59
65
 
60
66
  &--primary {
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { tw, tws, twx } from '@trackunit/tailwind-styled-components';
3
3
  import * as React from 'react';
4
- import React__default, { useState, forwardRef, useRef, Fragment as Fragment$1, useCallback, createElement, useEffect, useContext, memo } from 'react';
4
+ import React__default, { useState, forwardRef, useRef, Fragment as Fragment$1, useCallback, createElement, memo, useEffect, useContext } from 'react';
5
5
  import { HeroIconsArray, heroIconLookup, TrackunitSolidIconsArray, TrackunitOutlineIconsArray, trackunitIconLookup } from '@trackunit/ui-icons';
6
6
  import EChart from 'echarts-for-react';
7
7
  import { v4 } from 'uuid';
@@ -351,7 +351,7 @@ const Icon$1 = ({
351
351
  className: iconSizeClassName
352
352
  })
353
353
  }, {
354
- children: jsx(Root$4, Object.assign({
354
+ children: jsx(Root$5, Object.assign({
355
355
  "$color": color,
356
356
  onClick: onClick,
357
357
  "data-testid": dataTestId,
@@ -370,7 +370,7 @@ const Icon$1 = ({
370
370
  const LoadingDot = tw.span`
371
371
  tu-loading-dot
372
372
  `;
373
- const Root$4 = tw.span`
373
+ const Root$5 = tw.span`
374
374
  tu-icon
375
375
  ${({
376
376
  $color
@@ -1465,7 +1465,7 @@ const Label$2 = ({
1465
1465
  disabled,
1466
1466
  isInvalid
1467
1467
  }) => {
1468
- return jsx(Root$3, Object.assign({
1468
+ return jsx(Root$4, Object.assign({
1469
1469
  id: id || "",
1470
1470
  htmlFor: htmlFor || "",
1471
1471
  className: className,
@@ -1476,7 +1476,7 @@ const Label$2 = ({
1476
1476
  children: children
1477
1477
  }));
1478
1478
  };
1479
- const Root$3 = tw.label`
1479
+ const Root$4 = tw.label`
1480
1480
  tu-label
1481
1481
  ${({
1482
1482
  $disabled
@@ -2229,7 +2229,7 @@ const Tip = ({
2229
2229
  return jsx(TipContainer, Object.assign({
2230
2230
  "data-testid": dataTestId,
2231
2231
  "$as": renderAs,
2232
- href: ifAnchorTag() ? link === null || link === void 0 ? void 0 : link.toString() : undefined,
2232
+ href: ifAnchorTag() ? link : undefined,
2233
2233
  target: ifAnchorTag() ? "_blank" : undefined,
2234
2234
  className: className
2235
2235
  }, {
@@ -2686,7 +2686,7 @@ const LegendItem = ({
2686
2686
  dataTestId
2687
2687
  }) => {
2688
2688
  const handleOnClick = onClick && !disabled ? () => onClick() : undefined;
2689
- return jsxs(Root$2, Object.assign({
2689
+ return jsxs(Root$3, Object.assign({
2690
2690
  "data-testid": dataTestId,
2691
2691
  "$disabled": disabled,
2692
2692
  className: className,
@@ -2703,7 +2703,7 @@ const LegendItem = ({
2703
2703
  })]
2704
2704
  }));
2705
2705
  };
2706
- const Root$2 = tw.div`
2706
+ const Root$3 = tw.div`
2707
2707
  inline-flex
2708
2708
  items-center
2709
2709
  transition-all
@@ -3163,6 +3163,7 @@ const PageHeader = ({
3163
3163
  action,
3164
3164
  showLoading,
3165
3165
  description,
3166
+ link,
3166
3167
  title,
3167
3168
  tag,
3168
3169
  backTo,
@@ -3189,14 +3190,14 @@ const PageHeader = ({
3189
3190
  }, {
3190
3191
  children: title
3191
3192
  })), description && jsx(StyledTip, Object.assign({
3192
- link: description,
3193
+ link: isValidHttpUrl(link) ? link : undefined,
3193
3194
  dataTestId: `${dataTestId}-tip`,
3194
3195
  iconProps: {
3195
3196
  name: "QuestionMarkCircleIcon"
3196
3197
  },
3197
- renderAs: isValidHttpUrl(description) ? "a" : "span"
3198
+ renderAs: link ? "a" : "span"
3198
3199
  }, {
3199
- children: isValidHttpUrl(description) ? "Learn more" : description
3200
+ children: description
3200
3201
  }))]
3201
3202
  }), tag && jsx(Tag, Object.assign({
3202
3203
  dataTestId: dataTestId
@@ -3212,21 +3213,21 @@ const PageHeader = ({
3212
3213
  }));
3213
3214
  };
3214
3215
 
3215
- const isValidHttpUrl = description => {
3216
- if (!description || typeof description !== "string") {
3216
+ function isValidHttpUrl(link) {
3217
+ if (!link) {
3217
3218
  return false;
3218
3219
  }
3219
3220
 
3220
3221
  let url;
3221
3222
 
3222
3223
  try {
3223
- url = new URL(description);
3224
+ url = new URL(link);
3224
3225
  } catch (_) {
3225
3226
  return false;
3226
3227
  }
3227
3228
 
3228
3229
  return url.protocol === "http:" || url.protocol === "https:";
3229
- };
3230
+ }
3230
3231
 
3231
3232
  const StyledPageHeader = tw.div`
3232
3233
  tu-page-header
@@ -3244,7 +3245,240 @@ const ChildContainer = tw.div`
3244
3245
  tu-page-header__child-container
3245
3246
  `;
3246
3247
  const StyledTip = twx(Tip)`
3247
- ${p => isValidHttpUrl(p.link) ? tws`tu-page-header__tip` : null}
3248
+ ${p => p.link ? tws`tu-page-header__tip` : null}
3249
+ `;
3250
+
3251
+ /**
3252
+ * Display placeholder lines before the data gets loaded to reduce load-time frustration.
3253
+ */
3254
+
3255
+ const SkeletonLines = memo(({
3256
+ margin = "10px 0",
3257
+ lines = 1,
3258
+ height,
3259
+ width = "100%",
3260
+ className,
3261
+ dataTestId
3262
+ }) => {
3263
+ const skeletonLines = [];
3264
+
3265
+ const getWidth = index => {
3266
+ if (width instanceof Array) {
3267
+ return width[index] || "100%";
3268
+ }
3269
+
3270
+ return width;
3271
+ };
3272
+
3273
+ const getHeight = index => {
3274
+ if (height instanceof Array) {
3275
+ return height[index] || "auto";
3276
+ }
3277
+
3278
+ return height;
3279
+ };
3280
+
3281
+ for (let i = 0; i < lines; i++) {
3282
+ skeletonLines.push(jsx(SkeletonLine, Object.assign({
3283
+ "data-testid": dataTestId,
3284
+ "data-type": "loading-skeleton-line",
3285
+ className: className,
3286
+ style: {
3287
+ height: getHeight(i),
3288
+ width: getWidth(i),
3289
+ margin: margin
3290
+ }
3291
+ }, {
3292
+ children: "\u00A0"
3293
+ }), i));
3294
+ } // eslint-disable-next-line react/jsx-no-useless-fragment
3295
+
3296
+
3297
+ return jsx(Fragment, {
3298
+ children: skeletonLines
3299
+ });
3300
+ });
3301
+ const SkeletonLine = tw.div`
3302
+ tu-skeleton-line
3303
+ `;
3304
+
3305
+ /**
3306
+ * Pagination Description. It could be used when you need navigation for your paging feature.
3307
+ *
3308
+ * @param {IPaginationProps} props - The props for the Pagination component
3309
+ * @returns {JSX.Element} Pagination component
3310
+ */
3311
+
3312
+ const Pagination = ({
3313
+ gotoPage,
3314
+ previousPage,
3315
+ nextPage,
3316
+ pageCount,
3317
+ pageIndex,
3318
+ canPreviousPage,
3319
+ canNextPage,
3320
+ loading,
3321
+ eventName,
3322
+ parentName,
3323
+ className,
3324
+ dataTestId,
3325
+ logPageChange,
3326
+ getTranslatedCount,
3327
+ getTranslatedPage
3328
+ }) => {
3329
+ const [page, setPage] = React.useState(pageIndex);
3330
+ const [pageInputValue, setPageInputValue] = React.useState(String(pageIndex !== undefined ? pageIndex + 1 : 1));
3331
+
3332
+ if (!loading && pageCount === undefined) {
3333
+ throw Error("Pagination should be provided with a pageCount");
3334
+ }
3335
+
3336
+ React.useEffect(() => {
3337
+ if (pageIndex !== undefined && (isNaN(pageIndex) || pageIndex < 0)) {
3338
+ gotoPage(0);
3339
+ } else if (pageIndex !== undefined && pageCount !== null && pageCount !== undefined && pageIndex > pageCount - 1) {
3340
+ gotoPage(pageCount - 1);
3341
+ } else {
3342
+ setPage(pageIndex);
3343
+ pageIndex !== undefined && setPageInputValue(String(pageIndex + 1));
3344
+ }
3345
+ }, [pageIndex, gotoPage, setPage, setPageInputValue, pageCount]);
3346
+ const parsePageString = React.useCallback(pageString => {
3347
+ let val = parseInt(pageString);
3348
+ val = isNaN(val) ? 0 : val;
3349
+ val = val < 1 ? 0 : val - 1;
3350
+
3351
+ if (pageCount !== undefined && pageCount !== null) {
3352
+ val = val > pageCount ? pageCount - 1 : val;
3353
+ }
3354
+
3355
+ return val;
3356
+ }, [pageCount]);
3357
+ const handlePageChange = React.useCallback(action => {
3358
+ const from = page;
3359
+ let to = from;
3360
+ let description = "";
3361
+
3362
+ switch (action) {
3363
+ case "prev":
3364
+ to = from !== undefined ? from - 1 : 0;
3365
+ previousPage();
3366
+ description = "Previous Page";
3367
+ break;
3368
+
3369
+ case "next":
3370
+ to = from !== undefined ? from + 1 : 0;
3371
+ nextPage();
3372
+ description = "Next Page";
3373
+ break;
3374
+
3375
+ case "manual":
3376
+ to = parsePageString(pageInputValue);
3377
+ gotoPage(to);
3378
+ description = "Manual Page Change";
3379
+ break;
3380
+ }
3381
+
3382
+ if (to) {
3383
+ setPage(to);
3384
+ setPageInputValue(String(to + 1));
3385
+ }
3386
+
3387
+ eventName && logPageChange && logPageChange(eventName, {
3388
+ parentName,
3389
+ type: "Pagination",
3390
+ from: from !== undefined ? from : "",
3391
+ to: to !== undefined ? to : "",
3392
+ action: description
3393
+ });
3394
+ }, [page, logPageChange, eventName, parentName, previousPage, nextPage, parsePageString, pageInputValue, gotoPage]);
3395
+
3396
+ const handleOnBlur = () => {
3397
+ handlePageChange("manual");
3398
+ };
3399
+
3400
+ const handleOnFocus = e => e.target.select();
3401
+
3402
+ const handleOnChange = React.useCallback(e => {
3403
+ setPageInputValue(e.target.value);
3404
+ }, []);
3405
+ const handleOnKeyPress = React.useCallback(e => {
3406
+ if (e.key !== "Enter") {
3407
+ return;
3408
+ }
3409
+
3410
+ handlePageChange("manual");
3411
+ }, [handlePageChange]);
3412
+
3413
+ if (loading) {
3414
+ return jsx(Root$2, Object.assign({
3415
+ className: className
3416
+ }, {
3417
+ children: jsx(SkeletonLines, {
3418
+ height: 16,
3419
+ width: 150
3420
+ })
3421
+ }));
3422
+ }
3423
+
3424
+ return jsxs(Root$2, Object.assign({
3425
+ "data-testid": dataTestId,
3426
+ className: className
3427
+ }, {
3428
+ children: [jsx(IconButton, {
3429
+ onClick: () => handlePageChange("prev"),
3430
+ disabled: page !== undefined && page <= 0,
3431
+ "data-testid": "prev-page",
3432
+ icon: jsx(Icon$1, {
3433
+ name: "ChevronLeftIcon",
3434
+ size: "small"
3435
+ }),
3436
+ size: "small",
3437
+ color: "secondary",
3438
+ variant: "transparent"
3439
+ }), jsx(Text$1, {
3440
+ children: getTranslatedPage()
3441
+ }), jsx(PageInput, {
3442
+ type: "number",
3443
+ min: 1,
3444
+ // max={amountOfPages}
3445
+ value: pageInputValue,
3446
+ onFocus: handleOnFocus,
3447
+ onBlur: handleOnBlur,
3448
+ onChange: handleOnChange,
3449
+ onKeyPress: handleOnKeyPress,
3450
+ "data-testid": "page-input"
3451
+ }), jsx(TextStyled, Object.assign({
3452
+ dataTestId: "page-count"
3453
+ }, {
3454
+ children: pageCount !== null && pageCount !== undefined && getTranslatedCount(pageCount)
3455
+ })), jsx(IconButton, {
3456
+ onClick: () => handlePageChange("next"),
3457
+ disabled: page !== undefined && pageCount !== undefined && pageCount !== null && page >= pageCount - 1,
3458
+ "data-testid": "next-page",
3459
+ icon: jsx(Icon$1, {
3460
+ name: "ChevronRightIcon",
3461
+ size: "small"
3462
+ }),
3463
+ size: "small",
3464
+ color: "secondary",
3465
+ variant: "transparent"
3466
+ })]
3467
+ }));
3468
+ };
3469
+ const Root$2 = tw.div`
3470
+ tu-pagination
3471
+ `;
3472
+ const TextStyled = twx(Text$1)`
3473
+ tu-pagination__text
3474
+ `;
3475
+ const PageInput = tw.input`
3476
+ tu-pagination__page-input
3477
+ [&::-webkit-inner-spin-button]:[-webkit-appearance:none]
3478
+ [&::-webkit-outer-spin-button]:[-webkit-appearance:none]
3479
+ [&::-webkit-inner-spin-button]:m-0
3480
+ [&::-webkit-outer-spin-button]:m-0
3481
+ [-moz-appearance:textfield]
3248
3482
  `;
3249
3483
 
3250
3484
  function useConfirmExit(confirmExit, when = true) {
@@ -4416,60 +4650,6 @@ const SelectContainer = tw.div`
4416
4650
  `;
4417
4651
  Select.displayName = "Select";
4418
4652
 
4419
- /**
4420
- * Display placeholder lines before the data gets loaded to reduce load-time frustration.
4421
- */
4422
-
4423
- const SkeletonLines = memo(({
4424
- margin = "10px 0",
4425
- lines = 1,
4426
- height,
4427
- width = "100%",
4428
- className,
4429
- dataTestId
4430
- }) => {
4431
- const skeletonLines = [];
4432
-
4433
- const getWidth = index => {
4434
- if (width instanceof Array) {
4435
- return width[index] || "100%";
4436
- }
4437
-
4438
- return width;
4439
- };
4440
-
4441
- const getHeight = index => {
4442
- if (height instanceof Array) {
4443
- return height[index] || "auto";
4444
- }
4445
-
4446
- return height;
4447
- };
4448
-
4449
- for (let i = 0; i < lines; i++) {
4450
- skeletonLines.push(jsx(SkeletonLine, Object.assign({
4451
- "data-testid": dataTestId,
4452
- "data-type": "loading-skeleton-line",
4453
- className: className,
4454
- style: {
4455
- height: getHeight(i),
4456
- width: getWidth(i),
4457
- margin: margin
4458
- }
4459
- }, {
4460
- children: "\u00A0"
4461
- }), i));
4462
- } // eslint-disable-next-line react/jsx-no-useless-fragment
4463
-
4464
-
4465
- return jsx(Fragment, {
4466
- children: skeletonLines
4467
- });
4468
- });
4469
- const SkeletonLine = tw.div`
4470
- tu-skeleton-line
4471
- `;
4472
-
4473
4653
  /**
4474
4654
  * The TextArea is a base component, and should not be used very often.
4475
4655
  * For most cases the TextAreaField is the correct component.
@@ -5370,4 +5550,4 @@ const Layout = tw.div`
5370
5550
  tu-layout-TwoColumnLayout
5371
5551
  `;
5372
5552
 
5373
- export { ActionButton, Alert, AnimatedPopover, Badge, BaseInput, Button, Card, CardBody, CardFooter, CardHeader, Chart, Checkbox, Collapse, CopyableText, CreatableSelect, DateField, DateInput, DayPicker, DayPickerPopover, DayRangePicker, DensityContainer, FilterListMapLayout, FormGroup, FullWidthRepeatingLayout, GridArea, Heading$1 as Heading, Icon$1 as Icon, IconButton, Indicator$1 as Indicator, IntrinsicallyResponsiveGrid, Label$2 as Label, LegendItem, MenuItem, MenuList, MinTopFullContentLayout, Modal, ModalBackdrop, MultiSelectMenuItem, NumberField, NumberInput, PageHeader, PopoverLifecycle, PopoverList, PopoverListItem, PopoverTitle, Prompt, ROLE_CARD, RadioGroup, RadioItem, SectionHeader, Select, SelectPrefix, SingleSelectMenuItem, SkeletonLines, Spacer, Spinner, StarButton, Tag, Text$1 as Text, TextArea, TextAreaField, TextField, TextInput, Timeline, TimelineElement, Tip, TitleNavContentLayout, Toggle, Tooltip, TwoColumnLayout, ValueBar, docs, getOrderedOptions, getValueBarColorByValue, isMultiValue, useContainerProps, useCustomComponents, useDebounce, useLocalStorage, useModal, usePopover, usePrompt, useTimeout };
5553
+ export { ActionButton, Alert, AnimatedPopover, Badge, BaseInput, Button, Card, CardBody, CardFooter, CardHeader, Chart, Checkbox, Collapse, CopyableText, CreatableSelect, DateField, DateInput, DayPicker, DayPickerPopover, DayRangePicker, DensityContainer, FilterListMapLayout, FormGroup, FullWidthRepeatingLayout, GridArea, Heading$1 as Heading, Icon$1 as Icon, IconButton, Indicator$1 as Indicator, IntrinsicallyResponsiveGrid, Label$2 as Label, LegendItem, MenuItem, MenuList, MinTopFullContentLayout, Modal, ModalBackdrop, MultiSelectMenuItem, NumberField, NumberInput, PageHeader, Pagination, PopoverLifecycle, PopoverList, PopoverListItem, PopoverTitle, Prompt, ROLE_CARD, RadioGroup, RadioItem, SectionHeader, Select, SelectPrefix, SingleSelectMenuItem, SkeletonLines, Spacer, Spinner, StarButton, Tag, Text$1 as Text, TextArea, TextAreaField, TextField, TextInput, Timeline, TimelineElement, Tip, TitleNavContentLayout, Toggle, Tooltip, TwoColumnLayout, ValueBar, docs, getOrderedOptions, getValueBarColorByValue, isMultiValue, useContainerProps, useCustomComponents, useDebounce, useLocalStorage, useModal, usePopover, usePrompt, useTimeout };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "0.0.172",
3
+ "version": "0.0.174",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "dependencies": {
@@ -15,8 +15,8 @@
15
15
  "date-fns": "^2.29.3",
16
16
  "react-day-picker": "^8.3.0",
17
17
  "@trackunit/ui-icons": "0.0.48",
18
- "react-useportal": "1.0.16",
19
- "@react-spring/web": "9.5.2",
18
+ "react-useportal": "1.0.17",
19
+ "@react-spring/web": "9.5.4",
20
20
  "react-router-dom": "6.4.1",
21
21
  "@popperjs/core": "^2.11.6",
22
22
  "react-helmet": "^6.1.0",