@graphcommerce/next-ui 9.1.0-canary.54 → 10.0.0-canary.56

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 (37) hide show
  1. package/ActionCard/ActionCardList.tsx +2 -2
  2. package/Breadcrumbs/Breadcrumbs.tsx +4 -4
  3. package/Breadcrumbs/BreadcrumbsList.tsx +2 -2
  4. package/CHANGELOG.md +96 -0
  5. package/FramerScroller/SidebarGallery.tsx +5 -4
  6. package/Intl/DateTimeFormat/DateTimeFormat.tsx +3 -2
  7. package/Layout/components/LayoutHeaderBack.tsx +2 -2
  8. package/Layout/components/LayoutHeaderClose.tsx +11 -8
  9. package/LayoutOverlay/components/LayoutOverlayHeader2.tsx +213 -0
  10. package/LayoutOverlay/index.ts +1 -0
  11. package/Navigation/components/NavigationList.tsx +1 -1
  12. package/Navigation/components/NavigationOverlay.tsx +3 -3
  13. package/Navigation/components/NavigationTitle.tsx +2 -2
  14. package/Overlay/components/OverlayBase.tsx +3 -3
  15. package/Overlay/components/OverlayCloseButton.tsx +25 -0
  16. package/Overlay/components/OverlayHeader2.tsx +26 -0
  17. package/Overlay/components/index.ts +4 -2
  18. package/OverlayOrPopperChip/OverlayPanelActions.tsx +5 -5
  19. package/OverlayOrPopperChip/PopperPanelActions.tsx +5 -5
  20. package/Page/types.ts +1 -1
  21. package/PageMeta/canonicalize.ts +2 -1
  22. package/Pagination/Pagination.tsx +5 -2
  23. package/SkipLink/SkipLink.tsx +2 -2
  24. package/Snackbar/ErrorSnackbar.tsx +2 -2
  25. package/Snackbar/MessageSnackbarImpl.tsx +2 -2
  26. package/Styles/withEmotionCache.tsx +1 -3
  27. package/Tabs/TabItem.tsx +134 -0
  28. package/Tabs/Tabs.tsx +124 -0
  29. package/TextInputNumber/TextInputNumber.tsx +4 -4
  30. package/Theme/DarkLightModeThemeProvider.tsx +2 -2
  31. package/Theme/NextLink.tsx +4 -4
  32. package/hooks/memoDeep.ts +1 -1
  33. package/index.ts +2 -0
  34. package/package.json +34 -16
  35. package/po.d.ts +6 -0
  36. package/{types.d.ts → types.ts} +0 -9
  37. package/utils/storefrontConfig.ts +5 -3
@@ -1,5 +1,5 @@
1
- import { i18n } from '@lingui/core'
2
- import { Trans } from '@lingui/react'
1
+ import { t } from '@lingui/core/macro'
2
+ import { Trans } from '@lingui/react/macro'
3
3
  import { Box, Button, Fab, Typography } from '@mui/material'
4
4
  import { iconClose } from '../icons'
5
5
  import { IconSvg, useIconSvgSize } from '../IconSvg'
@@ -30,7 +30,7 @@ export function OverlayPanelActions(props: PanelActionsProps) {
30
30
  primary={
31
31
  onReset && (
32
32
  <Button variant='inline' color='primary' onClick={onReset}>
33
- <Trans id='Reset' />
33
+ <Trans>Reset</Trans>
34
34
  </Button>
35
35
  )
36
36
  }
@@ -43,7 +43,7 @@ export function OverlayPanelActions(props: PanelActionsProps) {
43
43
  ml: `calc((${fabSize} - ${svgSize}) * -0.5)`,
44
44
  }}
45
45
  size='small'
46
- aria-label={i18n._(/* i18n */ 'Close')}
46
+ aria-label={t`Close`}
47
47
  >
48
48
  <IconSvg src={iconClose} size='large' aria-hidden />
49
49
  </Fab>
@@ -68,7 +68,7 @@ export function OverlayPanelActions(props: PanelActionsProps) {
68
68
  fullWidth
69
69
  // sx={(theme) => ({ mt: theme.spacings.md })}
70
70
  >
71
- <Trans id='Apply' />
71
+ <Trans>Apply</Trans>
72
72
  </Button>
73
73
  </OverlayStickyBottom>
74
74
  </>
@@ -1,5 +1,5 @@
1
- import { i18n } from '@lingui/core'
2
- import { Trans } from '@lingui/react'
1
+ import { t } from '@lingui/core/macro'
2
+ import { Trans } from '@lingui/react/macro'
3
3
  import { Box, Button, Fab, Typography } from '@mui/material'
4
4
  import { iconClose } from '../icons'
5
5
  import { IconSvg, useIconSvgSize } from '../IconSvg'
@@ -24,7 +24,7 @@ export function PopperPanelActions(props: PanelActionsProps) {
24
24
  primary={
25
25
  onReset && (
26
26
  <Button variant='text' color='primary' size='medium' onClick={onReset}>
27
- <Trans id='Reset' />
27
+ <Trans>Reset</Trans>
28
28
  </Button>
29
29
  )
30
30
  }
@@ -37,7 +37,7 @@ export function PopperPanelActions(props: PanelActionsProps) {
37
37
  ml: `calc((${fabSize} - ${svgSize}) * -0.5)`,
38
38
  }}
39
39
  size='small'
40
- aria-label={i18n._(/* i18n */ 'Close')}
40
+ aria-label={t`Close`}
41
41
  >
42
42
  <IconSvg src={iconClose} size='large' aria-hidden />
43
43
  </Fab>
@@ -68,7 +68,7 @@ export function PopperPanelActions(props: PanelActionsProps) {
68
68
  color='primary'
69
69
  fullWidth
70
70
  >
71
- <Trans id='Apply' />
71
+ <Trans>Apply</Trans>
72
72
  </Button>
73
73
  </OverlayStickyBottom>
74
74
  </>
package/Page/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ParsedUrlQuery } from 'querystring'
2
- import type { UpPage } from '@graphcommerce/framer-next-pages/types'
2
+ import type { UpPage } from '@graphcommerce/framer-next-pages'
3
3
  // todo: remove references to GraphQL
4
4
  // eslint-disable-next-line import/no-extraneous-dependencies
5
5
  import type { NormalizedCacheObject } from '@graphcommerce/graphql'
@@ -1,4 +1,5 @@
1
1
  import type {} from '@graphcommerce/next-config'
2
+ import { canonicalBaseUrl } from '@graphcommerce/next-config/config'
2
3
  import { addBasePath } from 'next/dist/client/add-base-path'
3
4
  import { addLocale } from 'next/dist/client/add-locale'
4
5
  import { getDomainLocale } from 'next/dist/client/get-domain-locale'
@@ -49,7 +50,7 @@ export function canonicalize(router: PartialNextRouter, incoming?: Canonical) {
49
50
  addLocale(as, curLocale, conf?.domain ? conf.locale : router.defaultLocale),
50
51
  )
51
52
 
52
- let siteUrl = conf?.canonicalBaseUrl || import.meta.graphCommerce.canonicalBaseUrl
53
+ let siteUrl = conf?.canonicalBaseUrl || canonicalBaseUrl
53
54
 
54
55
  if (conf?.domain && !conf?.canonicalBaseUrl) siteUrl = `https://${conf.domain}`
55
56
 
@@ -1,4 +1,4 @@
1
- import { Trans } from '@lingui/react'
1
+ import { Trans } from '@lingui/react/macro'
2
2
  import type { PaginationProps, SxProps, Theme } from '@mui/material'
3
3
  import { Box, IconButton } from '@mui/material'
4
4
  import type { UsePaginationItem } from '@mui/material/usePagination'
@@ -59,6 +59,7 @@ export function Pagination(props: PagePaginationProps) {
59
59
  <IconSvg src={iconChevronRight} className={classes.icon} size='medium' />
60
60
  </IconButton>
61
61
  )
62
+ const max = Math.max(1, count)
62
63
 
63
64
  return (
64
65
  <Box
@@ -82,7 +83,9 @@ export function Pagination(props: PagePaginationProps) {
82
83
  {page === 1 ? chevronLeft : renderLink(page - 1, chevronLeft, prevBtnProps)}
83
84
 
84
85
  <Box typography='body1'>
85
- <Trans id='Page {page} of {count}' values={{ page, count: Math.max(1, count) }} />
86
+ <Trans>
87
+ Page {page} of {max}
88
+ </Trans>
86
89
  </Box>
87
90
 
88
91
  {page === count ? chevronRight : renderLink(page + 1, chevronRight, nextBtnProps)}
@@ -1,4 +1,4 @@
1
- import { Trans } from '@lingui/react'
1
+ import { Trans } from '@lingui/react/macro'
2
2
  import { Link } from '@mui/material'
3
3
 
4
4
  export function SkipLink() {
@@ -32,7 +32,7 @@ export function SkipLink() {
32
32
  },
33
33
  })}
34
34
  >
35
- <Trans id='Skip to main content' />
35
+ <Trans>Skip to main content</Trans>
36
36
  </Link>
37
37
  )
38
38
  }
@@ -1,4 +1,4 @@
1
- import { Trans } from '@lingui/react'
1
+ import { Trans } from '@lingui/react/macro'
2
2
  import { Button } from '@mui/material'
3
3
  import { MessageSnackbar } from './MessageSnackbar'
4
4
  import type { MessageSnackbarProps } from './MessageSnackbarImpl'
@@ -15,7 +15,7 @@ export function ErrorSnackbar(props: ErrorSnackbarProps) {
15
15
  action={
16
16
  action ?? (
17
17
  <Button size='medium' variant='pill' color='secondary' fullWidth>
18
- <Trans id='Ok' />
18
+ <Trans>Ok</Trans>
19
19
  </Button>
20
20
  )
21
21
  }
@@ -1,4 +1,4 @@
1
- import { i18n } from '@lingui/core'
1
+ import { t } from '@lingui/core/macro'
2
2
  import type { SnackbarProps, SxProps, Theme } from '@mui/material'
3
3
  import { Box, Fab, lighten, Portal, Snackbar, SnackbarContent } from '@mui/material'
4
4
  import React, { useEffect, useState } from 'react'
@@ -190,7 +190,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
190
190
  {!disableClose && (
191
191
  <Fab
192
192
  className={classes.close}
193
- aria-label={i18n._(/* i18n */ 'Close')}
193
+ aria-label={t`Close`}
194
194
  size='small'
195
195
  onClick={hideSnackbar}
196
196
  onMouseDown={preventAnimationBubble}
@@ -1,4 +1,3 @@
1
- import type { EmotionJSX } from '@emotion/react/types/jsx-namespace'
2
1
  import createEmotionServer from '@emotion/server/create-instance'
3
2
  import type { AppType } from 'next/app'
4
3
  // eslint-disable-next-line @next/next/no-document-import-in-page
@@ -8,7 +7,7 @@ import type { DocumentContext } from 'next/document'
8
7
  import { createEmotionCache } from './createEmotionCache'
9
8
  import type { EmotionProviderProps } from './EmotionProvider'
10
9
 
11
- export type EmotionCacheProps = { emotionStyleTags: EmotionJSX.Element[] }
10
+ export type EmotionCacheProps = { emotionStyleTags: React.ReactNode[] }
12
11
 
13
12
  export function withEmotionCache(Document: typeof NextDocument): typeof NextDocument {
14
13
  return class DocumentWithEmotionCache extends Document {
@@ -32,7 +31,6 @@ export function withEmotionCache(Document: typeof NextDocument): typeof NextDocu
32
31
  <style
33
32
  data-emotion={`${style.key} ${style.ids.join(' ')}`}
34
33
  key={style.key}
35
- // eslint-disable-next-line react/no-danger
36
34
  dangerouslySetInnerHTML={{ __html: style.css }}
37
35
  />
38
36
  ))
@@ -0,0 +1,134 @@
1
+ import { Box, ButtonBase, type SxProps, type Theme } from '@mui/material'
2
+ import { alpha, useTheme } from '@mui/material/styles'
3
+ import { forwardRef } from 'react'
4
+ import { extendableComponent } from '../Styles/extendableComponent'
5
+ import { responsiveVal } from '../Styles/responsiveVal'
6
+ import { sxx } from '../utils/sxx'
7
+
8
+ export type TabItemVariant = 'chrome'
9
+
10
+ export type TabItemProps = {
11
+ /** Whether this tab is selected */
12
+ selected: boolean
13
+ /** Content to display inside the tab */
14
+ children: React.ReactNode
15
+ /** Click handler */
16
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
17
+
18
+ /** Spacing for the tab */
19
+ spacing?: string
20
+ /** Custom styling */
21
+ sx?: SxProps<Theme>
22
+ /** Whether to disable ripple effect */
23
+ disableRipple?: boolean
24
+ /** Additional className */
25
+ className?: string
26
+
27
+ color: (theme: Theme) => string
28
+ variant?: 'chrome'
29
+ }
30
+
31
+ type State = {
32
+ selected: boolean
33
+ variant: 'chrome'
34
+ }
35
+
36
+ const name = 'TabItem'
37
+ const parts = ['root', 'content'] as const
38
+ const { withState } = extendableComponent<State, typeof name, typeof parts>(name, parts)
39
+
40
+ export const TabItem = forwardRef<HTMLButtonElement, TabItemProps>((props, ref) => {
41
+ const {
42
+ selected,
43
+ children,
44
+ onClick,
45
+ spacing = responsiveVal(4, 20),
46
+ sx,
47
+ disableRipple = true,
48
+ className,
49
+ color = (theme) => theme.palette.background.default,
50
+ variant = 'chrome',
51
+ ...other
52
+ } = props
53
+
54
+ const classes = withState({ selected, variant })
55
+
56
+ return (
57
+ <Box
58
+ ref={ref}
59
+ component={ButtonBase}
60
+ onClick={selected ? undefined : onClick}
61
+ className={`${classes.root} ${className ?? ''}`}
62
+ disableRipple={selected || disableRipple}
63
+ role='tab'
64
+ aria-selected={selected}
65
+ // tabindex={0}
66
+ sx={sxx(
67
+ (theme) => ({
68
+ position: 'relative',
69
+ textDecoration: 'none',
70
+ color: 'text.primary',
71
+ typography: 'subtitle1',
72
+ mt: spacing,
73
+ height: `calc(100% - ${spacing})`,
74
+ pb: spacing,
75
+ mx: `calc(${spacing} / 2)`,
76
+ transition: 'background-color 0.2s ease-in-out',
77
+
78
+ '&:hover:not(.selected) .TabItem-content': {
79
+ bgcolor: alpha(color(theme), 0.5),
80
+ },
81
+ '&:focus:not(.selected) .TabItem-content': {
82
+ bgcolor: alpha(color(theme), 0.5),
83
+ },
84
+ '&::before': {
85
+ opacity: 0,
86
+ transition: 'opacity 0.2s ease-in-out',
87
+ content: '""',
88
+ position: 'absolute',
89
+ bottom: 0,
90
+ left: '-10px',
91
+ width: '10px',
92
+ height: '10px',
93
+ background: `radial-gradient(circle at top left, transparent 10px, ${color(theme)} 10px)`,
94
+ },
95
+ '&::after': {
96
+ opacity: 0,
97
+ transition: 'opacity 0.2s ease-in-out',
98
+ content: '""',
99
+ position: 'absolute',
100
+ bottom: 0,
101
+ right: '-10px',
102
+ width: '10px',
103
+ height: '10px',
104
+ background: `radial-gradient(circle at top right, transparent 10px, ${color(theme)} 10px)`,
105
+ },
106
+ borderStartStartRadius: '0.5em',
107
+ borderStartEndRadius: '0.5em',
108
+ '&.selected': {
109
+ bgcolor: color(theme),
110
+ '&::before': { opacity: 1 },
111
+ '&::after': { opacity: 1 },
112
+ },
113
+ }),
114
+ sx,
115
+ )}
116
+ {...other}
117
+ >
118
+ <Box
119
+ className={classes.content}
120
+ sx={{
121
+ display: 'flex',
122
+ alignItems: 'center',
123
+ height: '100%',
124
+ px: spacing,
125
+ gap: `calc(${spacing} / 2)`,
126
+ borderRadius: '0.5em',
127
+ transition: 'background-color 0.2s ease-in-out',
128
+ }}
129
+ >
130
+ {children}
131
+ </Box>
132
+ </Box>
133
+ )
134
+ })
package/Tabs/Tabs.tsx ADDED
@@ -0,0 +1,124 @@
1
+ import { Scroller, ScrollerButton, ScrollerProvider } from '@graphcommerce/framer-scroller'
2
+ import { Box, type SxProps, type Theme } from '@mui/material'
3
+ import React from 'react'
4
+ import { iconChevronLeft, iconChevronRight } from '../icons'
5
+ import { IconSvg } from '../IconSvg'
6
+ import { extendableComponent } from '../Styles/extendableComponent'
7
+ import { sxx } from '../utils/sxx'
8
+
9
+ export type TabsProps = {
10
+ children: React.ReactNode
11
+ sx?: SxProps<Theme>
12
+ /** Additional content to show before tabs */
13
+ startContent?: React.ReactNode
14
+ /** Additional content to show after tabs */
15
+ endContent?: React.ReactNode
16
+ /** Whether to show separators between tabs */
17
+ showSeparators?: boolean
18
+ /** Custom separator component */
19
+ separator?: React.ReactNode
20
+ }
21
+
22
+ const { classes } = extendableComponent('Tabs', ['root', 'scroller', 'left', 'right', 'button'])
23
+
24
+ export function Tabs(props: TabsProps) {
25
+ const { children, sx, startContent, endContent, showSeparators = true, separator } = props
26
+
27
+ const defaultSeparator = (
28
+ <Box
29
+ className='separator'
30
+ sx={{ width: '2px', height: '1em', bgcolor: 'divider', mx: '-1px' }}
31
+ />
32
+ )
33
+
34
+ const separatorElement = separator ?? defaultSeparator
35
+
36
+ return (
37
+ <Box
38
+ className={classes.root}
39
+ sx={sxx(
40
+ {
41
+ alignItems: 'stretch',
42
+ position: 'relative',
43
+ pointerEvents: 'all',
44
+ gridTemplateColumns: 'auto 1fr auto',
45
+ display: 'grid',
46
+ gridAutoFlow: 'column',
47
+ },
48
+ sx,
49
+ )}
50
+ >
51
+ <ScrollerProvider scrollSnapAlign='none'>
52
+ <Scroller
53
+ hideScrollbar
54
+ sx={{
55
+ gridArea: '1 / 1 / 1 / 4',
56
+ gridAutoColumns: 'min-content',
57
+ alignItems: 'center',
58
+ // Hide the Separator that comes before or after the selected tab.
59
+ '& .separator': { transition: 'opacity 0.2s ease-in-out' },
60
+ '& .TabItem-root.selected + .separator': { opacity: 0 },
61
+ '& .separator:has(+ .TabItem-root.selected)': { opacity: 0 },
62
+ // Same for hover:
63
+ '& .TabItem-root:hover + .separator': { opacity: 0 },
64
+ '& .separator:has(+ .TabItem-root:hover)': { opacity: 0 },
65
+ }}
66
+ className={classes.scroller}
67
+ role='tablist'
68
+ >
69
+ {startContent}
70
+
71
+ {showSeparators && startContent && separatorElement}
72
+
73
+ {React.Children.map(children, (child, index) => (
74
+ // eslint-disable-next-line react/no-array-index-key
75
+ <React.Fragment key={index}>
76
+ {showSeparators && index > 0 && separatorElement}
77
+ {child}
78
+ </React.Fragment>
79
+ ))}
80
+
81
+ {showSeparators && endContent && separatorElement}
82
+
83
+ {endContent}
84
+ </Scroller>
85
+
86
+ <ScrollerButton
87
+ sxContainer={{
88
+ gridArea: '1 / 1 / 1 / 2',
89
+ display: 'flex',
90
+ alignItems: 'center',
91
+ justifyContent: 'center',
92
+ pointerEvents: 'none',
93
+ '& > *': { pointerEvents: 'all' },
94
+ }}
95
+ sx={{ pointerEvents: 'all' }}
96
+ direction='left'
97
+ size='small'
98
+ tabIndex={-1}
99
+ className={`${classes.left} ${classes.button}`}
100
+ >
101
+ <IconSvg src={iconChevronLeft} />
102
+ </ScrollerButton>
103
+
104
+ <ScrollerButton
105
+ sxContainer={{
106
+ gridArea: '1 / 3 / 1 / 4',
107
+ display: 'flex',
108
+ alignItems: 'center',
109
+ justifyContent: 'center',
110
+ pointerEvents: 'none',
111
+ '& > *': { pointerEvents: 'all' },
112
+ }}
113
+ sx={{ pointerEvents: 'all' }}
114
+ direction='right'
115
+ size='small'
116
+ tabIndex={-1}
117
+ className={`${classes.right} ${classes.button}`}
118
+ >
119
+ <IconSvg src={iconChevronRight} />
120
+ </ScrollerButton>
121
+ </ScrollerProvider>
122
+ </Box>
123
+ )
124
+ }
@@ -1,4 +1,4 @@
1
- import { i18n } from '@lingui/core'
1
+ import { t } from '@lingui/core/macro'
2
2
  import type { IconButtonProps, SxProps, TextFieldProps, Theme } from '@mui/material'
3
3
  import {
4
4
  Box,
@@ -125,7 +125,7 @@ export function TextInputNumber(props: TextInputNumberProps) {
125
125
  startAdornment: (
126
126
  <Box>
127
127
  <Fab
128
- aria-label={i18n._(/* i18n */ 'Decrease')}
128
+ aria-label={t`Decrease`}
129
129
  size='smaller'
130
130
  sx={{ boxShadow: variant === 'standard' ? 4 : 0, minHeight: '30px' }}
131
131
  onPointerDown={() => setDirection('down')}
@@ -141,7 +141,7 @@ export function TextInputNumber(props: TextInputNumberProps) {
141
141
  endAdornment: (
142
142
  <Box>
143
143
  <Fab
144
- aria-label={i18n._(/* i18n */ 'Increase')}
144
+ aria-label={t`Increase`}
145
145
  size='smaller'
146
146
  sx={{ boxShadow: variant === 'standard' ? 4 : 0, minHeight: '30px' }}
147
147
  onPointerDown={() => setDirection('up')}
@@ -160,7 +160,7 @@ export function TextInputNumber(props: TextInputNumberProps) {
160
160
  updateDisabled(e.target)
161
161
  }}
162
162
  inputProps={{
163
- 'aria-label': i18n._(/* i18n */ 'Number'),
163
+ 'aria-label': t`Number`,
164
164
  ...inputProps,
165
165
  sx: [
166
166
  {
@@ -1,4 +1,4 @@
1
- import { Trans } from '@lingui/react'
1
+ import { Trans } from '@lingui/react/macro'
2
2
  import type { FabProps, ListItemButtonProps, Theme } from '@mui/material'
3
3
  import {
4
4
  Fab,
@@ -133,7 +133,7 @@ export function DarkLightModeMenuSecondaryItem(props: ListItemButtonProps) {
133
133
  <IconSvg src={currentMode === 'light' ? iconMoon : iconSun} size='medium' />
134
134
  </ListItemIcon>
135
135
  <ListItemText>
136
- {currentMode === 'light' ? <Trans id='Dark Mode' /> : <Trans id='Light Mode' />}
136
+ {currentMode === 'light' ? <Trans>Dark Mode</Trans> : <Trans>Light Mode</Trans>}
137
137
  </ListItemText>
138
138
  </ListItemButton>
139
139
  )
@@ -1,3 +1,4 @@
1
+ import { canonicalBaseUrl } from '@graphcommerce/next-config/config'
1
2
  import type { LinkProps as NextLinkProps } from 'next/link'
2
3
  import Link from 'next/link'
3
4
  import { useRouter } from 'next/router'
@@ -30,8 +31,7 @@ export const NextLink = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) =>
30
31
  let { href, target, relative, locale, ...rest } = props
31
32
 
32
33
  const router = useRouter()
33
- const canonicalBaseUrl =
34
- useStorefrontConfig().canonicalBaseUrl ?? import.meta.graphCommerce.canonicalBaseUrl
34
+ const canonical = useStorefrontConfig().canonicalBaseUrl ?? canonicalBaseUrl
35
35
 
36
36
  // The href is optional in a MUI link, but required in a Next.js link
37
37
  // When the href is not a string, we pass it through directly
@@ -44,14 +44,14 @@ export const NextLink = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) =>
44
44
  * and make the URL relative without the locale. Prevents Next.js prefixing again with the current
45
45
  * locale.
46
46
  */
47
- if (!locale && isFullUrl && href.startsWith(canonicalBaseUrl)) {
47
+ if (!locale && isFullUrl && href.startsWith(canonical)) {
48
48
  const url = new URL(href)
49
49
  locale = router.locales?.find((l) => url.pathname.startsWith(`/${l}/`))
50
50
  href = locale ? url.pathname.replace(`/${locale}/`, '/') : url.pathname
51
51
  href += url.search
52
52
  }
53
53
 
54
- const isExternal = isFullUrl && !href.startsWith(canonicalBaseUrl)
54
+ const isExternal = isFullUrl && !href.startsWith(canonical)
55
55
  if (isExternal) target = target || '_blank'
56
56
 
57
57
  // Relative URL's cause more pain than they're worth
package/hooks/memoDeep.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // eslint-disable-next-line import/no-extraneous-dependencies
2
- import diff from '@graphcommerce/react-hook-form/src/diff'
2
+ import { diff } from '@graphcommerce/react-hook-form'
3
3
  // eslint-disable-next-line import/no-extraneous-dependencies
4
4
  import { equal } from '@wry/equality'
5
5
  import type { FunctionComponent, NamedExoticComponent } from 'react'
package/index.ts CHANGED
@@ -59,6 +59,8 @@ export * from './Stepper/Stepper'
59
59
  export * from './Styles'
60
60
  export * from './TextInputNumber/TextInputNumber'
61
61
  export * from './Theme'
62
+ export * from './Tabs/TabItem'
63
+ export * from './Tabs/Tabs'
62
64
  export * from './TimeAgo/TimeAgo'
63
65
  export * from './ToggleButton/ToggleButton'
64
66
  export * from './ToggleButtonGroup/ToggleButtonGroup'
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": "9.1.0-canary.54",
5
+ "version": "10.0.0-canary.56",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -11,35 +11,53 @@
11
11
  "project": "./tsconfig.json"
12
12
  }
13
13
  },
14
+ "exports": {
15
+ ".": "./index.ts",
16
+ "./types": "./types.ts",
17
+ "./server": "./server.ts",
18
+ "./Layout/components/LayoutHeaderClose": "./Layout/components/LayoutHeaderClose.tsx",
19
+ "./LayoutOverlay/test/LayoutOverlayDemo": "./LayoutOverlay/test/LayoutOverlayDemo.tsx",
20
+ "./Styles/extendableComponent": "./Styles/extendableComponent.ts",
21
+ "./Styles": "./Styles/index.ts",
22
+ "./hooks/useMatchMedia": "./hooks/useMatchMedia.ts",
23
+ "./hooks/useStorefrontConfig": "./hooks/useStorefrontConfig.ts",
24
+ "./hooks/useIsSsr": "./hooks/useIsSsr.ts",
25
+ "./hooks/useMemoObject": "./hooks/useMemoObject.ts",
26
+ "./utils/cssFlags": "./utils/cssFlags.tsx",
27
+ "./Intl/DateTimeFormat/toDate": "./Intl/DateTimeFormat/toDate.ts",
28
+ "./Breadcrumbs/BreadcrumbsJsonLd": "./Breadcrumbs/BreadcrumbsJsonLd.tsx",
29
+ "./Breadcrumbs/jsonLdBreadcrumb": "./Breadcrumbs/jsonLdBreadcrumb.tsx",
30
+ "./RenderType/filterNonNullableKeys": "./RenderType/filterNonNullableKeys.ts"
31
+ },
14
32
  "dependencies": {
15
33
  "cookie": "^1.0.2",
16
- "react-is": "^18.3.1"
34
+ "react-is": "^19.2.0"
17
35
  },
18
36
  "devDependencies": {
19
- "@types/react-is": "^18.3.1",
20
- "typescript": "5.7.2"
37
+ "@types/react-is": "^19.2.0",
38
+ "typescript": "5.9.3"
21
39
  },
22
40
  "peerDependencies": {
23
41
  "@emotion/cache": "^11",
24
42
  "@emotion/react": "^11",
25
43
  "@emotion/server": "^11",
26
44
  "@emotion/styled": "^11",
27
- "@graphcommerce/eslint-config-pwa": "^9.1.0-canary.54",
28
- "@graphcommerce/framer-next-pages": "^9.1.0-canary.54",
29
- "@graphcommerce/framer-scroller": "^9.1.0-canary.54",
30
- "@graphcommerce/framer-utils": "^9.1.0-canary.54",
31
- "@graphcommerce/image": "^9.1.0-canary.54",
32
- "@graphcommerce/prettier-config-pwa": "^9.1.0-canary.54",
33
- "@graphcommerce/typescript-config-pwa": "^9.1.0-canary.54",
34
- "@lingui/core": "^4.2.1",
35
- "@lingui/macro": "^4.2.1",
36
- "@lingui/react": "^4.2.1",
45
+ "@graphcommerce/eslint-config-pwa": "^10.0.0-canary.56",
46
+ "@graphcommerce/framer-next-pages": "^10.0.0-canary.56",
47
+ "@graphcommerce/framer-scroller": "^10.0.0-canary.56",
48
+ "@graphcommerce/framer-utils": "^10.0.0-canary.56",
49
+ "@graphcommerce/image": "^10.0.0-canary.56",
50
+ "@graphcommerce/prettier-config-pwa": "^10.0.0-canary.56",
51
+ "@graphcommerce/typescript-config-pwa": "^10.0.0-canary.56",
52
+ "@lingui/core": "^5",
53
+ "@lingui/macro": "^5",
54
+ "@lingui/react": "^5",
37
55
  "@mui/lab": "^5.0.0-alpha.68",
38
56
  "@mui/material": "^5.10.16",
39
57
  "framer-motion": "^11.0.0",
40
58
  "next": "*",
41
59
  "next-sitemap": "4.2.3",
42
- "react": "^18.2.0",
43
- "react-dom": "^18.2.0"
60
+ "react": "^19.2.0",
61
+ "react-dom": "^19.2.0"
44
62
  }
45
63
  }
package/po.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare module '*.po' {
2
+ export const messages: Record<
3
+ string,
4
+ string | Array<string | Array<string | (string | undefined) | Record<string, unknown>>>
5
+ >
6
+ }
@@ -5,12 +5,3 @@
5
5
  import './Theme/createTheme'
6
6
  // eslint-disable-next-line react/no-typos
7
7
  import 'react'
8
-
9
- declare module '*.po' {
10
- const messages: Record<
11
- string,
12
- string | Array<string | Array<string | (string | undefined) | Record<string, unknown>>>
13
- >
14
-
15
- export const messages
16
- }
@@ -1,8 +1,10 @@
1
- export const storefrontAll = import.meta.graphCommerce.storefront
1
+ import { storefront } from '@graphcommerce/next-config/config'
2
+
3
+ export const storefrontAll = storefront
2
4
 
3
5
  /** Get the current storefront config based on the provided locale */
4
6
  export const storefrontConfig = (locale?: string | undefined) =>
5
- storefrontAll.find((l) => l.locale === locale)
7
+ storefront.find((l) => l.locale === locale)
6
8
 
7
9
  export const storefrontConfigDefault = () =>
8
- storefrontAll.find((l) => l.defaultLocale) ?? storefrontAll[0]
10
+ storefront.find((l) => l.defaultLocale) ?? storefrontAll[0]