@graphcommerce/next-ui 4.19.0 → 4.22.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 +46 -12
- package/ActionCard/ActionCardList.tsx +17 -15
- package/ActionCard/ActionCardListForm.tsx +1 -2
- package/AnimatedRow/AnimatedRow.tsx +3 -3
- package/Blog/BlogHeader/BlogHeader.tsx +7 -1
- package/Blog/BlogListItem/BlogListItem.tsx +5 -1
- package/Blog/BlogTags/BlogTag.tsx +2 -3
- package/CHANGELOG.md +57 -0
- package/Footer/Footer.tsx +3 -2
- package/Form/Form.tsx +7 -1
- package/Form/FormActions.tsx +1 -1
- package/FramerScroller/SidebarGallery.tsx +1 -5
- package/IconSvg/IconSvg.tsx +4 -4
- package/Layout/components/LayoutHeaderClose.tsx +2 -0
- package/Layout/components/LayoutHeaderContent.tsx +3 -1
- package/LayoutDefault/components/LayoutDefault.tsx +3 -3
- package/LayoutOverlay/components/LayoutOverlay.tsx +2 -1
- package/LayoutParts/DesktopHeaderBadge.tsx +3 -3
- package/Navigation/components/NavigationItem.tsx +31 -17
- package/Navigation/components/NavigationList.tsx +21 -15
- package/Navigation/components/NavigationOverlay.tsx +48 -27
- package/Navigation/components/NavigationProvider.tsx +12 -30
- package/Navigation/hooks/useNavigation.ts +33 -6
- package/Overlay/components/Overlay.tsx +1 -2
- package/Overlay/components/OverlayBase.tsx +27 -23
- package/Overlay/hooks/useOverlayPosition.ts +6 -1
- package/Row/HeroBanner/HeroBanner.tsx +7 -2
- package/Row/ImageText/ImageText.tsx +10 -3
- package/Row/ImageTextBoxed/ImageTextBoxed.tsx +7 -1
- package/Row/ParagraphWithSidebarSlide/ParagraphWithSidebarSlide.tsx +4 -2
- package/Row/SpecialBanner/SpecialBanner.tsx +10 -2
- package/Snackbar/MessageSnackbarImpl.tsx +9 -5
- package/Stepper/Stepper.tsx +1 -1
- package/Styles/breakpointVal.tsx +1 -1
- package/TextInputNumber/TextInputNumber.tsx +5 -4
- package/Theme/MuiButton.ts +12 -3
- package/Theme/MuiFab.ts +2 -2
- package/ToggleButton/ToggleButton.tsx +15 -5
- package/hooks/index.ts +1 -0
- package/hooks/useMemoDeep.ts +15 -0
- package/package.json +6 -6
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
import { styled } from '@mui/material'
|
|
2
|
+
import React from 'react'
|
|
2
3
|
import { extendableComponent } from '../../Styles/extendableComponent'
|
|
3
|
-
import { NavigationNode
|
|
4
|
+
import { NavigationNode } from '../hooks/useNavigation'
|
|
4
5
|
import { NavigationItem, mouseEventPref } from './NavigationItem'
|
|
5
6
|
|
|
6
|
-
const NavigationUList = styled('ul')({
|
|
7
|
+
const NavigationUList = styled('ul')({
|
|
8
|
+
display: 'block',
|
|
9
|
+
position: 'absolute',
|
|
10
|
+
left: '-10000px',
|
|
11
|
+
top: '-10000px',
|
|
12
|
+
'&.selected': {
|
|
13
|
+
display: 'contents',
|
|
14
|
+
},
|
|
15
|
+
})
|
|
7
16
|
|
|
8
17
|
type NavigationItemsProps = {
|
|
9
|
-
parentPath?:
|
|
18
|
+
parentPath?: string
|
|
10
19
|
items: NavigationNode[]
|
|
11
20
|
selected?: boolean
|
|
12
21
|
} & mouseEventPref
|
|
13
22
|
|
|
14
23
|
type OwnerState = {
|
|
15
24
|
column: number
|
|
25
|
+
selected: boolean
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
const name = 'NavigationList'
|
|
19
29
|
const parts = ['root'] as const
|
|
20
30
|
const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export function NavigationList(props: NavigationItemsProps) {
|
|
26
|
-
const { items, parentPath = [], selected = false, mouseEvent } = props
|
|
32
|
+
export const NavigationList = React.memo<NavigationItemsProps>((props) => {
|
|
33
|
+
const { items, parentPath = '', selected = false, mouseEvent } = props
|
|
27
34
|
|
|
35
|
+
const classes = withState({ column: 0, selected })
|
|
28
36
|
return (
|
|
29
|
-
<NavigationUList
|
|
30
|
-
sx={[
|
|
31
|
-
{ display: 'block', position: 'absolute', left: '-10000px', top: '-10000px' },
|
|
32
|
-
selected && { display: 'contents' },
|
|
33
|
-
]}
|
|
34
|
-
className={withState({ column: 0 }).root}
|
|
35
|
-
>
|
|
37
|
+
<NavigationUList className={classes.root}>
|
|
36
38
|
{items.map((item, idx) => (
|
|
37
39
|
<NavigationItem
|
|
38
40
|
NavigationList={NavigationList}
|
|
@@ -48,4 +50,8 @@ export function NavigationList(props: NavigationItemsProps) {
|
|
|
48
50
|
))}
|
|
49
51
|
</NavigationUList>
|
|
50
52
|
)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
56
|
+
NavigationList.displayName = 'NavigationList'
|
|
51
57
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import styled from '@emotion/styled'
|
|
2
|
+
import { useMotionValueValue, useMotionSelector } from '@graphcommerce/framer-utils'
|
|
2
3
|
import { i18n } from '@lingui/core'
|
|
3
4
|
import { Trans } from '@lingui/react'
|
|
4
5
|
import { Box, Fab, SxProps, Theme, useEventCallback, useMediaQuery } from '@mui/material'
|
|
5
|
-
import { m } from 'framer-motion'
|
|
6
|
-
import { useEffect, useState } from 'react'
|
|
6
|
+
import { m, useMotionValue } from 'framer-motion'
|
|
7
|
+
import React, { startTransition, useEffect, useRef, useState } from 'react'
|
|
7
8
|
import { IconSvg, useIconSvgSize } from '../../IconSvg'
|
|
8
9
|
import { LayoutHeaderContent } from '../../Layout/components/LayoutHeaderContent'
|
|
9
10
|
import { LayoutTitle } from '../../Layout/components/LayoutTitle'
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
NavigationContextType,
|
|
18
19
|
NavigationNodeButton,
|
|
19
20
|
NavigationNodeHref,
|
|
21
|
+
NavigationPath,
|
|
20
22
|
useNavigation,
|
|
21
23
|
} from '../hooks/useNavigation'
|
|
22
24
|
import { mouseEventPref } from './NavigationItem'
|
|
@@ -27,7 +29,6 @@ type LayoutOverlaySize = 'floating' | 'minimal' | 'full'
|
|
|
27
29
|
type LayoutOverlayAlign = 'start' | 'end' | 'center' | 'stretch'
|
|
28
30
|
|
|
29
31
|
type NavigationOverlayProps = {
|
|
30
|
-
active: boolean
|
|
31
32
|
sx?: SxProps<Theme>
|
|
32
33
|
stretchColumns?: boolean
|
|
33
34
|
variantSm: LayoutOverlayVariant
|
|
@@ -42,8 +43,9 @@ type NavigationOverlayProps = {
|
|
|
42
43
|
|
|
43
44
|
function findCurrent(
|
|
44
45
|
items: NavigationContextType['items'],
|
|
45
|
-
selected:
|
|
46
|
+
selected: NavigationPath | false,
|
|
46
47
|
): NavigationNodeHref | NavigationNodeButton | undefined {
|
|
48
|
+
if (selected === false) return undefined
|
|
47
49
|
const lastItem = selected.slice(-1)[0]
|
|
48
50
|
|
|
49
51
|
if (!lastItem) return undefined
|
|
@@ -67,9 +69,8 @@ const componentName = 'Navigation'
|
|
|
67
69
|
const parts = ['root', 'navigation', 'header', 'column'] as const
|
|
68
70
|
const { classes } = extendableComponent(componentName, parts)
|
|
69
71
|
|
|
70
|
-
export
|
|
72
|
+
export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
|
|
71
73
|
const {
|
|
72
|
-
active,
|
|
73
74
|
sx,
|
|
74
75
|
stretchColumns,
|
|
75
76
|
variantMd,
|
|
@@ -82,28 +83,42 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
82
83
|
itemWidthMd,
|
|
83
84
|
mouseEvent,
|
|
84
85
|
} = props
|
|
85
|
-
const {
|
|
86
|
+
const { selection, items, animating, closing } = useNavigation()
|
|
86
87
|
|
|
87
88
|
const fabSize = useFabSize('responsive')
|
|
88
89
|
const svgSize = useIconSvgSize('large')
|
|
89
90
|
|
|
90
91
|
const isMobile = useMediaQuery<Theme>((theme) => theme.breakpoints.down('md'))
|
|
91
92
|
const handleOnBack = useEventCallback(() => {
|
|
92
|
-
if (isMobile)
|
|
93
|
-
|
|
93
|
+
if (isMobile) {
|
|
94
|
+
const current = selection.get()
|
|
95
|
+
selection.set(current !== false ? current.slice(0, -1) : false)
|
|
96
|
+
} else selection.set([])
|
|
94
97
|
})
|
|
95
98
|
|
|
99
|
+
const selectedLevel = useMotionValueValue(selection, (s) => (s === false ? -1 : s.length))
|
|
100
|
+
const activeAndNotClosing = useMotionSelector([selection, closing], ([s, c]) =>
|
|
101
|
+
c ? false : s !== false,
|
|
102
|
+
)
|
|
103
|
+
|
|
96
104
|
useEffect(() => {
|
|
97
|
-
animating.
|
|
98
|
-
}, [
|
|
105
|
+
animating.set(false)
|
|
106
|
+
}, [activeAndNotClosing, animating])
|
|
99
107
|
|
|
100
|
-
const
|
|
108
|
+
const afterClose = useEventCallback(() => {
|
|
109
|
+
if (!closing.get()) return
|
|
110
|
+
closing.set(false)
|
|
111
|
+
selection.set(false)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const handleClose = useEventCallback(() => closing.set(true))
|
|
101
115
|
|
|
102
116
|
return (
|
|
103
117
|
<Overlay
|
|
104
118
|
className={classes.root}
|
|
105
|
-
active={
|
|
106
|
-
|
|
119
|
+
active={activeAndNotClosing}
|
|
120
|
+
safeToRemove={afterClose}
|
|
121
|
+
onClosed={handleClose}
|
|
107
122
|
variantSm={variantSm}
|
|
108
123
|
sizeSm={sizeSm}
|
|
109
124
|
justifySm={justifySm}
|
|
@@ -111,11 +126,13 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
111
126
|
sizeMd={sizeMd}
|
|
112
127
|
justifyMd={justifyMd}
|
|
113
128
|
overlayPaneProps={{
|
|
129
|
+
layout: true,
|
|
130
|
+
initial: false,
|
|
114
131
|
onLayoutAnimationStart: () => {
|
|
115
|
-
animating.
|
|
132
|
+
animating.set(true)
|
|
116
133
|
},
|
|
117
134
|
onLayoutAnimationComplete: () => {
|
|
118
|
-
animating.
|
|
135
|
+
animating.set(false)
|
|
119
136
|
},
|
|
120
137
|
}}
|
|
121
138
|
sx={{
|
|
@@ -145,7 +162,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
145
162
|
switchPoint={0}
|
|
146
163
|
layout='position'
|
|
147
164
|
left={
|
|
148
|
-
|
|
165
|
+
selectedLevel > 0 && (
|
|
149
166
|
<Fab
|
|
150
167
|
color='inherit'
|
|
151
168
|
onClick={handleOnBack}
|
|
@@ -164,7 +181,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
164
181
|
right={
|
|
165
182
|
<Fab
|
|
166
183
|
color='inherit'
|
|
167
|
-
onClick={
|
|
184
|
+
onClick={handleClose}
|
|
168
185
|
sx={{
|
|
169
186
|
boxShadow: 'none',
|
|
170
187
|
marginLeft: `calc((${fabSize} - ${svgSize}) * -0.5)`,
|
|
@@ -178,7 +195,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
178
195
|
}
|
|
179
196
|
>
|
|
180
197
|
<LayoutTitle size='small' component='span'>
|
|
181
|
-
{findCurrent(items,
|
|
198
|
+
{findCurrent(items, selection.get())?.name ?? <Trans id='Menu' />}
|
|
182
199
|
</LayoutTitle>
|
|
183
200
|
</LayoutHeaderContent>
|
|
184
201
|
</Box>
|
|
@@ -192,15 +209,15 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
192
209
|
'& .NavigationItem-item': {
|
|
193
210
|
// eslint-disable-next-line no-nested-ternary
|
|
194
211
|
width: itemWidthMd
|
|
195
|
-
?
|
|
212
|
+
? selectedLevel >= 1
|
|
196
213
|
? `calc(${itemWidthMd} + 1px)`
|
|
197
214
|
: itemWidthMd
|
|
198
|
-
: '
|
|
215
|
+
: 'stretch',
|
|
199
216
|
},
|
|
200
217
|
[theme.breakpoints.down('md')]: {
|
|
201
218
|
width:
|
|
202
219
|
sizeSm !== 'floating'
|
|
203
|
-
? `calc(${itemWidthSm || '100vw'} + ${
|
|
220
|
+
? `calc(${itemWidthSm || '100vw'} + ${selectedLevel}px)`
|
|
204
221
|
: `calc(${itemWidthSm || '100vw'} - ${theme.page.horizontal} - ${
|
|
205
222
|
theme.page.horizontal
|
|
206
223
|
})`,
|
|
@@ -212,7 +229,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
212
229
|
sizeSm !== 'floating'
|
|
213
230
|
? `calc(${itemWidthSm || '100vw'} - ${theme.spacings.md} - ${
|
|
214
231
|
theme.spacings.md
|
|
215
|
-
} + ${
|
|
232
|
+
} + ${selectedLevel}px)`
|
|
216
233
|
: `calc(${itemWidthSm || '100vw'} - ${theme.spacings.md} - ${
|
|
217
234
|
theme.spacings.md
|
|
218
235
|
} - ${theme.page.horizontal} - ${theme.page.horizontal})`,
|
|
@@ -250,7 +267,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
250
267
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
251
268
|
]}
|
|
252
269
|
>
|
|
253
|
-
{
|
|
270
|
+
{selectedLevel >= 0 && (
|
|
254
271
|
<Box
|
|
255
272
|
sx={(theme) => ({
|
|
256
273
|
gridArea: '1 / 1 / 999 / 2',
|
|
@@ -259,7 +276,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
259
276
|
className={classes.column}
|
|
260
277
|
/>
|
|
261
278
|
)}
|
|
262
|
-
{
|
|
279
|
+
{selectedLevel >= 1 && (
|
|
263
280
|
<Box
|
|
264
281
|
sx={(theme) => ({
|
|
265
282
|
gridArea: '1 / 2 / 999 / 3',
|
|
@@ -268,7 +285,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
268
285
|
className={classes.column}
|
|
269
286
|
/>
|
|
270
287
|
)}
|
|
271
|
-
{
|
|
288
|
+
{selectedLevel >= 2 && (
|
|
272
289
|
<Box
|
|
273
290
|
sx={(theme) => ({
|
|
274
291
|
gridArea: '1 / 3 / 999 / 4',
|
|
@@ -277,7 +294,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
277
294
|
className={classes.column}
|
|
278
295
|
/>
|
|
279
296
|
)}
|
|
280
|
-
{
|
|
297
|
+
{selectedLevel >= 3 && (
|
|
281
298
|
<Box
|
|
282
299
|
sx={(theme) => ({
|
|
283
300
|
gridArea: '1 / 4 / 999 / 5',
|
|
@@ -293,4 +310,8 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
|
|
|
293
310
|
</MotionDiv>
|
|
294
311
|
</Overlay>
|
|
295
312
|
)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
316
|
+
NavigationOverlay.displayName = 'NavigationOverlay'
|
|
296
317
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useState, useMemo, SetStateAction, useRef } from 'react'
|
|
1
|
+
import { MotionConfig, useMotionValue } from 'framer-motion'
|
|
2
|
+
import React, { useMemo, useRef } from 'react'
|
|
4
3
|
import { isElement } from 'react-is'
|
|
5
4
|
import {
|
|
6
5
|
NavigationNode,
|
|
7
|
-
NavigationPath,
|
|
8
6
|
NavigationContextType,
|
|
9
7
|
NavigationContext,
|
|
10
|
-
|
|
8
|
+
UseNavigationSelection,
|
|
11
9
|
} from '../hooks/useNavigation'
|
|
12
10
|
|
|
13
11
|
export type NavigationProviderProps = {
|
|
@@ -16,51 +14,35 @@ export type NavigationProviderProps = {
|
|
|
16
14
|
closeAfterNavigate?: boolean
|
|
17
15
|
children?: React.ReactNode
|
|
18
16
|
animationDuration?: number
|
|
19
|
-
|
|
20
|
-
setSelected: (value: SetStateAction<NavigationPath>) => void
|
|
21
|
-
onChange?: NavigationSelect
|
|
22
|
-
onClose?: NavigationContextType['onClose']
|
|
17
|
+
selection: UseNavigationSelection
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
const nonNullable = <T,>(value: T): value is NonNullable<T> => value !== null && value !== undefined
|
|
26
21
|
|
|
27
|
-
export
|
|
22
|
+
export const NavigationProvider = React.memo<NavigationProviderProps>((props) => {
|
|
28
23
|
const {
|
|
29
24
|
items,
|
|
30
|
-
onChange,
|
|
31
25
|
hideRootOnNavigate = true,
|
|
32
26
|
closeAfterNavigate = false,
|
|
33
27
|
animationDuration = 0.275,
|
|
34
28
|
children,
|
|
35
|
-
|
|
36
|
-
selected,
|
|
37
|
-
setSelected,
|
|
29
|
+
selection,
|
|
38
30
|
} = props
|
|
39
31
|
|
|
40
|
-
const animating =
|
|
41
|
-
|
|
42
|
-
const select = useEventCallback((incomming: NavigationPath) => {
|
|
43
|
-
setSelected(incomming)
|
|
44
|
-
onChange?.(incomming)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const onClose: NavigationContextType['onClose'] = useEventCallback((e, href) => {
|
|
48
|
-
onCloseUnstable?.(e, href)
|
|
49
|
-
setTimeout(() => select([]), animationDuration * 1000)
|
|
50
|
-
})
|
|
32
|
+
const animating = useMotionValue(false)
|
|
33
|
+
const closing = useMotionValue(false)
|
|
51
34
|
|
|
52
35
|
const value = useMemo<NavigationContextType>(
|
|
53
36
|
() => ({
|
|
54
37
|
hideRootOnNavigate,
|
|
55
|
-
|
|
56
|
-
select,
|
|
38
|
+
selection,
|
|
57
39
|
animating,
|
|
40
|
+
closing,
|
|
58
41
|
items: items
|
|
59
42
|
.map((item, index) => (isElement(item) ? { id: item.key ?? index, component: item } : item))
|
|
60
43
|
.filter(nonNullable),
|
|
61
|
-
onClose,
|
|
62
44
|
}),
|
|
63
|
-
[hideRootOnNavigate,
|
|
45
|
+
[hideRootOnNavigate, selection, animating, closing, items],
|
|
64
46
|
)
|
|
65
47
|
|
|
66
48
|
return (
|
|
@@ -68,4 +50,4 @@ export function NavigationProvider(props: NavigationProviderProps) {
|
|
|
68
50
|
<NavigationContext.Provider value={value}>{children}</NavigationContext.Provider>
|
|
69
51
|
</MotionConfig>
|
|
70
52
|
)
|
|
71
|
-
}
|
|
53
|
+
})
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MotionValue, useMotionValue } from 'framer-motion'
|
|
2
|
+
import { createContext, MutableRefObject, useContext } from 'react'
|
|
2
3
|
|
|
3
4
|
export type NavigationId = string | number
|
|
4
5
|
export type NavigationPath = NavigationId[]
|
|
5
|
-
export type NavigationSelect = (selected: NavigationPath) => void
|
|
6
6
|
export type NavigationRender = React.FC<
|
|
7
7
|
(NavigationNodeComponent | NavigationNodeHref) & { children?: React.ReactNode }
|
|
8
8
|
>
|
|
9
9
|
|
|
10
|
+
export type UseNavigationSelection = MotionValue<NavigationPath | false>
|
|
11
|
+
|
|
10
12
|
export type NavigationOnClose = (
|
|
11
13
|
event?: React.MouseEvent<HTMLAnchorElement>,
|
|
12
14
|
href?: string | undefined,
|
|
13
15
|
) => void
|
|
14
16
|
export type NavigationContextType = {
|
|
15
|
-
|
|
16
|
-
select: NavigationSelect
|
|
17
|
+
selection: UseNavigationSelection
|
|
17
18
|
items: NavigationNode[]
|
|
18
19
|
hideRootOnNavigate: boolean
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
animating: MotionValue<boolean>
|
|
21
|
+
closing: MotionValue<boolean>
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
type NavigationNodeBase = {
|
|
@@ -57,3 +58,29 @@ export const NavigationContext = createContext(undefined as unknown as Navigatio
|
|
|
57
58
|
export function useNavigation() {
|
|
58
59
|
return useContext(NavigationContext)
|
|
59
60
|
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* To prevent excessive rerenders we're not using plain React useState, but we're using a reactive
|
|
64
|
+
* motion value (could easily be any other reactive variable like Zustand, MobX, etc).
|
|
65
|
+
*
|
|
66
|
+
* Usage:
|
|
67
|
+
*
|
|
68
|
+
* ```tsx
|
|
69
|
+
* const selection = useNavigationSelection()
|
|
70
|
+
*
|
|
71
|
+
* function onClose() {
|
|
72
|
+
* selection.set(false)
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* function openRoot() {
|
|
76
|
+
* selection.set([])
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
79
|
+
* function openPath() {
|
|
80
|
+
* selection.set(['my-path'])
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function useNavigationSelection(): UseNavigationSelection {
|
|
85
|
+
return useMotionValue<NavigationPath | false>(false)
|
|
86
|
+
}
|
|
@@ -6,7 +6,7 @@ import { OverlayBase, LayoutOverlayBaseProps } from './OverlayBase'
|
|
|
6
6
|
|
|
7
7
|
export type OverlayProps = Omit<
|
|
8
8
|
SetOptional<LayoutOverlayBaseProps, 'variantSm' | 'variantMd'>,
|
|
9
|
-
'direction' | 'offsetPageY' | 'isPresent'
|
|
9
|
+
'direction' | 'offsetPageY' | 'isPresent'
|
|
10
10
|
>
|
|
11
11
|
|
|
12
12
|
export function Overlay(props: OverlayProps) {
|
|
@@ -50,7 +50,6 @@ export function Overlay(props: OverlayProps) {
|
|
|
50
50
|
direction={1}
|
|
51
51
|
active={active}
|
|
52
52
|
isPresent={active}
|
|
53
|
-
safeToRemove={undefined}
|
|
54
53
|
{...otherProps}
|
|
55
54
|
>
|
|
56
55
|
{children}
|
|
@@ -39,7 +39,7 @@ export type LayoutOverlayBaseProps = {
|
|
|
39
39
|
onClosed: () => void
|
|
40
40
|
offsetPageY: number
|
|
41
41
|
isPresent: boolean
|
|
42
|
-
safeToRemove
|
|
42
|
+
safeToRemove?: (() => void) | null | undefined
|
|
43
43
|
overlayPaneProps?: MotionProps
|
|
44
44
|
} & StyleProps &
|
|
45
45
|
OverridableProps
|
|
@@ -115,14 +115,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
115
115
|
useIsomorphicLayoutEffect(() => {
|
|
116
116
|
const scroller = scrollerRef.current
|
|
117
117
|
|
|
118
|
-
if (!scroller) return undefined
|
|
119
|
-
|
|
120
|
-
if (!isPresent && position.get() === OverlayPosition.UNOPENED) {
|
|
121
|
-
scroller.scrollLeft = positions.closed.x.get()
|
|
122
|
-
scroller.scrollTop = positions.closed.y.get()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!isPresent) return undefined
|
|
118
|
+
if (!scroller || !isPresent) return undefined
|
|
126
119
|
|
|
127
120
|
const open = { x: positions.open.x.get(), y: positions.open.y.get() }
|
|
128
121
|
|
|
@@ -151,10 +144,15 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
151
144
|
if (!scroller) return () => {}
|
|
152
145
|
|
|
153
146
|
const resize = () => {
|
|
154
|
-
if (positions.open.visible.get()
|
|
147
|
+
if (positions.open.visible.get() === 1) {
|
|
148
|
+
scroller.scrollLeft = positions.open.x.get()
|
|
149
|
+
scroller.scrollTop = positions.open.y.get()
|
|
150
|
+
}
|
|
155
151
|
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
if (positions.open.visible.get() === 0) {
|
|
153
|
+
scroller.scrollLeft = positions.closed.x.get()
|
|
154
|
+
scroller.scrollTop = positions.closed.y.get()
|
|
155
|
+
}
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
window.addEventListener('resize', resize)
|
|
@@ -165,16 +163,23 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
165
163
|
|
|
166
164
|
// When the overlay is closed by navigating away, we're closing the overlay.
|
|
167
165
|
useEffect(() => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
clearScrollLock()
|
|
166
|
+
const scroller = scrollerRef.current
|
|
167
|
+
if (isPresent || !scroller) return
|
|
171
168
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
if (position.get() === OverlayPosition.UNOPENED) {
|
|
170
|
+
position.set(OverlayPosition.CLOSED)
|
|
171
|
+
clearScrollLock()
|
|
172
|
+
scroller.scrollLeft = positions.closed.x.get()
|
|
173
|
+
scroller.scrollTop = positions.closed.y.get()
|
|
174
|
+
safeToRemove?.()
|
|
175
|
+
} else {
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
177
|
+
scrollTo({
|
|
178
|
+
x: positions.closed.x.get(),
|
|
179
|
+
y: positions.closed.y.get(),
|
|
180
|
+
}).then(() => safeToRemove?.())
|
|
181
|
+
}
|
|
182
|
+
}, [isPresent, position, positions, safeToRemove, scrollTo, scrollerRef])
|
|
178
183
|
|
|
179
184
|
// Only go back to a previous page if the overlay isn't closed.
|
|
180
185
|
const closeOverlay = useCallback(() => {
|
|
@@ -370,7 +375,6 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
370
375
|
>
|
|
371
376
|
<MotionDiv
|
|
372
377
|
{...overlayPaneProps}
|
|
373
|
-
layout
|
|
374
378
|
className={classes.overlayPane}
|
|
375
379
|
sx={(theme) => ({
|
|
376
380
|
pointerEvents: 'all',
|
|
@@ -380,7 +384,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
380
384
|
[theme.breakpoints.down('md')]: {
|
|
381
385
|
minWidth: '80vw',
|
|
382
386
|
'&:not(.sizeMdFull)': {
|
|
383
|
-
width: '
|
|
387
|
+
width: 'auto',
|
|
384
388
|
},
|
|
385
389
|
|
|
386
390
|
'&.variantSmBottom.sizeSmFull': {
|
|
@@ -26,6 +26,7 @@ export function useOverlayPosition() {
|
|
|
26
26
|
|
|
27
27
|
const measure = () => {
|
|
28
28
|
const positions = getScrollSnapPositions()
|
|
29
|
+
|
|
29
30
|
state.open.x.set(positions.x[1] ?? 0)
|
|
30
31
|
state.closed.x.set(positions.x[0])
|
|
31
32
|
state.open.y.set(positions.y[1] ?? 0)
|
|
@@ -50,10 +51,14 @@ export function useOverlayPosition() {
|
|
|
50
51
|
|
|
51
52
|
const xC = state.closed.x.get()
|
|
52
53
|
const xO = state.open.x.get()
|
|
54
|
+
|
|
53
55
|
const visX = xO === xC ? 1 : Math.max(0, Math.min(1, (x - xC) / (xO - xC)))
|
|
54
56
|
|
|
57
|
+
let vis = visY * visX
|
|
58
|
+
if (xC === 0 && xO === 0 && yC === 0 && yO === 0) vis = 0
|
|
59
|
+
|
|
55
60
|
// todo: visibility sometimes flickers
|
|
56
|
-
state.open.visible.set(
|
|
61
|
+
state.open.visible.set(vis)
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
const cancelY = scroll.y.onChange(calc)
|
|
@@ -3,6 +3,7 @@ import { m, useTransform } from 'framer-motion'
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import { useScrollY } from '../../Layout/hooks/useScrollY'
|
|
5
5
|
import { extendableComponent } from '../../Styles'
|
|
6
|
+
import { breakpointVal } from '../../Styles/breakpointVal'
|
|
6
7
|
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
7
8
|
import { Row } from '../Row'
|
|
8
9
|
|
|
@@ -84,9 +85,11 @@ export function HeroBanner(props: HeroBannerProps) {
|
|
|
84
85
|
width: '100%',
|
|
85
86
|
height: '100%',
|
|
86
87
|
[theme.breakpoints.down('md')]: {
|
|
87
|
-
|
|
88
|
+
...breakpointVal(
|
|
89
|
+
'borderRadius',
|
|
88
90
|
theme.shape.borderRadius * 2,
|
|
89
91
|
theme.shape.borderRadius * 3,
|
|
92
|
+
theme.breakpoints.values,
|
|
90
93
|
),
|
|
91
94
|
},
|
|
92
95
|
},
|
|
@@ -99,9 +102,11 @@ export function HeroBanner(props: HeroBannerProps) {
|
|
|
99
102
|
style={{ width: !matches ? width : 0, borderRadius }}
|
|
100
103
|
className={classes.animated}
|
|
101
104
|
sx={(theme) => ({
|
|
102
|
-
|
|
105
|
+
...breakpointVal(
|
|
106
|
+
'borderRadius',
|
|
103
107
|
theme.shape.borderRadius * 2,
|
|
104
108
|
theme.shape.borderRadius * 3,
|
|
109
|
+
theme.breakpoints.values,
|
|
105
110
|
),
|
|
106
111
|
overflow: 'hidden',
|
|
107
112
|
transform: 'translateZ(0)',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Box, SxProps, Theme } from '@mui/material'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { extendableComponent } from '../../Styles'
|
|
4
|
-
import {
|
|
4
|
+
import { breakpointVal } from '../../Styles/breakpointVal'
|
|
5
5
|
import { Row } from '../Row'
|
|
6
6
|
|
|
7
7
|
export type ImageTextProps = {
|
|
@@ -37,7 +37,12 @@ export function ImageText(props: ImageTextProps) {
|
|
|
37
37
|
background: 'none',
|
|
38
38
|
gridTemplateColumns: '1fr 1fr',
|
|
39
39
|
},
|
|
40
|
-
|
|
40
|
+
...breakpointVal(
|
|
41
|
+
'borderRadius',
|
|
42
|
+
theme.shape.borderRadius * 2,
|
|
43
|
+
theme.shape.borderRadius * 3,
|
|
44
|
+
theme.breakpoints.values,
|
|
45
|
+
),
|
|
41
46
|
})}
|
|
42
47
|
>
|
|
43
48
|
<Box
|
|
@@ -49,9 +54,11 @@ export function ImageText(props: ImageTextProps) {
|
|
|
49
54
|
height: '100%',
|
|
50
55
|
width: '100%',
|
|
51
56
|
objectFit: 'cover',
|
|
52
|
-
|
|
57
|
+
...breakpointVal(
|
|
58
|
+
'borderRadius',
|
|
53
59
|
theme.shape.borderRadius * 2,
|
|
54
60
|
theme.shape.borderRadius * 3,
|
|
61
|
+
theme.breakpoints.values,
|
|
55
62
|
),
|
|
56
63
|
},
|
|
57
64
|
})}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box, SxProps, Theme } from '@mui/material'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { extendableComponent } from '../../Styles'
|
|
4
|
+
import { breakpointVal } from '../../Styles/breakpointVal'
|
|
4
5
|
import { responsiveVal } from '../../Styles/responsiveVal'
|
|
5
6
|
import { Row } from '../Row'
|
|
6
7
|
|
|
@@ -32,7 +33,12 @@ export function ImageTextBoxed(props: ImageTextBoxedProps) {
|
|
|
32
33
|
gridTemplateColumns: '1fr auto',
|
|
33
34
|
columnGap: `${theme.spacings.lg}`,
|
|
34
35
|
},
|
|
35
|
-
|
|
36
|
+
...breakpointVal(
|
|
37
|
+
'borderRadius',
|
|
38
|
+
theme.shape.borderRadius * 2,
|
|
39
|
+
theme.shape.borderRadius * 3,
|
|
40
|
+
theme.breakpoints.values,
|
|
41
|
+
),
|
|
36
42
|
overflow: 'hidden',
|
|
37
43
|
})}
|
|
38
44
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box, ContainerProps } from '@mui/material'
|
|
2
2
|
import { extendableComponent } from '../../Styles'
|
|
3
|
-
import {
|
|
3
|
+
import { breakpointVal } from '../../Styles/breakpointVal'
|
|
4
4
|
import { Row } from '../Row'
|
|
5
5
|
|
|
6
6
|
export type ParagraphWithSidebarSlideProps = ContainerProps & {
|
|
@@ -46,9 +46,11 @@ export function ParagraphWithSidebarSlide(props: ParagraphWithSidebarSlideProps)
|
|
|
46
46
|
filter: 'brightness(100%)',
|
|
47
47
|
height: '100%',
|
|
48
48
|
},
|
|
49
|
-
|
|
49
|
+
...breakpointVal(
|
|
50
|
+
'borderRadius',
|
|
50
51
|
theme.shape.borderRadius * 2,
|
|
51
52
|
theme.shape.borderRadius * 3,
|
|
53
|
+
theme.breakpoints.values,
|
|
52
54
|
),
|
|
53
55
|
},
|
|
54
56
|
})}
|