@graphcommerce/next-ui 5.2.0-canary.9 → 6.0.0-canary.20

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.
@@ -15,11 +15,11 @@ export function BlogAuthor(props: BlogAuthorProps) {
15
15
 
16
16
  return (
17
17
  <Box
18
+ maxWidth='md'
18
19
  sx={[
19
20
  {
20
21
  display: 'flex',
21
22
  justifyContent: 'left',
22
- maxWidth: 800,
23
23
  margin: `0 auto`,
24
24
  marginBottom: (theme) => theme.spacings.md,
25
25
  },
@@ -1,7 +1,25 @@
1
- import { styled } from '@mui/material'
1
+ import { Box, SxProps, Theme } from '@mui/material'
2
2
 
3
- export const BlogContent = styled('div')(({ theme }) => ({
4
- maxWidth: 800,
5
- margin: '0 auto',
6
- marginBottom: theme.spacings.sm,
7
- }))
3
+ type BlogContentProps = {
4
+ children: React.ReactNode
5
+ sx?: SxProps<Theme>
6
+ }
7
+
8
+ export function BlogContent(props: BlogContentProps) {
9
+ const { children, sx = [] } = props
10
+
11
+ return (
12
+ <Box
13
+ maxWidth='md'
14
+ sx={[
15
+ (theme) => ({
16
+ margin: '0 auto',
17
+ marginBottom: theme.spacings.sm,
18
+ }),
19
+ ...(Array.isArray(sx) ? sx : [sx]),
20
+ ]}
21
+ >
22
+ {children}
23
+ </Box>
24
+ )
25
+ }
@@ -18,12 +18,13 @@ export function BlogHeader(props: BlogHeaderProps) {
18
18
  return (
19
19
  <Box
20
20
  className={classes.header}
21
+ maxWidth='md'
21
22
  sx={[
22
23
  (theme) => ({
23
- maxWidth: 800,
24
24
  margin: `0 auto`,
25
25
  marginBottom: theme.spacings.md,
26
26
  '& img': {
27
+ objectFit: 'cover',
27
28
  ...breakpointVal(
28
29
  'borderRadius',
29
30
  theme.shape.borderRadius * 2,
@@ -1,9 +1,26 @@
1
- import { styled } from '@mui/material'
1
+ import { SxProps, Theme } from '@mui/material'
2
2
  import { Row } from '../../Row/Row'
3
- import { responsiveVal } from '../../Styles/responsiveVal'
4
3
 
5
- export const BlogItemGrid = styled(Row, { name: 'BlogList' })(({ theme }) => ({
6
- display: 'grid',
7
- gap: theme.spacings.md,
8
- gridTemplateColumns: `repeat(auto-fill, minmax(${responsiveVal(150, 285)}, 1fr))`,
9
- }))
4
+ type BlogItemGridProps = {
5
+ children: React.ReactNode
6
+ sx?: SxProps<Theme>
7
+ }
8
+
9
+ export function BlogItemGrid(props: BlogItemGridProps) {
10
+ const { children, sx = [] } = props
11
+
12
+ return (
13
+ <Row
14
+ sx={[
15
+ (theme) => ({
16
+ display: 'grid',
17
+ gap: theme.spacings.md,
18
+ gridTemplateColumns: { xs: `repeat(2, 1fr)`, md: `repeat(3, 1fr)`, lg: `repeat(4, 1fr)` },
19
+ }),
20
+ ...(Array.isArray(sx) ? sx : [sx]),
21
+ ]}
22
+ >
23
+ {children}
24
+ </Row>
25
+ )
26
+ }
@@ -28,7 +28,7 @@ export function BlogListItem(props: BlogListItemProps) {
28
28
  sx={[
29
29
  (theme) => ({
30
30
  display: 'grid',
31
- gridTemplateRows: `${responsiveVal(140, 220)} auto auto`,
31
+ gridTemplateRows: `auto auto auto`,
32
32
  alignContent: 'start',
33
33
  color: theme.palette.text.primary,
34
34
  gap: theme.spacings.xxs,
@@ -41,26 +41,16 @@ export function BlogListItem(props: BlogListItemProps) {
41
41
  <Box
42
42
  className={classes.asset}
43
43
  sx={(theme) => ({
44
- display: 'grid',
45
- alignContent: 'center',
46
- overflow: 'hidden',
47
- height: '100%',
48
- width: '100%',
49
- ...breakpointVal(
50
- 'borderRadius',
51
- theme.shape.borderRadius * 2,
52
- theme.shape.borderRadius * 3,
53
- theme.breakpoints.values,
54
- ),
55
44
  '& img': {
56
- height: '100% !important',
45
+ aspectRatio: '3/2',
57
46
  objectFit: 'cover',
47
+ ...breakpointVal(
48
+ 'borderRadius',
49
+ theme.shape.borderRadius * 2,
50
+ theme.shape.borderRadius * 3,
51
+ theme.breakpoints.values,
52
+ ),
58
53
  },
59
- '& p': {
60
- alignSelf: 'center',
61
- justifySelf: 'center',
62
- },
63
- background: theme.palette.background.paper,
64
54
  })}
65
55
  >
66
56
  {asset}
@@ -1,7 +1,7 @@
1
1
  import { styled } from '@mui/material'
2
2
 
3
3
  export const BlogTags = styled('div')(({ theme }) => ({
4
- maxWidth: 800,
4
+ maxWidth: theme.breakpoints.values.md,
5
5
  margin: `0 auto`,
6
6
  marginBottom: theme.spacings.sm,
7
7
  }))
@@ -8,7 +8,12 @@ export function BlogTitle(props: BlogTitleProps) {
8
8
  const { sx = [], children } = props
9
9
 
10
10
  return (
11
- <Box sx={[{ maxWidth: 800, margin: '0 auto' }, ...(Array.isArray(sx) ? sx : [sx])]}>
11
+ <Box
12
+ sx={[
13
+ (theme) => ({ maxWidth: theme.breakpoints.values.md, margin: '0 auto' }),
14
+ ...(Array.isArray(sx) ? sx : [sx]),
15
+ ]}
16
+ >
12
17
  <LayoutTitle variant='h1'>{children}</LayoutTitle>
13
18
  </Box>
14
19
  )
package/Button/Button.tsx CHANGED
@@ -3,7 +3,7 @@ import { LoadingButton as Button, LoadingButtonProps, LoadingButtonTypeMap } fro
3
3
 
4
4
  export type ButtonProps<
5
5
  D extends React.ElementType = LoadingButtonTypeMap['defaultComponent'],
6
- P = {},
6
+ P = object,
7
7
  > = LoadingButtonProps<D, P>
8
8
 
9
9
  export { Button }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Change Log
2
2
 
3
+ ## 6.0.0-canary.20
4
+
5
+ ## 5.2.0-canary.19
6
+
7
+ ## 5.2.0-canary.18
8
+
9
+ ## 5.2.0-canary.17
10
+
11
+ ## 5.2.0-canary.16
12
+
13
+ ## 5.2.0-canary.15
14
+
15
+ ### Minor Changes
16
+
17
+ - [#1798](https://github.com/graphcommerce-org/graphcommerce/pull/1798) [`3cee17a51`](https://github.com/graphcommerce-org/graphcommerce/commit/3cee17a51ff961f4363d95c9decb8c7d1f9ca319) - Added filterByTypename function to filter types based on \_\_typename ([@mikekeehnen](https://github.com/mikekeehnen))
18
+
19
+ ## 5.2.0-canary.14
20
+
21
+ ## 5.2.0-canary.13
22
+
23
+ ### Minor Changes
24
+
25
+ - [#1795](https://github.com/graphcommerce-org/graphcommerce/pull/1795) [`236d698b2`](https://github.com/graphcommerce-org/graphcommerce/commit/236d698b2aac55598fc45a6a58574a538f23e160) - Navigation link fix, homepage and category style fixes ([@ErwinOtten](https://github.com/ErwinOtten))
26
+
27
+ ## 5.2.0-canary.12
28
+
29
+ ## 5.2.0-canary.11
30
+
31
+ ### Patch Changes
32
+
33
+ - [#1794](https://github.com/graphcommerce-org/graphcommerce/pull/1794) [`29e15cf63`](https://github.com/graphcommerce-org/graphcommerce/commit/29e15cf63251cf98cf42325322fcf09fb7a6c0b7) - Fix scroll issue with bottom overlay on Android ([@bramvanderholst](https://github.com/bramvanderholst))
34
+
35
+ ## 5.2.0-canary.10
36
+
37
+ ### Minor Changes
38
+
39
+ - [#1793](https://github.com/graphcommerce-org/graphcommerce/pull/1793) [`5562fa69b`](https://github.com/graphcommerce-org/graphcommerce/commit/5562fa69b1bc260f68555dcfaf30153eda489bed) - Add newsletter subscribe form ([@ErwinOtten](https://github.com/ErwinOtten))
40
+
3
41
  ## 5.2.0-canary.9
4
42
 
5
43
  ### Patch Changes
@@ -288,8 +288,8 @@ export function SidebarGallery(props: SidebarGalleryProps) {
288
288
  <Box
289
289
  className={classes.bottomCenter}
290
290
  sx={{
291
- display: 'grid',
292
- gridAutoFlow: 'column',
291
+ display: 'flex',
292
+ px: theme.page.horizontal,
293
293
  gap: theme.spacings.xxs,
294
294
  position: 'absolute',
295
295
  bottom: theme.spacings.xxs,
@@ -13,6 +13,7 @@ const ESCAPE_REGEX = new RegExp(`[${Object.keys(ESCAPE_ENTITIES).join('')}]`, 'g
13
13
  const ESCAPE_REPLACER = (t: string): string => ESCAPE_ENTITIES[t as keyof typeof ESCAPE_ENTITIES]
14
14
 
15
15
  // Utility: Assert never
16
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
17
  function isNever(_: never): void {}
17
18
 
18
19
  /**
@@ -7,7 +7,7 @@ export type LayoutProviderProps = {
7
7
  } & LayoutContext
8
8
 
9
9
  export function LayoutProvider(props: LayoutProviderProps) {
10
- const { children, scroll: scroll } = props
10
+ const { children, scroll } = props
11
11
 
12
12
  return (
13
13
  <layoutContext.Provider value={useMemo(() => ({ scroll }), [scroll])}>
@@ -1,9 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import { useMotionValueValue } from '@graphcommerce/framer-utils'
3
- import { alpha, Box, ListItemButton, styled, useEventCallback, useTheme } from '@mui/material'
3
+ import { alpha, Box, ListItemButton, styled, useEventCallback } from '@mui/material'
4
4
  import React from 'react'
5
5
  import { IconSvg } from '../../IconSvg'
6
6
  import { extendableComponent } from '../../Styles/extendableComponent'
7
+ import { NextLink } from '../../Theme'
7
8
  import { useMatchMedia } from '../../hooks'
8
9
  import { iconChevronRight } from '../../icons'
9
10
  import {
@@ -14,8 +15,6 @@ import {
14
15
  useNavigation,
15
16
  } from '../hooks/useNavigation'
16
17
  import type { NavigationList } from './NavigationList'
17
- import { NextLink } from '../../Theme'
18
- import { useRouter } from 'next/router'
19
18
 
20
19
  type OwnerState = {
21
20
  first?: boolean
@@ -49,8 +48,6 @@ const NavigationLI = styled('li')({ display: 'contents' })
49
48
  export const NavigationItem = React.memo<NavigationItemProps>((props) => {
50
49
  const { id, parentPath, idx, first, last, NavigationList, mouseEvent } = props
51
50
  const { selection, hideRootOnNavigate, closing, animating, serverRenderDepth } = useNavigation()
52
- const router = useRouter()
53
- const { locale } = router
54
51
 
55
52
  const row = idx + 1
56
53
 
@@ -83,14 +80,13 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
83
80
 
84
81
  if (isNavigationButton(props)) {
85
82
  const { childItems, name, href } = props
86
- const prefix = locale === router.defaultLocale ? '' : `/${locale}`
87
83
  const skipChildren = itemPath.length + 1 > serverRenderDepth && !isSelected && !!href
88
84
 
89
85
  return (
90
86
  <NavigationLI className={classes.li}>
91
87
  <ListItemButton
92
- component={href ? 'a' : 'div'}
93
- href={href ? prefix + href : undefined}
88
+ component={NextLink}
89
+ href={href}
94
90
  className={classes.item}
95
91
  role='button'
96
92
  sx={[
@@ -1,5 +1,5 @@
1
1
  import { MotionValue, useMotionValue } from 'framer-motion'
2
- import React, { createContext, MutableRefObject, useContext } from 'react'
2
+ import React, { createContext, useContext } from 'react'
3
3
 
4
4
  export type NavigationId = string | number
5
5
  export type NavigationPath = NavigationId[]
@@ -2,7 +2,7 @@ import { Scroller, useScrollerContext, useScrollTo } from '@graphcommerce/framer
2
2
  import { dvh, dvw, useIsomorphicLayoutEffect } from '@graphcommerce/framer-utils'
3
3
  import { Box, styled, SxProps, Theme, useTheme, useThemeProps } from '@mui/material'
4
4
  import { m, MotionProps, useDomEvent, useMotionValue, useTransform } from 'framer-motion'
5
- import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'
5
+ import React, { useCallback, useEffect, useRef } from 'react'
6
6
  import { LayoutProvider } from '../../Layout/components/LayoutProvider'
7
7
  import { ExtendableComponent, extendableComponent } from '../../Styles'
8
8
  import { useOverlayPosition } from '../hooks/useOverlayPosition'
@@ -287,7 +287,18 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
287
287
  borderTopLeftRadius: theme.shape.borderRadius * 3,
288
288
  borderTopRightRadius: theme.shape.borderRadius * 3,
289
289
  gridTemplate: `"beforeOverlay" "overlay"`,
290
- height: dvh(100),
290
+ height: `calc(${dvh(100)} - 1px)`,
291
+
292
+ '&::after': {
293
+ content: `""`,
294
+ display: 'block',
295
+ position: 'absolute',
296
+ width: '100%',
297
+ height: '1px',
298
+ top: 'calc(100% - 1px)',
299
+ left: '0',
300
+ background: theme.palette.background.paper,
301
+ },
291
302
  },
292
303
  },
293
304
  [theme.breakpoints.up('md')]: {
@@ -1,9 +1,5 @@
1
1
  import { useScrollerContext } from '@graphcommerce/framer-scroller'
2
- import {
3
- useConstant,
4
- useElementScroll,
5
- useIsomorphicLayoutEffect,
6
- } from '@graphcommerce/framer-utils'
2
+ import { useConstant, useIsomorphicLayoutEffect } from '@graphcommerce/framer-utils'
7
3
  import { motionValue } from 'framer-motion'
8
4
  import { useCallback, useEffect } from 'react'
9
5
  import { useMatchMedia } from '../../hooks'
@@ -4,7 +4,6 @@ import { IconSvg } from '../IconSvg'
4
4
  import { responsiveVal } from '../Styles'
5
5
  import { iconChevronDown, iconChevronUp } from '../icons'
6
6
  import { OverlayOrPopperPanel, OverlayOrPopperPanelProps } from './OverlayOrPopperPanel'
7
- import { PanelProps } from './types'
8
7
 
9
8
  function isMulti(
10
9
  selectedLabel: React.ReactNode | React.ReactNode[],
@@ -1,5 +1,4 @@
1
1
  import { ClickAwayListener, Popper, PopperProps } from '@mui/material'
2
- import { m } from 'framer-motion'
3
2
  import { useRef } from 'react'
4
3
  import { PopperPanelActions } from './PopperPanelActions'
5
4
  import { PanelProps } from './types'
@@ -5,6 +5,7 @@ import { getDomainLocale } from 'next/dist/client/get-domain-locale'
5
5
  import { NextRouter, resolveHref } from 'next/dist/shared/lib/router/router'
6
6
  import Head from 'next/head'
7
7
  import { useRouter } from 'next/router'
8
+ import type {} from '@graphcommerce/next-config'
8
9
 
9
10
  // https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives
10
11
  export type MetaRobots =
@@ -48,12 +49,6 @@ export function canonicalize(router: PartialNextRouter, incoming?: Canonical) {
48
49
  }
49
50
 
50
51
  if (canonical.startsWith('/')) {
51
- if (!process.env.NEXT_PUBLIC_SITE_URL) {
52
- if (process.env.NODE_ENV !== 'production') {
53
- throw Error('NEXT_PUBLIC_SITE_URL is not defined in .env')
54
- }
55
- }
56
-
57
52
  let [href, as = href] = resolveHref(router as NextRouter, canonical, true)
58
53
 
59
54
  const curLocale = router.locale
@@ -65,10 +60,8 @@ export function canonicalize(router: PartialNextRouter, incoming?: Canonical) {
65
60
 
66
61
  href = localeDomain || addBasePath(addLocale(as, curLocale, router.defaultLocale))
67
62
 
68
- let siteUrl = process.env.NEXT_PUBLIC_SITE_URL
69
- if (siteUrl && siteUrl.endsWith('/')) {
70
- siteUrl = siteUrl.slice(0, -1)
71
- }
63
+ let siteUrl = import.meta.graphCommerce.canonicalBaseUrl
64
+ if (siteUrl.endsWith('/')) siteUrl = siteUrl.slice(0, -1)
72
65
 
73
66
  canonical = `${siteUrl}${href}`
74
67
  }
@@ -52,3 +52,13 @@ export function isTypename<T extends TypeObject, Typenames extends T['__typename
52
52
  ): type is FilterTypeByTypename<T, Typenames[number]> {
53
53
  return typename.includes(type.__typename)
54
54
  }
55
+
56
+ export function filterByTypename<T extends TypeObject, Typename extends T['__typename']>(
57
+ type: (T | undefined | null)[] | undefined | null,
58
+ typename: Typename,
59
+ ): FilterTypeByTypename<T, Typename>[] | undefined {
60
+ return type?.filter((item) => item?.__typename === typename) as FilterTypeByTypename<
61
+ T,
62
+ Typename
63
+ >[]
64
+ }
@@ -10,11 +10,17 @@ export type ButtonLinkListItemProps = {
10
10
  } & Omit<ListItemButtonProps<typeof NextLink>, 'href'>
11
11
 
12
12
  export function ButtonLinkListItem(props: ButtonLinkListItemProps) {
13
- const { children, endIcon = <IconSvg src={iconChevronRight} />, ...listItemButtonProps } = props
13
+ const {
14
+ children,
15
+ url,
16
+ endIcon = <IconSvg src={iconChevronRight} />,
17
+ ...listItemButtonProps
18
+ } = props
14
19
 
15
20
  return (
16
21
  <ListItemButton
17
22
  component={NextLink}
23
+ href={url}
18
24
  sx={(theme) => ({
19
25
  padding: `${theme.spacings.xxs} 0`,
20
26
  borderBottom: `1px solid ${theme.palette.divider}`,
@@ -65,7 +65,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
65
65
  setShowSnackbar(!!open)
66
66
  }, [open])
67
67
 
68
- const hideSnackbar = (e) => {
68
+ const hideSnackbar = () => {
69
69
  setShowSnackbar(false)
70
70
  onClose?.()
71
71
  }
@@ -1,4 +1,3 @@
1
- import { SxProps, Theme } from '@mui/material'
2
1
  import { Shadows } from '@mui/material/styles/shadows'
3
2
  import { spreadVal } from '../Styles/spreadVal'
4
3
  import { breakpoints } from './breakpoints'
@@ -103,12 +103,16 @@ const ToggleButtonGroup = React.forwardRef<HTMLDivElement, ToggleButtonGroupProp
103
103
  }
104
104
 
105
105
  return React.cloneElement(child, {
106
+ // @ts-expect-error TODO: fix TS error
106
107
  className: `${classes.button} ${child.props.className ?? ''}`,
107
108
  onChange: exclusive ? handleExclusiveChange : handleChange,
108
109
  selected:
110
+ // @ts-expect-error TODO: fix TS error
109
111
  child.props.selected === undefined
110
- ? isValueSelected(child.props.value as string, value as string | string[])
111
- : child.props.selected,
112
+ ? // @ts-expect-error TODO: fix TS error
113
+ isValueSelected(child.props.value as string, value as string | string[])
114
+ : // @ts-expect-error TODO: fix TS error
115
+ child.props.selected,
112
116
  })
113
117
  })}
114
118
  </Box>
package/hooks/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './useDateTimeFormat'
2
+ export * from './useI18nConfig'
3
+ export * from './useMatchMedia'
4
+ export * from './useMemoDeep'
2
5
  export * from './useNumberFormat'
3
6
  export * from './useUrlQuery'
4
- export * from './useMemoDeep'
5
- export * from './useMatchMedia'
@@ -0,0 +1,17 @@
1
+ import type {} from '@graphcommerce/next-config'
2
+ import { useRouter } from 'next/router'
3
+
4
+ export const i18nAll = import.meta.graphCommerce.i18n
5
+
6
+ /** Get the current i18n config based on the provided locale */
7
+ export const i18nConfig = (locale?: string | undefined) => i18nAll.find((l) => l.locale === locale)
8
+
9
+ export const i18nConfigDefault = () => i18nAll.find((l) => l.defaultLocale) ?? i18nAll[0]
10
+
11
+ /** Automatically selects the correct i18n config based on the current locale */
12
+ export function useI18nConfig(locale?: string | undefined) {
13
+ const routerLocale = useRouter().locale
14
+ const config = i18nConfig(locale ?? routerLocale)
15
+ if (!config) throw Error(`No i18n config found for locale ${locale}`)
16
+ return config
17
+ }
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
1
2
  import { equal } from '@wry/equality'
2
3
  import { DependencyList, useMemo, useRef } from 'react'
3
4
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/next-ui",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "5.2.0-canary.9",
5
+ "version": "6.0.0-canary.20",
6
6
  "author": "",
7
7
  "license": "MIT",
8
8
  "sideEffects": false,
@@ -18,25 +18,25 @@
18
18
  "@emotion/react": "^11.10.5",
19
19
  "@emotion/server": "^11.4.0",
20
20
  "@emotion/styled": "^11.10.5",
21
- "@graphcommerce/framer-next-pages": "5.2.0-canary.9",
22
- "@graphcommerce/framer-scroller": "5.2.0-canary.9",
23
- "@graphcommerce/framer-utils": "5.2.0-canary.9",
24
- "@graphcommerce/image": "5.2.0-canary.9",
21
+ "@graphcommerce/framer-next-pages": "6.0.0-canary.20",
22
+ "@graphcommerce/framer-scroller": "6.0.0-canary.20",
23
+ "@graphcommerce/framer-utils": "6.0.0-canary.20",
24
+ "@graphcommerce/image": "6.0.0-canary.20",
25
25
  "cookie": "^0.5.0",
26
26
  "react-is": "^18.2.0",
27
27
  "schema-dts": "^1.1.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@graphcommerce/eslint-config-pwa": "5.2.0-canary.9",
31
- "@graphcommerce/prettier-config-pwa": "5.2.0-canary.9",
32
- "@graphcommerce/typescript-config-pwa": "5.2.0-canary.9",
30
+ "@graphcommerce/eslint-config-pwa": "6.0.0-canary.20",
31
+ "@graphcommerce/prettier-config-pwa": "6.0.0-canary.20",
32
+ "@graphcommerce/typescript-config-pwa": "6.0.0-canary.20",
33
33
  "@types/cookie": "^0.5.1",
34
34
  "@types/react-is": "^17.0.3",
35
35
  "typescript": "4.9.4"
36
36
  },
37
37
  "peerDependencies": {
38
- "@lingui/core": "^3.13.2",
39
38
  "@lingui/react": "^3.13.2",
39
+ "@lingui/core": "^3.13.2",
40
40
  "@mui/lab": "^5.0.0-alpha.68",
41
41
  "@mui/material": "^5.10.16",
42
42
  "framer-motion": "^7.0.0",