@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.
- package/ActionCard/ActionCardList.tsx +2 -2
- package/Breadcrumbs/Breadcrumbs.tsx +4 -4
- package/Breadcrumbs/BreadcrumbsList.tsx +2 -2
- package/CHANGELOG.md +96 -0
- package/FramerScroller/SidebarGallery.tsx +5 -4
- package/Intl/DateTimeFormat/DateTimeFormat.tsx +3 -2
- package/Layout/components/LayoutHeaderBack.tsx +2 -2
- package/Layout/components/LayoutHeaderClose.tsx +11 -8
- package/LayoutOverlay/components/LayoutOverlayHeader2.tsx +213 -0
- package/LayoutOverlay/index.ts +1 -0
- package/Navigation/components/NavigationList.tsx +1 -1
- package/Navigation/components/NavigationOverlay.tsx +3 -3
- package/Navigation/components/NavigationTitle.tsx +2 -2
- package/Overlay/components/OverlayBase.tsx +3 -3
- package/Overlay/components/OverlayCloseButton.tsx +25 -0
- package/Overlay/components/OverlayHeader2.tsx +26 -0
- package/Overlay/components/index.ts +4 -2
- package/OverlayOrPopperChip/OverlayPanelActions.tsx +5 -5
- package/OverlayOrPopperChip/PopperPanelActions.tsx +5 -5
- package/Page/types.ts +1 -1
- package/PageMeta/canonicalize.ts +2 -1
- package/Pagination/Pagination.tsx +5 -2
- package/SkipLink/SkipLink.tsx +2 -2
- package/Snackbar/ErrorSnackbar.tsx +2 -2
- package/Snackbar/MessageSnackbarImpl.tsx +2 -2
- package/Styles/withEmotionCache.tsx +1 -3
- package/Tabs/TabItem.tsx +134 -0
- package/Tabs/Tabs.tsx +124 -0
- package/TextInputNumber/TextInputNumber.tsx +4 -4
- package/Theme/DarkLightModeThemeProvider.tsx +2 -2
- package/Theme/NextLink.tsx +4 -4
- package/hooks/memoDeep.ts +1 -1
- package/index.ts +2 -0
- package/package.json +34 -16
- package/po.d.ts +6 -0
- package/{types.d.ts → types.ts} +0 -9
- package/utils/storefrontConfig.ts +5 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Trans } from '@lingui/react'
|
|
1
|
+
import { Trans } from '@lingui/react/macro'
|
|
2
2
|
import type { SxProps, Theme } from '@mui/material'
|
|
3
3
|
import { Alert } from '@mui/material'
|
|
4
4
|
import React from 'react'
|
|
@@ -168,7 +168,7 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
|
|
|
168
168
|
variant='text'
|
|
169
169
|
onClick={() => setShow(!show)}
|
|
170
170
|
>
|
|
171
|
-
{!show ? <Trans
|
|
171
|
+
{!show ? <Trans>More options</Trans> : <Trans>Less options</Trans>}{' '}
|
|
172
172
|
<IconSvg
|
|
173
173
|
sx={{
|
|
174
174
|
transform: show ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Trans } from '@lingui/react'
|
|
1
|
+
import { t } from '@lingui/core/macro'
|
|
2
|
+
import { Trans } from '@lingui/react/macro'
|
|
3
3
|
import type {
|
|
4
4
|
LinkProps,
|
|
5
5
|
BreadcrumbsProps as MuiBreadcrumbProps,
|
|
@@ -71,7 +71,7 @@ export function Breadcrumbs(props: BreadcrumbsProps) {
|
|
|
71
71
|
return (
|
|
72
72
|
<MuiBreadcrumbs
|
|
73
73
|
{...rest}
|
|
74
|
-
aria-label={
|
|
74
|
+
aria-label={t`Breadcrumbs`}
|
|
75
75
|
maxItems={maxItems}
|
|
76
76
|
color='inherit'
|
|
77
77
|
sx={[
|
|
@@ -167,7 +167,7 @@ export function Breadcrumbs(props: BreadcrumbsProps) {
|
|
|
167
167
|
{...linkProps}
|
|
168
168
|
sx={[...(Array.isArray(itemSx) ? itemSx : [itemSx])]}
|
|
169
169
|
>
|
|
170
|
-
<Trans
|
|
170
|
+
<Trans>Home</Trans>
|
|
171
171
|
</Link>
|
|
172
172
|
)}
|
|
173
173
|
{breadcrumbLinks.map((breadcrumb) => (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Trans } from '@lingui/react'
|
|
1
|
+
import { Trans } from '@lingui/react/macro'
|
|
2
2
|
import { alpha, Box, Link, useTheme } from '@mui/material'
|
|
3
3
|
import type { KeyboardEvent } from 'react'
|
|
4
4
|
import { useEffect, useRef } from 'react'
|
|
@@ -74,7 +74,7 @@ export function BreadcrumbsList(props: PopperBreadcrumbsListProps) {
|
|
|
74
74
|
},
|
|
75
75
|
}}
|
|
76
76
|
>
|
|
77
|
-
<Trans
|
|
77
|
+
<Trans>Home</Trans>
|
|
78
78
|
</Link>
|
|
79
79
|
{breadcrumbs.slice(0, breadcrumbs.length).map((breadcrumb) => (
|
|
80
80
|
<Link
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,101 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 10.0.0-canary.56
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#2546](https://github.com/graphcommerce-org/graphcommerce/pull/2546) [`ed9332a`](https://github.com/graphcommerce-org/graphcommerce/commit/ed9332a7f78966d932041d9a7725641edc92b28d) - ## GraphCommerce 10 - Turbopack Support
|
|
8
|
+
|
|
9
|
+
This major release brings full Turbopack compatibility, dramatically improving development speed.
|
|
10
|
+
|
|
11
|
+
### 🚀 Turbopack-Compatible Interceptor System
|
|
12
|
+
|
|
13
|
+
The entire plugin/interceptor system has been rewritten to work with Turbopack:
|
|
14
|
+
|
|
15
|
+
- **No more Webpack plugins** - Removed `InterceptorPlugin` webpack plugin entirely
|
|
16
|
+
- **File-based interception** - Original files are moved to `.original.tsx` and replaced with interceptor content
|
|
17
|
+
- **Direct imports** - Interceptors import from `.original` files instead of embedding source
|
|
18
|
+
- **New CLI commands**:
|
|
19
|
+
- `graphcommerce codegen-interceptors` - Generate interceptor files
|
|
20
|
+
- `graphcommerce cleanup-interceptors` - Reset interceptor system, restore original files
|
|
21
|
+
- **Stable file hashing** - Deterministic interceptor generation for better caching
|
|
22
|
+
|
|
23
|
+
### ⚙️ Treeshakable Configuration System
|
|
24
|
+
|
|
25
|
+
Replaced Webpack `DefinePlugin`-based `import.meta.graphCommerce` with a new generated configuration system:
|
|
26
|
+
|
|
27
|
+
- **New `codegen-config-values` command** - Generates TypeScript files with precise typing
|
|
28
|
+
- **Schema-driven** - Dynamically introspects Zod schemas to determine all available properties
|
|
29
|
+
- **Fully treeshakable** - Unused config values are eliminated from the bundle
|
|
30
|
+
- **Type-safe** - Uses `Get<GraphCommerceConfig, 'path'>` for nested property access
|
|
31
|
+
- **Separate files for nested objects** - Optimal treeshaking for complex configurations
|
|
32
|
+
|
|
33
|
+
### 🔧 withGraphCommerce Changes
|
|
34
|
+
|
|
35
|
+
- **Removed** `InterceptorPlugin` - No longer needed with file-based interception
|
|
36
|
+
- **Removed** `DefinePlugin` for `import.meta.graphCommerce` - Replaced with generated config
|
|
37
|
+
- **Removed** `@mui/*` alias rewrites - No longer required
|
|
38
|
+
- **Added** Turbopack loader rules for `.yaml`, `.yml`, and `.po` files
|
|
39
|
+
- **Added** `serverExternalPackages` for all `@whatwg-node/*` packages
|
|
40
|
+
- **Added** `optimizePackageImports` for better bundle optimization
|
|
41
|
+
- **Added** `images.qualities: [52, 75]` for Next.js image optimization
|
|
42
|
+
|
|
43
|
+
### 📦 Lingui Configuration
|
|
44
|
+
|
|
45
|
+
- **Renamed** `lingui.config.js` → `lingui.config.ts` with TypeScript support
|
|
46
|
+
- **Updated** `@graphcommerce/lingui-next/config` to TypeScript with proper exports
|
|
47
|
+
- **Simplified** formatter options
|
|
48
|
+
|
|
49
|
+
### ⚛️ React 19 & Next.js 16 Compatibility
|
|
50
|
+
|
|
51
|
+
- Updated `RefObject<T>` types for React 19 (now includes `null` by default)
|
|
52
|
+
- Replaced deprecated `React.VFC` with `React.FC`
|
|
53
|
+
- Fixed `useRef` calls to require explicit initial values
|
|
54
|
+
- Updated `MutableRefObject` usage in `framer-scroller`
|
|
55
|
+
|
|
56
|
+
### 📋 ESLint 9 Flat Config
|
|
57
|
+
|
|
58
|
+
- Migrated from legacy `.eslintrc` to new flat config format (`eslint.config.mjs`)
|
|
59
|
+
- Updated `@typescript-eslint/*` packages to v8
|
|
60
|
+
- Fixed AST selector for `SxProps` rule (`typeParameters` → `typeArguments`)
|
|
61
|
+
|
|
62
|
+
### 🔄 Apollo Client
|
|
63
|
+
|
|
64
|
+
- Fixed deprecated `name` option → `clientAwareness: { name: 'ssr' }`
|
|
65
|
+
- Updated error handling types to accept `ApolloError | null | undefined`
|
|
66
|
+
|
|
67
|
+
### ⚠️ Breaking Changes
|
|
68
|
+
|
|
69
|
+
- **Node.js 24.x not supported** - Restricted to `>=20 <24.0.0` due to [nodejs/undici#4290](https://github.com/nodejs/undici/issues/4290)
|
|
70
|
+
- **Interceptor files changed** - Original components now at `.original.tsx`
|
|
71
|
+
- **Config access changed** - Use generated config values instead of `import.meta.graphCommerce`
|
|
72
|
+
- **ESLint config format** - Must use flat config (`eslint.config.mjs`)
|
|
73
|
+
- **Lingui config** - Rename `lingui.config.js` to `lingui.config.ts`
|
|
74
|
+
|
|
75
|
+
### 🗑️ Removed
|
|
76
|
+
|
|
77
|
+
- `InterceptorPlugin` webpack plugin
|
|
78
|
+
- `configToImportMeta` utility
|
|
79
|
+
- Webpack `DefinePlugin` usage for config
|
|
80
|
+
- `@mui/*` modern alias rewrites
|
|
81
|
+
- Debug plugins (`CircularDependencyPlugin`, `DuplicatesPlugin`) ([@paales](https://github.com/paales))
|
|
82
|
+
|
|
83
|
+
## 9.1.0-canary.55
|
|
84
|
+
|
|
85
|
+
### Patch Changes
|
|
86
|
+
|
|
87
|
+
- [#2539](https://github.com/graphcommerce-org/graphcommerce/pull/2539) [`87fc3c2`](https://github.com/graphcommerce-org/graphcommerce/commit/87fc3c28165c7c66b48882b0f044bbc9b63b9846) - Created new Tabs and TabItem component to be used for MultiCart setup ([@paales](https://github.com/paales))
|
|
88
|
+
|
|
89
|
+
- [#2539](https://github.com/graphcommerce-org/graphcommerce/pull/2539) [`286a20e`](https://github.com/graphcommerce-org/graphcommerce/commit/286a20e01f2a565f058415fa1c8dfbb2eeb3163b) - Added an OverlayCloseButton and implemented it for various locations. ([@paales](https://github.com/paales))
|
|
90
|
+
|
|
91
|
+
- [#2539](https://github.com/graphcommerce-org/graphcommerce/pull/2539) [`65dcefb`](https://github.com/graphcommerce-org/graphcommerce/commit/65dcefb8740166fd5df662e0e895c65d70273393) - Solve hydration error because multiple literals could be in a DateTimeFormat ([@paales](https://github.com/paales))
|
|
92
|
+
|
|
93
|
+
- [#2539](https://github.com/graphcommerce-org/graphcommerce/pull/2539) [`88fd114`](https://github.com/graphcommerce-org/graphcommerce/commit/88fd11485f9368e79d277fa45942e58214f794a6) - Created a LayoutOverlayHeader2 that does not support any floating modes or something and thus is simpler to customize. ([@paales](https://github.com/paales))
|
|
94
|
+
|
|
95
|
+
- [#2539](https://github.com/graphcommerce-org/graphcommerce/pull/2539) [`23793aa`](https://github.com/graphcommerce-org/graphcommerce/commit/23793aab26455b1bea0d1b3b37c96a228b656bc4) - Prevent excessive rerender when multiple images with the same url are in a product ([@paales](https://github.com/paales))
|
|
96
|
+
|
|
97
|
+
- [#2539](https://github.com/graphcommerce-org/graphcommerce/pull/2539) [`a419257`](https://github.com/graphcommerce-org/graphcommerce/commit/a4192571eb2332630ba3d103f61ff69dac8b2e5c) - Solve issue where the sidebar wasn't 100% width on the PDP on mobile ([@paales](https://github.com/paales))
|
|
98
|
+
|
|
3
99
|
## 9.1.0-canary.54
|
|
4
100
|
|
|
5
101
|
## 9.1.0-canary.53
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
unstable_usePreventScroll as usePreventScroll,
|
|
11
11
|
} from '@graphcommerce/framer-scroller'
|
|
12
12
|
import { dvh } from '@graphcommerce/framer-utils'
|
|
13
|
+
import { sidebarGallery } from '@graphcommerce/next-config/config'
|
|
13
14
|
import type { SxProps, Theme } from '@mui/material'
|
|
14
15
|
import { Box, Fab, styled, Unstable_TrapFocus as TrapFocus, useTheme } from '@mui/material'
|
|
15
16
|
import { m, useDomEvent, useMotionValue } from 'framer-motion'
|
|
@@ -242,7 +243,8 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
242
243
|
>
|
|
243
244
|
{images.map((image, idx) => (
|
|
244
245
|
<MotionImageAspect
|
|
245
|
-
|
|
246
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
247
|
+
key={idx}
|
|
246
248
|
layout
|
|
247
249
|
layoutDependency={zoomed}
|
|
248
250
|
src={image.src}
|
|
@@ -349,8 +351,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
349
351
|
},
|
|
350
352
|
}}
|
|
351
353
|
>
|
|
352
|
-
{
|
|
353
|
-
'THUMBNAILS_BOTTOM' ? (
|
|
354
|
+
{sidebarGallery?.paginationVariant === 'THUMBNAILS_BOTTOM' ? (
|
|
354
355
|
<ScrollerThumbnails layoutDependency={zoomed} images={images} />
|
|
355
356
|
) : (
|
|
356
357
|
<ScrollerDots layout />
|
|
@@ -362,7 +363,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
362
363
|
className={classes.sidebarWrapper}
|
|
363
364
|
sx={[
|
|
364
365
|
{
|
|
365
|
-
width: sidebarSize,
|
|
366
|
+
width: { xs: '100%', md: sidebarSize },
|
|
366
367
|
gridArea: 'right',
|
|
367
368
|
boxSizing: 'content-box',
|
|
368
369
|
display: 'grid',
|
|
@@ -19,8 +19,9 @@ export function DateTimeFormat(props: DateTimeFormatProps) {
|
|
|
19
19
|
return (
|
|
20
20
|
<Box component='span' className='DateTimeFormat' suppressHydrationWarning sx={sx}>
|
|
21
21
|
{dateValue &&
|
|
22
|
-
formatter.formatToParts(dateValue).map((part) => (
|
|
23
|
-
|
|
22
|
+
formatter.formatToParts(dateValue).map((part, index) => (
|
|
23
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
24
|
+
<span className={part.type} key={`${part.type}-${index}`} suppressHydrationWarning>
|
|
24
25
|
{part.value}
|
|
25
26
|
</span>
|
|
26
27
|
))}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
usePrevUp,
|
|
5
5
|
useUp,
|
|
6
6
|
} from '@graphcommerce/framer-next-pages'
|
|
7
|
-
import {
|
|
7
|
+
import { t } from '@lingui/core/macro'
|
|
8
8
|
import type { SxProps, Theme } from '@mui/material'
|
|
9
9
|
import { Box } from '@mui/material'
|
|
10
10
|
import { useRouter } from 'next/router'
|
|
@@ -55,7 +55,7 @@ export function LayoutHeaderBack(props: BackProps) {
|
|
|
55
55
|
const backIcon = <IconSvg src={iconChevronLeft} size='medium' />
|
|
56
56
|
const canClickBack = backSteps > 0 && path !== prevUp?.href && !disableBackNavigation
|
|
57
57
|
|
|
58
|
-
let label =
|
|
58
|
+
let label = t`Back`
|
|
59
59
|
if (up?.href === path && up?.title) label = up.title
|
|
60
60
|
if (prevUp?.href === path && prevUp?.title) label = prevUp.title
|
|
61
61
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useGo, usePageContext } from '@graphcommerce/framer-next-pages'
|
|
2
|
-
import {
|
|
3
|
-
import { Fab } from '@mui/material'
|
|
2
|
+
import { t } from '@lingui/core/macro'
|
|
3
|
+
import { Fab, type ButtonProps } from '@mui/material'
|
|
4
4
|
import { useState } from 'react'
|
|
5
|
+
import { Button } from '../../Button'
|
|
6
|
+
import type { FabProps } from '../../Fab'
|
|
5
7
|
import { iconClose } from '../../icons'
|
|
6
8
|
import { IconSvg, useIconSvgSize } from '../../IconSvg'
|
|
7
9
|
import { useFabSize } from '../../Theme'
|
|
8
10
|
|
|
9
11
|
export type LayoutHeaderCloseProps = {
|
|
10
12
|
onClose?: () => void
|
|
13
|
+
size?: FabProps['size']
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export function useShowClose() {
|
|
@@ -16,31 +19,31 @@ export function useShowClose() {
|
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
export function LayoutHeaderClose(props: LayoutHeaderCloseProps) {
|
|
19
|
-
const { onClose } = props
|
|
22
|
+
const { onClose, size = 'responsive' } = props
|
|
20
23
|
const { closeSteps } = usePageContext()
|
|
21
24
|
const [disabled, setDisabled] = useState(false)
|
|
22
25
|
const go = useGo(closeSteps * -1)
|
|
23
26
|
const onClick = () => {
|
|
24
27
|
setDisabled(true)
|
|
25
|
-
|
|
26
28
|
return onClose ? onClose() : go()
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
const fabSize = useFabSize(
|
|
30
|
-
const svgSize = useIconSvgSize('large')
|
|
31
|
+
const fabSize = useFabSize(size)
|
|
32
|
+
const svgSize = useIconSvgSize(size === 'large' ? 'large' : 'medium')
|
|
31
33
|
|
|
32
34
|
return (
|
|
33
35
|
<Fab
|
|
34
36
|
onClick={onClick}
|
|
37
|
+
className='LayoutHeaderClose-root'
|
|
35
38
|
sx={{
|
|
36
39
|
boxShadow: 'none',
|
|
37
40
|
marginLeft: `calc((${fabSize} - ${svgSize}) * -0.5)`,
|
|
38
41
|
marginRight: `calc((${fabSize} - ${svgSize}) * -0.5)`,
|
|
39
42
|
background: 'none',
|
|
40
43
|
}}
|
|
41
|
-
size=
|
|
44
|
+
size={size}
|
|
42
45
|
disabled={disabled}
|
|
43
|
-
aria-label={
|
|
46
|
+
aria-label={t`Close`}
|
|
44
47
|
>
|
|
45
48
|
<IconSvg src={iconClose} size='large' />
|
|
46
49
|
</Fab>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
2
|
+
import { Box } from '@mui/material'
|
|
3
|
+
import React, { useRef } from 'react'
|
|
4
|
+
import type { BackProps } from '../../Layout/components/LayoutHeaderBack'
|
|
5
|
+
import { LayoutHeaderBack, useShowBack } from '../../Layout/components/LayoutHeaderBack'
|
|
6
|
+
import { LayoutHeaderClose, useShowClose } from '../../Layout/components/LayoutHeaderClose'
|
|
7
|
+
import { extendableComponent } from '../../Styles'
|
|
8
|
+
import { sxx } from '../../utils/sxx'
|
|
9
|
+
|
|
10
|
+
export type LayoutOverlayHeader2Props = Pick<BackProps, 'disableBackNavigation'> & {
|
|
11
|
+
/** Main content to display in the center */
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
|
|
14
|
+
disableChildrenPadding?: boolean
|
|
15
|
+
|
|
16
|
+
/** Button to display on the left side of the title */
|
|
17
|
+
primary?: React.ReactNode
|
|
18
|
+
|
|
19
|
+
/** Button to display on the right side of the title */
|
|
20
|
+
secondary?: React.ReactNode
|
|
21
|
+
|
|
22
|
+
/** Hide the back button */
|
|
23
|
+
hideBackButton?: boolean
|
|
24
|
+
|
|
25
|
+
/** Justify alignment for center content */
|
|
26
|
+
justify?: 'start' | 'center' | 'end'
|
|
27
|
+
|
|
28
|
+
/** Custom styles for the root element */
|
|
29
|
+
sx?: SxProps<Theme>
|
|
30
|
+
|
|
31
|
+
closeLocation?: 'left' | 'right'
|
|
32
|
+
|
|
33
|
+
size?: 'small' | 'responsive'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const name = 'LayoutOverlayHeader2'
|
|
37
|
+
const parts = ['root', 'bg', 'content', 'left', 'center', 'right'] as const
|
|
38
|
+
|
|
39
|
+
type State = {
|
|
40
|
+
left: boolean
|
|
41
|
+
right: boolean
|
|
42
|
+
childrenPadding: boolean
|
|
43
|
+
size: 'small' | 'responsive'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { withState } = extendableComponent<State, typeof name, typeof parts>(name, parts)
|
|
47
|
+
|
|
48
|
+
export const LayoutOverlayHeader2 = React.memo<LayoutOverlayHeader2Props>((props) => {
|
|
49
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
children,
|
|
53
|
+
hideBackButton = false,
|
|
54
|
+
primary,
|
|
55
|
+
secondary,
|
|
56
|
+
justify = 'center',
|
|
57
|
+
closeLocation = 'right',
|
|
58
|
+
sx = [],
|
|
59
|
+
disableBackNavigation,
|
|
60
|
+
disableChildrenPadding = false,
|
|
61
|
+
size = 'responsive',
|
|
62
|
+
} = props
|
|
63
|
+
|
|
64
|
+
const showBack = useShowBack() && !hideBackButton
|
|
65
|
+
const showClose = useShowClose()
|
|
66
|
+
|
|
67
|
+
const close = showClose && <LayoutHeaderClose size={size === 'small' ? 'small' : 'responsive'} />
|
|
68
|
+
const back = showBack && <LayoutHeaderBack disableBackNavigation={disableBackNavigation} />
|
|
69
|
+
|
|
70
|
+
let left = secondary
|
|
71
|
+
let right = primary
|
|
72
|
+
|
|
73
|
+
if (back && !secondary) left = back
|
|
74
|
+
|
|
75
|
+
// Handle close button positioning based on closeLocation
|
|
76
|
+
if (close && closeLocation === 'left' && !left) left = close
|
|
77
|
+
if (close && closeLocation === 'right' && !right) right = close
|
|
78
|
+
if (close && closeLocation === 'right' && !left && right) left = close
|
|
79
|
+
if (!left && !right && !children) return null
|
|
80
|
+
|
|
81
|
+
const classes = withState({
|
|
82
|
+
left: !!left,
|
|
83
|
+
right: !!right,
|
|
84
|
+
childrenPadding: !disableChildrenPadding,
|
|
85
|
+
size,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Box
|
|
90
|
+
className={classes.root}
|
|
91
|
+
sx={sxx(
|
|
92
|
+
(theme) => ({
|
|
93
|
+
zIndex: children ? theme.zIndex.appBar : theme.zIndex.appBar - 2,
|
|
94
|
+
position: 'sticky',
|
|
95
|
+
left: 0,
|
|
96
|
+
right: 0,
|
|
97
|
+
top: 0,
|
|
98
|
+
marginTop: 0,
|
|
99
|
+
|
|
100
|
+
// Special positioning for bottom overlays
|
|
101
|
+
[theme.breakpoints.down('md')]: {
|
|
102
|
+
height: theme.appShell.headerHeightSm,
|
|
103
|
+
'.variantSmBottom.sizeSmFull &, .variantSmBottom.sizeSmMinimal &': {
|
|
104
|
+
top: `calc(${theme.appShell.headerHeightSm} * 0.5 * -1)`,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
[theme.breakpoints.up('md')]: {
|
|
108
|
+
height: theme.appShell.appBarHeightMd,
|
|
109
|
+
'.variantMdBottom.sizeMdFull &, .variantMdBottom.sizeMdMinimal &': {
|
|
110
|
+
top: `calc(${theme.appShell.appBarHeightMd} * 0.5 * -1)`,
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
'&.sizeSmall': {
|
|
114
|
+
height: theme.appShell.headerHeightSm,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
sx,
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
<Box
|
|
122
|
+
className={classes.content}
|
|
123
|
+
ref={ref}
|
|
124
|
+
sx={sxx((theme) => ({
|
|
125
|
+
position: 'absolute',
|
|
126
|
+
inset: 0,
|
|
127
|
+
width: '100%',
|
|
128
|
+
display: 'grid',
|
|
129
|
+
alignItems: 'center',
|
|
130
|
+
gap: theme.spacings.xs,
|
|
131
|
+
height: theme.appShell.headerHeightSm,
|
|
132
|
+
[theme.breakpoints.up('md')]: {
|
|
133
|
+
height: theme.appShell.appBarHeightMd,
|
|
134
|
+
'&.sizeSmall': {
|
|
135
|
+
height: theme.appShell.headerHeightSm,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
// Default: center only
|
|
140
|
+
gridTemplateAreas: '"center"',
|
|
141
|
+
gridTemplateColumns: '1fr',
|
|
142
|
+
|
|
143
|
+
// Left only
|
|
144
|
+
'&.left:not(.right)': {
|
|
145
|
+
gridTemplateAreas: '"left center"',
|
|
146
|
+
gridTemplateColumns: justify === 'center' ? '1fr max-content' : 'auto 1fr',
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Right only
|
|
150
|
+
'&.right:not(.left)': {
|
|
151
|
+
gridTemplateAreas: '"center right"',
|
|
152
|
+
gridTemplateColumns: justify === 'center' ? 'max-content 1fr' : '1fr auto',
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
// Both left and right
|
|
156
|
+
'&.left.right': {
|
|
157
|
+
gridTemplateAreas: '"left center right"',
|
|
158
|
+
gridTemplateColumns: justify === 'center' ? '1fr max-content 1fr' : 'auto 1fr auto',
|
|
159
|
+
},
|
|
160
|
+
}))}
|
|
161
|
+
>
|
|
162
|
+
{left && (
|
|
163
|
+
<Box
|
|
164
|
+
className={classes.left}
|
|
165
|
+
sx={(theme) => ({
|
|
166
|
+
pl: theme.spacings.sm,
|
|
167
|
+
display: 'grid',
|
|
168
|
+
gridAutoFlow: 'column',
|
|
169
|
+
gap: theme.spacings.sm,
|
|
170
|
+
gridArea: 'left',
|
|
171
|
+
justifyContent: 'start',
|
|
172
|
+
})}
|
|
173
|
+
>
|
|
174
|
+
{left}
|
|
175
|
+
</Box>
|
|
176
|
+
)}
|
|
177
|
+
|
|
178
|
+
<Box
|
|
179
|
+
className={classes.center}
|
|
180
|
+
sx={(theme) => ({
|
|
181
|
+
display: 'grid',
|
|
182
|
+
gridAutoFlow: 'column',
|
|
183
|
+
gap: theme.spacings.sm,
|
|
184
|
+
gridArea: 'center',
|
|
185
|
+
overflow: 'hidden',
|
|
186
|
+
height: '100%',
|
|
187
|
+
'&.childrenPadding:not(.right)': { pr: theme.spacings.sm },
|
|
188
|
+
'&.childrenPadding:not(.left)': { pl: theme.spacings.sm },
|
|
189
|
+
})}
|
|
190
|
+
>
|
|
191
|
+
{children}
|
|
192
|
+
</Box>
|
|
193
|
+
|
|
194
|
+
{right && (
|
|
195
|
+
<Box
|
|
196
|
+
className={classes.right}
|
|
197
|
+
sx={(theme) => ({
|
|
198
|
+
'& > *': { width: 'min-content' },
|
|
199
|
+
display: 'grid',
|
|
200
|
+
gridAutoFlow: 'column',
|
|
201
|
+
gap: theme.spacings.sm,
|
|
202
|
+
gridArea: 'right',
|
|
203
|
+
justifyContent: 'end',
|
|
204
|
+
pr: theme.spacings.sm,
|
|
205
|
+
})}
|
|
206
|
+
>
|
|
207
|
+
{right}
|
|
208
|
+
</Box>
|
|
209
|
+
)}
|
|
210
|
+
</Box>
|
|
211
|
+
</Box>
|
|
212
|
+
)
|
|
213
|
+
})
|
package/LayoutOverlay/index.ts
CHANGED
|
@@ -39,8 +39,8 @@ export const NavigationList = React.memo<NavigationItemsProps>((props) => {
|
|
|
39
39
|
{items.map((item, idx) => (
|
|
40
40
|
<NavigationItem
|
|
41
41
|
NavigationList={NavigationList}
|
|
42
|
-
key={item.id}
|
|
43
42
|
{...item}
|
|
43
|
+
key={item.id ?? idx}
|
|
44
44
|
parentPath={parentPath}
|
|
45
45
|
idx={idx}
|
|
46
46
|
first={idx === 0}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dvw, useMotionSelector, useMotionValueValue } from '@graphcommerce/framer-utils'
|
|
2
|
-
import {
|
|
2
|
+
import { t } from '@lingui/core/macro'
|
|
3
3
|
import type { SxProps, Theme } from '@mui/material'
|
|
4
4
|
import { Box, Fab, styled, useEventCallback, useTheme } from '@mui/material'
|
|
5
5
|
import { m } from 'framer-motion'
|
|
@@ -160,7 +160,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
160
160
|
onClick={handleOnBack}
|
|
161
161
|
sx={{ boxShadow: 'none', my: fabMarginY }}
|
|
162
162
|
size='responsive'
|
|
163
|
-
aria-label={
|
|
163
|
+
aria-label={t`Back`}
|
|
164
164
|
>
|
|
165
165
|
<IconSvg src={iconChevronLeft} size='large' aria-hidden />
|
|
166
166
|
</Fab>
|
|
@@ -173,7 +173,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
173
173
|
onClick={handleClose}
|
|
174
174
|
sx={{ boxShadow: 'none', my: fabMarginY }}
|
|
175
175
|
size='responsive'
|
|
176
|
-
aria-label={
|
|
176
|
+
aria-label={t`Close`}
|
|
177
177
|
ref={a11yFocusRef}
|
|
178
178
|
>
|
|
179
179
|
<IconSvg src={iconClose} size='large' aria-hidden />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMotionValueValue } from '@graphcommerce/framer-utils'
|
|
2
|
-
import { Trans } from '@lingui/react'
|
|
2
|
+
import { Trans } from '@lingui/react/macro'
|
|
3
3
|
import type {
|
|
4
4
|
NavigationNodeButton,
|
|
5
5
|
NavigationNodeHref,
|
|
@@ -35,6 +35,6 @@ function findCurrent(
|
|
|
35
35
|
export function NavigationTitle() {
|
|
36
36
|
const { selection, items } = useNavigation()
|
|
37
37
|
return (
|
|
38
|
-
<>{useMotionValueValue(selection, (v) => findCurrent(items, v))?.name ?? <Trans
|
|
38
|
+
<>{useMotionValueValue(selection, (v) => findCurrent(items, v))?.name ?? <Trans>Menu</Trans>}</>
|
|
39
39
|
)
|
|
40
40
|
}
|
|
@@ -160,7 +160,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
160
160
|
() => (match.up('md') ? variantMd : variantSm),
|
|
161
161
|
[match, variantMd, variantSm],
|
|
162
162
|
)
|
|
163
|
-
const prevVariant = useRef<LayoutOverlayVariant>()
|
|
163
|
+
const prevVariant = useRef<LayoutOverlayVariant | null>(null)
|
|
164
164
|
|
|
165
165
|
const openClosePositions = useCallback((): {
|
|
166
166
|
open: [number, number]
|
|
@@ -418,7 +418,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
418
418
|
return (
|
|
419
419
|
<>
|
|
420
420
|
<MotionDiv
|
|
421
|
-
inert={active
|
|
421
|
+
inert={active}
|
|
422
422
|
className={classes.backdrop}
|
|
423
423
|
style={{ opacity: positions.open.visible }}
|
|
424
424
|
sx={[
|
|
@@ -440,7 +440,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
440
440
|
]}
|
|
441
441
|
/>
|
|
442
442
|
<Scroller
|
|
443
|
-
inert={disableInert || active ?
|
|
443
|
+
inert={disableInert || active ? false : true}
|
|
444
444
|
className={`${classes.scroller} ${className ?? ''}`}
|
|
445
445
|
grid={false}
|
|
446
446
|
onClick={onClickAway}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useGo, usePageContext } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import { t } from '@lingui/core/macro'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { Button, type ButtonProps } from '../../Button'
|
|
5
|
+
|
|
6
|
+
export function OverlayCloseButton(props: ButtonProps) {
|
|
7
|
+
const { disabled, ...buttonProps } = props
|
|
8
|
+
const { closeSteps } = usePageContext()
|
|
9
|
+
const [isDisabled, setIsDisabled] = useState(disabled)
|
|
10
|
+
const go = useGo(closeSteps * -1)
|
|
11
|
+
|
|
12
|
+
const onClick = () => {
|
|
13
|
+
setIsDisabled(true)
|
|
14
|
+
go()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Button
|
|
19
|
+
onClick={onClick}
|
|
20
|
+
disabled={isDisabled}
|
|
21
|
+
aria-label={t`Close overlay`}
|
|
22
|
+
{...buttonProps}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LayoutTitle, type TitleProps } from '../../Layout'
|
|
2
|
+
import { LayoutHeaderClose } from '../../Layout/components/LayoutHeaderClose'
|
|
3
|
+
import { LayoutOverlayHeader2, type LayoutOverlayHeader2Props } from '../../LayoutOverlay'
|
|
4
|
+
|
|
5
|
+
export type OverlayHeader2Props = Omit<LayoutOverlayHeader2Props, 'hideBackButton'> & {
|
|
6
|
+
onClose: () => void
|
|
7
|
+
icon?: TitleProps['icon']
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function OverlayHeader2(props: OverlayHeader2Props) {
|
|
11
|
+
const { children, onClose, primary, secondary, icon, ...rest } = props
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<LayoutOverlayHeader2
|
|
15
|
+
hideBackButton
|
|
16
|
+
size='small'
|
|
17
|
+
primary={primary ?? <LayoutHeaderClose onClose={onClose} size='small' />}
|
|
18
|
+
secondary={primary ? <LayoutHeaderClose onClose={onClose} size='small' /> : secondary}
|
|
19
|
+
{...rest}
|
|
20
|
+
>
|
|
21
|
+
<LayoutTitle size='small' component='span' icon={icon}>
|
|
22
|
+
{children}
|
|
23
|
+
</LayoutTitle>
|
|
24
|
+
</LayoutOverlayHeader2>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export * from './OverlayBase'
|
|
2
1
|
export * from './Overlay'
|
|
3
|
-
export * from './
|
|
2
|
+
export * from './OverlayBase'
|
|
3
|
+
export * from './OverlayCloseButton'
|
|
4
4
|
export * from './OverlayHeader'
|
|
5
|
+
export * from './OverlayHeader2'
|
|
6
|
+
export * from './OverlayStickyBottom'
|