@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.
@@ -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
+ }
@@ -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, usePresence, useTransform } from 'framer-motion'
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 LayoutOverlayBase(incommingProps: LayoutOverlayBaseProps) {
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
- if (!scroller || !isPresent) return undefined
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
- close()
168
- }, [close, position])
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
- <Box
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
- </Box>
436
+ </MotionDiv>
423
437
  </Box>
424
438
  </Scroller>
425
439
  </>
@@ -0,0 +1,2 @@
1
+ export * from './OverlayBase'
2
+ export * from './Overlay'
@@ -0,0 +1 @@
1
+ export * from './components'
@@ -1,10 +1,8 @@
1
1
  import { usePageContext } from '@graphcommerce/framer-next-pages'
2
- import {
3
- resolveHref,
4
- addBasePath,
5
- addLocale,
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.12.0",
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.7.1",
19
- "@emotion/react": "^11.9.0",
18
+ "@emotion/cache": "^11.9.3",
19
+ "@emotion/react": "^11.9.3",
20
20
  "@emotion/server": "^11.4.0",
21
- "@emotion/styled": "^11.6.0",
22
- "@graphcommerce/framer-next-pages": "3.2.3",
23
- "@graphcommerce/framer-scroller": "2.1.21",
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
- "react-is": "^18.1.0",
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.8",
42
+ "@graphcommerce/eslint-config-pwa": "^4.1.9",
42
43
  "@graphcommerce/prettier-config-pwa": "^4.0.6",
43
- "@graphcommerce/typescript-config-pwa": "^4.0.3",
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.3"
49
+ "typescript": "4.7.4"
48
50
  }
49
51
  }
@@ -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
+ }