@graphcommerce/next-ui 4.12.0 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ActionCard/ActionCard.tsx +2 -2
- package/ActionCard/ActionCardList.tsx +107 -76
- package/ActionCard/ActionCardListForm.tsx +2 -1
- package/Blog/BlogListItem/BlogListItem.tsx +1 -1
- package/CHANGELOG.md +44 -0
- package/FramerScroller/SidebarGallery.tsx +12 -1
- package/Layout/components/LayoutHeader.tsx +1 -1
- package/Layout/components/LayoutHeaderContent.tsx +9 -4
- package/LayoutDefault/components/LayoutDefault.tsx +5 -3
- package/LayoutOverlay/components/LayoutOverlay.tsx +25 -6
- package/LayoutParts/DesktopNavBar.tsx +1 -1
- package/LayoutParts/DesktopNavBarItem.tsx +38 -4
- package/LayoutParts/MenuFabSecondaryItem.tsx +2 -1
- package/Navigation/components/NavigationFab.tsx +106 -0
- package/Navigation/components/NavigationItem.tsx +147 -0
- package/Navigation/components/NavigationList.tsx +50 -0
- package/Navigation/components/NavigationOverlay.tsx +237 -0
- package/Navigation/components/NavigationProvider.tsx +66 -0
- package/Navigation/hooks/useNavigation.ts +58 -0
- package/Navigation/index.ts +4 -0
- package/Overlay/components/Overlay.tsx +61 -0
- package/{LayoutOverlay/components/LayoutOverlayBase.tsx → Overlay/components/OverlayBase.tsx} +29 -15
- package/Overlay/components/index.ts +2 -0
- package/{LayoutOverlay → Overlay}/hooks/useOverlayPosition.ts +0 -0
- package/Overlay/index.ts +1 -0
- package/PageMeta/PageMeta.tsx +4 -6
- package/Theme/DarkLightModeThemeProvider.tsx +1 -5
- package/index.ts +7 -3
- package/package.json +12 -10
- package/utils/cookie.ts +33 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ScrollerProvider, ScrollSnapType } from '@graphcommerce/framer-scroller'
|
|
2
|
+
import { Box } from '@mui/material'
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import type { SetOptional } from 'type-fest'
|
|
5
|
+
import { OverlayBase, LayoutOverlayBaseProps } from './OverlayBase'
|
|
6
|
+
|
|
7
|
+
export type OverlayProps = Omit<
|
|
8
|
+
SetOptional<LayoutOverlayBaseProps, 'variantSm' | 'variantMd'>,
|
|
9
|
+
'direction' | 'offsetPageY' | 'isPresent' | 'safeToRemove'
|
|
10
|
+
>
|
|
11
|
+
|
|
12
|
+
export function Overlay(props: OverlayProps) {
|
|
13
|
+
const { children, variantSm = 'bottom', variantMd = 'right', active, ...otherProps } = props
|
|
14
|
+
|
|
15
|
+
const scrollSnapTypeSm: ScrollSnapType =
|
|
16
|
+
variantSm === 'left' || variantSm === 'right' ? 'inline mandatory' : 'block proximity'
|
|
17
|
+
const scrollSnapTypeMd: ScrollSnapType =
|
|
18
|
+
variantMd === 'left' || variantMd === 'right' ? 'inline mandatory' : 'block proximity'
|
|
19
|
+
|
|
20
|
+
// todo: The overlay is always present in the DOM and the initial scroll position is set to 0.
|
|
21
|
+
// This means in this case that the overlay is visisble. LayoutOverlayBase sets the scroll position to 320 with JS.
|
|
22
|
+
// This would cause the overlay to be visisble for a moment before the scroll position is set.
|
|
23
|
+
// The solution is to set the the first scroll-snap-align and scroll-snap-stop to the open position of the overlay.
|
|
24
|
+
// However: We have control of the LayoutOverlayBase, we do not have control of all the child components so that solution will not work..
|
|
25
|
+
const [loaded, setLoaded] = useState(false)
|
|
26
|
+
useEffect(() => setLoaded(true), [])
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Box
|
|
30
|
+
className='Overlay'
|
|
31
|
+
sx={{
|
|
32
|
+
position: 'fixed',
|
|
33
|
+
top: 0,
|
|
34
|
+
left: 0,
|
|
35
|
+
transform: loaded ? undefined : 'translateX(-200vw)',
|
|
36
|
+
pointerEvents: active ? undefined : 'none',
|
|
37
|
+
right: 0,
|
|
38
|
+
bottom: 0,
|
|
39
|
+
zIndex: 'drawer',
|
|
40
|
+
'& .LayoutOverlayBase-overlayPane': {
|
|
41
|
+
boxShadow: active ? undefined : 0,
|
|
42
|
+
},
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
<ScrollerProvider scrollSnapTypeSm={scrollSnapTypeSm} scrollSnapTypeMd={scrollSnapTypeMd}>
|
|
46
|
+
<OverlayBase
|
|
47
|
+
offsetPageY={0}
|
|
48
|
+
variantMd={variantMd}
|
|
49
|
+
variantSm={variantSm}
|
|
50
|
+
direction={1}
|
|
51
|
+
active={active}
|
|
52
|
+
isPresent={active}
|
|
53
|
+
safeToRemove={undefined}
|
|
54
|
+
{...otherProps}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</OverlayBase>
|
|
58
|
+
</ScrollerProvider>
|
|
59
|
+
</Box>
|
|
60
|
+
)
|
|
61
|
+
}
|
package/{LayoutOverlay/components/LayoutOverlayBase.tsx → Overlay/components/OverlayBase.tsx}
RENAMED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useGo, usePageContext, useScrollOffset } from '@graphcommerce/framer-next-pages'
|
|
2
1
|
import { Scroller, useScrollerContext, useScrollTo } from '@graphcommerce/framer-scroller'
|
|
3
2
|
import {
|
|
4
3
|
clientSizeCssVar,
|
|
@@ -6,7 +5,7 @@ import {
|
|
|
6
5
|
useIsomorphicLayoutEffect,
|
|
7
6
|
} from '@graphcommerce/framer-utils'
|
|
8
7
|
import { Box, styled, SxProps, Theme, useTheme, useThemeProps } from '@mui/material'
|
|
9
|
-
import { m, useDomEvent, useMotionValue,
|
|
8
|
+
import { m, useDomEvent, useMotionValue, useTransform } from 'framer-motion'
|
|
10
9
|
import React, { useCallback, useEffect, useRef } from 'react'
|
|
11
10
|
import { LayoutProvider } from '../../Layout/components/LayoutProvider'
|
|
12
11
|
import { ExtendableComponent, extendableComponent } from '../../Styles'
|
|
@@ -35,6 +34,12 @@ export type LayoutOverlayBaseProps = {
|
|
|
35
34
|
className?: string
|
|
36
35
|
sx?: SxProps<Theme>
|
|
37
36
|
sxBackdrop?: SxProps<Theme>
|
|
37
|
+
active: boolean
|
|
38
|
+
direction: 1 | -1
|
|
39
|
+
onClosed: () => void
|
|
40
|
+
offsetPageY: number
|
|
41
|
+
isPresent: boolean
|
|
42
|
+
safeToRemove: (() => void) | null | undefined
|
|
38
43
|
} & StyleProps &
|
|
39
44
|
OverridableProps
|
|
40
45
|
|
|
@@ -61,7 +66,7 @@ const clearScrollLock = () => {
|
|
|
61
66
|
document.body.style.overflow = ''
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
export function
|
|
69
|
+
export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
65
70
|
const props = useThemeProps({ name, props: incommingProps })
|
|
66
71
|
|
|
67
72
|
const {
|
|
@@ -75,6 +80,12 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
75
80
|
justifyMd = 'stretch',
|
|
76
81
|
sx = [],
|
|
77
82
|
sxBackdrop = [],
|
|
83
|
+
active,
|
|
84
|
+
onClosed,
|
|
85
|
+
direction,
|
|
86
|
+
offsetPageY,
|
|
87
|
+
isPresent,
|
|
88
|
+
safeToRemove,
|
|
78
89
|
} = props
|
|
79
90
|
|
|
80
91
|
const th = useTheme()
|
|
@@ -88,12 +99,8 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
88
99
|
const { scrollerRef, snap } = useScrollerContext()
|
|
89
100
|
const positions = useOverlayPosition()
|
|
90
101
|
const scrollTo = useScrollTo()
|
|
91
|
-
const [isPresent, safeToRemove] = usePresence()
|
|
92
102
|
const beforeRef = useRef<HTMLDivElement>(null)
|
|
93
103
|
|
|
94
|
-
const { closeSteps, active, direction } = usePageContext()
|
|
95
|
-
const close = useGo(closeSteps * -1)
|
|
96
|
-
|
|
97
104
|
const position = useMotionValue<OverlayPosition>(OverlayPosition.UNOPENED)
|
|
98
105
|
|
|
99
106
|
const classes = withState({ variantSm, variantMd, sizeSm, sizeMd, justifySm, justifyMd })
|
|
@@ -105,7 +112,15 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
105
112
|
// When the component is mounted, we need to set the initial position of the overlay.
|
|
106
113
|
useIsomorphicLayoutEffect(() => {
|
|
107
114
|
const scroller = scrollerRef.current
|
|
108
|
-
|
|
115
|
+
|
|
116
|
+
if (!scroller) return undefined
|
|
117
|
+
|
|
118
|
+
if (!isPresent && position.get() === OverlayPosition.UNOPENED) {
|
|
119
|
+
scroller.scrollLeft = positions.closed.x.get()
|
|
120
|
+
scroller.scrollTop = positions.closed.y.get()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!isPresent) return undefined
|
|
109
124
|
|
|
110
125
|
const open = { x: positions.open.x.get(), y: positions.open.y.get() }
|
|
111
126
|
|
|
@@ -148,7 +163,7 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
148
163
|
|
|
149
164
|
// When the overlay is closed by navigating away, we're closing the overlay.
|
|
150
165
|
useEffect(() => {
|
|
151
|
-
if (isPresent) return
|
|
166
|
+
if (isPresent || position.get() === OverlayPosition.UNOPENED) return
|
|
152
167
|
position.set(OverlayPosition.CLOSED)
|
|
153
168
|
clearScrollLock()
|
|
154
169
|
|
|
@@ -164,8 +179,8 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
164
179
|
if (position.get() !== OverlayPosition.OPENED) return
|
|
165
180
|
position.set(OverlayPosition.CLOSED)
|
|
166
181
|
clearScrollLock()
|
|
167
|
-
|
|
168
|
-
}, [
|
|
182
|
+
onClosed()
|
|
183
|
+
}, [onClosed, position])
|
|
169
184
|
|
|
170
185
|
// Handle escape key
|
|
171
186
|
const windowRef = useRef(typeof window !== 'undefined' ? window : null)
|
|
@@ -187,7 +202,6 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
187
202
|
}, [offsetY])
|
|
188
203
|
|
|
189
204
|
// Create the exact position for the LayoutProvider which offsets the top of the overlay
|
|
190
|
-
const offsetPageY = useScrollOffset().y
|
|
191
205
|
const scrollWithoffset = useTransform(
|
|
192
206
|
[scroll.y, positions.open.y, offsetY],
|
|
193
207
|
([y, openY, offsetYv]: number[]) => Math.max(0, y - openY - offsetYv + offsetPageY),
|
|
@@ -333,7 +347,6 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
333
347
|
marginTop: `calc(${smSpacingTop} * -1)`,
|
|
334
348
|
paddingTop: smSpacingTop,
|
|
335
349
|
},
|
|
336
|
-
|
|
337
350
|
'&.sizeSmFloating': {
|
|
338
351
|
padding: `${theme.page.vertical} ${theme.page.horizontal}`,
|
|
339
352
|
},
|
|
@@ -353,7 +366,8 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
353
366
|
},
|
|
354
367
|
})}
|
|
355
368
|
>
|
|
356
|
-
<
|
|
369
|
+
<MotionDiv
|
|
370
|
+
layout
|
|
357
371
|
className={classes.overlayPane}
|
|
358
372
|
sx={(theme) => ({
|
|
359
373
|
pointerEvents: 'all',
|
|
@@ -419,7 +433,7 @@ export function LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
419
433
|
})}
|
|
420
434
|
>
|
|
421
435
|
<LayoutProvider scroll={scrollWithoffset}>{children}</LayoutProvider>
|
|
422
|
-
</
|
|
436
|
+
</MotionDiv>
|
|
423
437
|
</Box>
|
|
424
438
|
</Scroller>
|
|
425
439
|
</>
|
|
File without changes
|
package/Overlay/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components'
|
package/PageMeta/PageMeta.tsx
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { usePageContext } from '@graphcommerce/framer-next-pages'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
getDomainLocale,
|
|
7
|
-
} from 'next/dist/shared/lib/router/router'
|
|
2
|
+
import { addBasePath } from 'next/dist/client/add-base-path'
|
|
3
|
+
import { addLocale } from 'next/dist/client/add-locale'
|
|
4
|
+
import { getDomainLocale } from 'next/dist/client/get-domain-locale'
|
|
5
|
+
import { resolveHref } from 'next/dist/shared/lib/router/router'
|
|
8
6
|
import Head from 'next/head'
|
|
9
7
|
import { useRouter } from 'next/router'
|
|
10
8
|
|
|
@@ -108,11 +108,7 @@ export function DarkLightModeMenuSecondaryItem(props: ListItemButtonProps) {
|
|
|
108
108
|
<IconSvg src={currentMode === 'light' ? iconMoon : iconSun} size='medium' />
|
|
109
109
|
</ListItemIcon>
|
|
110
110
|
<ListItemText>
|
|
111
|
-
{currentMode === 'light' ?
|
|
112
|
-
<Trans id='Switch to Dark Mode' />
|
|
113
|
-
) : (
|
|
114
|
-
<Trans id='Switch to Light Mode' />
|
|
115
|
-
)}
|
|
111
|
+
{currentMode === 'light' ? <Trans id='Dark Mode' /> : <Trans id='Light Mode' />}
|
|
116
112
|
</ListItemText>
|
|
117
113
|
</ListItemButton>
|
|
118
114
|
)
|
package/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export * from './ActionCard/ActionCardList'
|
|
2
1
|
export * from './ActionCard/ActionCard'
|
|
2
|
+
export * from './ActionCard/ActionCardList'
|
|
3
|
+
export * from './ActionCard/ActionCardListForm'
|
|
3
4
|
export * from './AnimatedRow/AnimatedRow'
|
|
4
5
|
export * from './Blog/BlogAuthor/BlogAuthor'
|
|
5
6
|
export * from './Blog/BlogContent/BlogContent'
|
|
@@ -22,6 +23,7 @@ export * from './Form/InputCheckmark'
|
|
|
22
23
|
export * from './FramerScroller'
|
|
23
24
|
export * from './FullPageMessage/FullPageMessage'
|
|
24
25
|
export * from './Highlight/Highlight'
|
|
26
|
+
export * from './hooks'
|
|
25
27
|
export * from './IconHeader/IconHeader'
|
|
26
28
|
export * from './icons'
|
|
27
29
|
export * from './IconSvg'
|
|
@@ -30,6 +32,8 @@ export * from './Layout'
|
|
|
30
32
|
export * from './LayoutDefault'
|
|
31
33
|
export * from './LayoutOverlay'
|
|
32
34
|
export * from './LayoutParts'
|
|
35
|
+
export * from './Navigation'
|
|
36
|
+
export * from './Overlay'
|
|
33
37
|
export * from './Page'
|
|
34
38
|
export * from './PageLoadIndicator/PageLoadIndicator'
|
|
35
39
|
export * from './PageMeta/PageMeta'
|
|
@@ -39,8 +43,8 @@ export * from './Row'
|
|
|
39
43
|
export * from './SectionContainer/SectionContainer'
|
|
40
44
|
export * from './SectionHeader/SectionHeader'
|
|
41
45
|
export * from './Separator/Separator'
|
|
42
|
-
export * from './Snackbar/MessageSnackbar'
|
|
43
46
|
export * from './Snackbar/ErrorSnackbar'
|
|
47
|
+
export * from './Snackbar/MessageSnackbar'
|
|
44
48
|
export * from './Snackbar/MessageSnackbarImpl'
|
|
45
49
|
export * from './StarRatingField/StarRatingField'
|
|
46
50
|
export * from './Stepper/Stepper'
|
|
@@ -50,6 +54,6 @@ export * from './Theme'
|
|
|
50
54
|
export * from './TimeAgo/TimeAgo'
|
|
51
55
|
export * from './ToggleButton/ToggleButton'
|
|
52
56
|
export * from './ToggleButtonGroup/ToggleButtonGroup'
|
|
53
|
-
export * from './hooks'
|
|
54
57
|
export * from './UspList/UspList'
|
|
55
58
|
export * from './UspList/UspListItem'
|
|
59
|
+
export * from './utils/cookie'
|
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": "4.
|
|
5
|
+
"version": "4.14.0",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@emotion/babel-preset-css-prop": "^11.2.0",
|
|
18
|
-
"@emotion/cache": "^11.
|
|
19
|
-
"@emotion/react": "^11.9.
|
|
18
|
+
"@emotion/cache": "^11.9.3",
|
|
19
|
+
"@emotion/react": "^11.9.3",
|
|
20
20
|
"@emotion/server": "^11.4.0",
|
|
21
|
-
"@emotion/styled": "^11.
|
|
22
|
-
"@graphcommerce/framer-next-pages": "3.2.
|
|
23
|
-
"@graphcommerce/framer-scroller": "2.1.
|
|
21
|
+
"@emotion/styled": "^11.9.3",
|
|
22
|
+
"@graphcommerce/framer-next-pages": "3.2.4",
|
|
23
|
+
"@graphcommerce/framer-scroller": "2.1.24",
|
|
24
24
|
"@graphcommerce/framer-utils": "3.1.4",
|
|
25
25
|
"@graphcommerce/image": "3.1.7",
|
|
26
|
-
"
|
|
26
|
+
"cookie": "^0.5.0",
|
|
27
|
+
"react-is": "^18.2.0",
|
|
27
28
|
"react-schemaorg": "^2.0.0",
|
|
28
29
|
"schema-dts": "^1.1.0"
|
|
29
30
|
},
|
|
@@ -38,12 +39,13 @@
|
|
|
38
39
|
"react-dom": "^18.0.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@graphcommerce/eslint-config-pwa": "^4.1.
|
|
42
|
+
"@graphcommerce/eslint-config-pwa": "^4.1.9",
|
|
42
43
|
"@graphcommerce/prettier-config-pwa": "^4.0.6",
|
|
43
|
-
"@graphcommerce/typescript-config-pwa": "^4.0.
|
|
44
|
+
"@graphcommerce/typescript-config-pwa": "^4.0.4",
|
|
44
45
|
"@playwright/test": "^1.21.1",
|
|
46
|
+
"@types/cookie": "^0.5.1",
|
|
45
47
|
"@types/react-is": "^17.0.3",
|
|
46
48
|
"type-fest": "^2.12.2",
|
|
47
|
-
"typescript": "4.7.
|
|
49
|
+
"typescript": "4.7.4"
|
|
48
50
|
}
|
|
49
51
|
}
|
package/utils/cookie.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { serialize, parse, CookieSerializeOptions } from 'cookie'
|
|
2
|
+
|
|
3
|
+
/** Read a cookie */
|
|
4
|
+
export function cookie(name: string): string | undefined
|
|
5
|
+
/** Set a cookie */
|
|
6
|
+
export function cookie(name: string, value: string, options?: CookieSerializeOptions): void
|
|
7
|
+
/** Delete a cookie */
|
|
8
|
+
export function cookie(name: string, value: null): void
|
|
9
|
+
/** Function to handle the three different cases */
|
|
10
|
+
export function cookie(name: string, value?: string | null, options?: CookieSerializeOptions) {
|
|
11
|
+
if (typeof window === 'undefined') {
|
|
12
|
+
return undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Read a cookie
|
|
16
|
+
if (typeof value === 'undefined') return parse(document.cookie)[name]
|
|
17
|
+
|
|
18
|
+
// Set a cookie
|
|
19
|
+
if (typeof value === 'string') {
|
|
20
|
+
const serialized = serialize(name, value, { path: '/', maxAge: 31536000, ...options })
|
|
21
|
+
document.cookie = serialized
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Delete a cookie
|
|
26
|
+
if (value === null) {
|
|
27
|
+
const serialized = serialize(name, '', { path: '/', maxAge: 0 })
|
|
28
|
+
document.cookie = serialized
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return undefined
|
|
33
|
+
}
|