@graphcommerce/next-ui 4.28.0 → 4.28.1
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 +12 -6
- package/ActionCard/ActionCardLayout.tsx +57 -0
- package/ActionCard/ActionCardList.tsx +9 -35
- package/ActionCard/index.ts +4 -0
- package/CHANGELOG.md +18 -0
- package/FramerScroller/ItemScroller.tsx +95 -0
- package/FramerScroller/SidebarSlider.tsx +2 -2
- package/FramerScroller/index.ts +1 -0
- package/Layout/components/LayoutHeaderContent.tsx +1 -0
- package/LayoutOverlay/components/LayoutOverlay.tsx +5 -1
- package/Navigation/components/NavigationOverlay.tsx +1 -1
- package/Overlay/components/OverlayBase.tsx +19 -13
- package/Overlay/hooks/useOverlayPosition.ts +56 -26
- package/RenderType/filterNonNullableKeys.ts +10 -7
- package/Snackbar/MessageSnackbarImpl.tsx +5 -4
- package/index.ts +1 -3
- package/package.json +5 -5
|
@@ -12,7 +12,7 @@ function isButtonProps(props: ButtonProps<'div'> | BoxProps<'div'>): props is Bu
|
|
|
12
12
|
return props.onClick !== undefined
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const ButtonOrBox = (props: ButtonProps<'div'> | BoxProps<'div'>) =>
|
|
16
16
|
isButtonProps(props) ? <ButtonBase component='div' {...props} /> : <Box {...props} />
|
|
17
17
|
|
|
18
18
|
export type ActionCardProps = {
|
|
@@ -101,26 +101,26 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
101
101
|
})
|
|
102
102
|
|
|
103
103
|
return (
|
|
104
|
-
<
|
|
104
|
+
<ButtonOrBox
|
|
105
105
|
className={classes.root}
|
|
106
106
|
onClick={onClick && ((event) => onClick?.(event, value))}
|
|
107
107
|
disabled={disabled}
|
|
108
108
|
sx={[
|
|
109
109
|
(theme) => ({
|
|
110
110
|
'&.sizeSmall': {
|
|
111
|
-
|
|
111
|
+
py: `5px`,
|
|
112
112
|
display: 'flex',
|
|
113
113
|
typography: 'body2',
|
|
114
114
|
},
|
|
115
115
|
|
|
116
116
|
'&.sizeMedium': {
|
|
117
|
-
|
|
117
|
+
py: `10px`,
|
|
118
118
|
typography: 'body2',
|
|
119
119
|
display: 'block',
|
|
120
120
|
},
|
|
121
121
|
|
|
122
122
|
'&.sizeLarge': {
|
|
123
|
-
|
|
123
|
+
py: theme.spacings.xxs,
|
|
124
124
|
display: 'block',
|
|
125
125
|
},
|
|
126
126
|
|
|
@@ -184,6 +184,10 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
184
184
|
),
|
|
185
185
|
},
|
|
186
186
|
|
|
187
|
+
'&.sizeSmall': { px: `10px` },
|
|
188
|
+
'&.sizeMedium': { px: `12px` },
|
|
189
|
+
'&.sizeLarge': { px: theme.spacings.xs },
|
|
190
|
+
|
|
187
191
|
'&.selected': {
|
|
188
192
|
border: `2px solid ${theme.palette[color].main}`,
|
|
189
193
|
boxShadow: `0 0 0 4px ${alpha(
|
|
@@ -223,6 +227,7 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
223
227
|
flexDirection: 'row',
|
|
224
228
|
width: '100%',
|
|
225
229
|
justifyContent: 'space-between',
|
|
230
|
+
alignContent: 'stretch',
|
|
226
231
|
}}
|
|
227
232
|
>
|
|
228
233
|
<Box
|
|
@@ -230,6 +235,7 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
230
235
|
display: 'flex',
|
|
231
236
|
flexDirection: 'row',
|
|
232
237
|
justifyContent: 'space-between',
|
|
238
|
+
alignContent: 'stretch',
|
|
233
239
|
}}
|
|
234
240
|
>
|
|
235
241
|
{image && (
|
|
@@ -302,6 +308,6 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
302
308
|
</Box>
|
|
303
309
|
</Box>
|
|
304
310
|
{after && <Box className={classes.after}>{after}</Box>}
|
|
305
|
-
</
|
|
311
|
+
</ButtonOrBox>
|
|
306
312
|
)
|
|
307
313
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Box, BoxProps } from '@mui/material'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { extendableComponent } from '../Styles'
|
|
4
|
+
import { ActionCardProps } from './ActionCard'
|
|
5
|
+
|
|
6
|
+
type ActionCardLayoutProps = {
|
|
7
|
+
children?: React.ReactNode
|
|
8
|
+
} & Pick<ActionCardProps, 'layout'> &
|
|
9
|
+
BoxProps
|
|
10
|
+
|
|
11
|
+
const parts = ['root'] as const
|
|
12
|
+
const name = 'ActionCardLayout'
|
|
13
|
+
const { withState } = extendableComponent<
|
|
14
|
+
Pick<ActionCardProps, 'layout'>,
|
|
15
|
+
typeof name,
|
|
16
|
+
typeof parts
|
|
17
|
+
>(name, parts)
|
|
18
|
+
|
|
19
|
+
export const ActionCardLayout = React.forwardRef<HTMLDivElement, ActionCardLayoutProps>(
|
|
20
|
+
(props, ref) => {
|
|
21
|
+
const { layout = 'list' } = props
|
|
22
|
+
|
|
23
|
+
const classes = withState({ layout })
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Box
|
|
27
|
+
ref={ref}
|
|
28
|
+
{...props}
|
|
29
|
+
className={classes.root}
|
|
30
|
+
sx={[
|
|
31
|
+
(theme) => ({
|
|
32
|
+
'&.layoutStack': {
|
|
33
|
+
display: 'grid',
|
|
34
|
+
height: 'min-content',
|
|
35
|
+
gap: theme.spacings.xxs,
|
|
36
|
+
},
|
|
37
|
+
'&.layoutList': {
|
|
38
|
+
display: 'grid',
|
|
39
|
+
height: 'min-content',
|
|
40
|
+
},
|
|
41
|
+
'&.layoutGrid': {
|
|
42
|
+
display: 'grid',
|
|
43
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
44
|
+
gap: theme.spacings.xxs,
|
|
45
|
+
},
|
|
46
|
+
'&.layoutInline': {
|
|
47
|
+
display: 'flex',
|
|
48
|
+
flexWrap: 'wrap',
|
|
49
|
+
gap: theme.spacings.xxs,
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
...(Array.isArray(props.sx) ? props.sx : [props.sx]),
|
|
53
|
+
]}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
},
|
|
57
|
+
)
|
|
@@ -3,6 +3,7 @@ import React from 'react'
|
|
|
3
3
|
import { isFragment } from 'react-is'
|
|
4
4
|
import { extendableComponent } from '../Styles'
|
|
5
5
|
import { ActionCardProps } from './ActionCard'
|
|
6
|
+
import { ActionCardLayout } from './ActionCardLayout'
|
|
6
7
|
|
|
7
8
|
type MultiSelect = {
|
|
8
9
|
multiple: true
|
|
@@ -132,52 +133,25 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
|
|
|
132
133
|
|
|
133
134
|
return (
|
|
134
135
|
<div>
|
|
135
|
-
<
|
|
136
|
-
className={classes.root}
|
|
137
|
-
ref={ref}
|
|
138
|
-
sx={[
|
|
139
|
-
(theme) => ({
|
|
140
|
-
'&.layoutStack': {
|
|
141
|
-
display: 'grid',
|
|
142
|
-
height: 'min-content',
|
|
143
|
-
gap: theme.spacings.xxs,
|
|
144
|
-
},
|
|
145
|
-
'&.layoutList': {
|
|
146
|
-
display: 'grid',
|
|
147
|
-
height: 'min-content',
|
|
148
|
-
},
|
|
149
|
-
'&.layoutGrid': {
|
|
150
|
-
display: 'grid',
|
|
151
|
-
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
152
|
-
gap: theme.spacings.xxs,
|
|
153
|
-
},
|
|
154
|
-
'&.layoutInline': {
|
|
155
|
-
display: 'flex',
|
|
156
|
-
flexWrap: 'wrap',
|
|
157
|
-
gap: theme.spacings.xxs,
|
|
158
|
-
},
|
|
159
|
-
}),
|
|
160
|
-
|
|
161
|
-
...(Array.isArray(sx) ? sx : [sx]),
|
|
162
|
-
]}
|
|
163
|
-
>
|
|
136
|
+
<ActionCardLayout sx={sx} className={classes.root} layout={layout}>
|
|
164
137
|
{childReactNodes.map((child) => {
|
|
165
138
|
if (collapse && Boolean(value) && !isValueSelected(child.props.value, value))
|
|
166
139
|
return null
|
|
167
140
|
return React.cloneElement(child, {
|
|
168
141
|
onClick: handleChange,
|
|
169
|
-
error
|
|
170
|
-
color
|
|
171
|
-
variant
|
|
172
|
-
size
|
|
173
|
-
layout
|
|
142
|
+
error,
|
|
143
|
+
color,
|
|
144
|
+
variant,
|
|
145
|
+
size,
|
|
146
|
+
layout,
|
|
147
|
+
...child.props,
|
|
174
148
|
selected:
|
|
175
149
|
child.props.selected === undefined
|
|
176
150
|
? isValueSelected(child.props.value, value)
|
|
177
151
|
: child.props.selected,
|
|
178
152
|
})
|
|
179
153
|
})}
|
|
180
|
-
</
|
|
154
|
+
</ActionCardLayout>
|
|
181
155
|
{error && errorMessage && (
|
|
182
156
|
<Alert
|
|
183
157
|
severity='error'
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.28.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`9e630670f`](https://github.com/graphcommerce-org/graphcommerce/commit/9e630670ff6c952ab7b938d890b5509804985cf3) Thanks [@paales](https://github.com/paales)! - Added a new ItemScroller component to be able to make horizontal product scrollers
|
|
8
|
+
|
|
9
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`2e9fa5984`](https://github.com/graphcommerce-org/graphcommerce/commit/2e9fa5984a07ff14fc1b3a4f62189a26e8e3ecdd) Thanks [@paales](https://github.com/paales)! - Measure the size of children of the overlay to determine the size of children
|
|
10
|
+
|
|
11
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`adf13069a`](https://github.com/graphcommerce-org/graphcommerce/commit/adf13069af6460c960276b402237371c12fc6dec) Thanks [@paales](https://github.com/paales)! - Use realtime measurements for useOverlayPosition instead of computed values, to improve flickering issues
|
|
12
|
+
|
|
13
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`1b1504c9b`](https://github.com/graphcommerce-org/graphcommerce/commit/1b1504c9b0e51f2787bce91e1ff1940f540411d6) Thanks [@paales](https://github.com/paales)! - Added crosssel functionality
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`81f31d1e5`](https://github.com/graphcommerce-org/graphcommerce/commit/81f31d1e54397368088a4289aaddd29facfceeef), [`a8905d263`](https://github.com/graphcommerce-org/graphcommerce/commit/a8905d263273cb9322583d5759a5fdc66eceb8e4), [`1b1504c9b`](https://github.com/graphcommerce-org/graphcommerce/commit/1b1504c9b0e51f2787bce91e1ff1940f540411d6), [`6c2e27b1b`](https://github.com/graphcommerce-org/graphcommerce/commit/6c2e27b1be4aaa888e65a2bd69eaeb467a54a023)]:
|
|
16
|
+
- @graphcommerce/framer-scroller@2.1.41
|
|
17
|
+
- @graphcommerce/framer-utils@3.2.1
|
|
18
|
+
- @graphcommerce/framer-next-pages@3.3.2
|
|
19
|
+
- @graphcommerce/image@3.1.10
|
|
20
|
+
|
|
3
21
|
## 4.28.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ScrollerProvider,
|
|
3
|
+
Scroller,
|
|
4
|
+
ScrollerButton,
|
|
5
|
+
ScrollerButtonProps,
|
|
6
|
+
} from '@graphcommerce/framer-scroller'
|
|
7
|
+
import { Box, SxProps, Theme } from '@mui/material'
|
|
8
|
+
import { IconSvg } from '../IconSvg'
|
|
9
|
+
import { extendableComponent, responsiveVal } from '../Styles'
|
|
10
|
+
import { useFabSize } from '../Theme'
|
|
11
|
+
import { iconChevronLeft, iconChevronRight } from '../icons'
|
|
12
|
+
|
|
13
|
+
const { classes } = extendableComponent('SidebarSlider', [
|
|
14
|
+
'root',
|
|
15
|
+
'grid',
|
|
16
|
+
'sidebar',
|
|
17
|
+
'scrollerContainer',
|
|
18
|
+
'scroller',
|
|
19
|
+
'sliderButtons',
|
|
20
|
+
'centerLeft',
|
|
21
|
+
'centerRight',
|
|
22
|
+
] as const)
|
|
23
|
+
|
|
24
|
+
type SliderProps = {
|
|
25
|
+
children: React.ReactNode
|
|
26
|
+
sx?: SxProps<Theme>
|
|
27
|
+
buttonSize?: ScrollerButtonProps['size']
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ItemScroller(props: SliderProps) {
|
|
31
|
+
const { children, sx, buttonSize = 'responsive' } = props
|
|
32
|
+
|
|
33
|
+
const size = useFabSize(buttonSize)
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Box sx={sx} className={classes.root}>
|
|
37
|
+
<Box sx={{ position: 'relative', minWidth: 1 }} className={classes.scrollerContainer}>
|
|
38
|
+
<ScrollerProvider scrollSnapAlign='start'>
|
|
39
|
+
<Scroller
|
|
40
|
+
className={classes.scroller}
|
|
41
|
+
hideScrollbar
|
|
42
|
+
sx={(theme) => ({
|
|
43
|
+
gridColumnGap: theme.spacings.md,
|
|
44
|
+
gridRowGap: theme.spacings.lg,
|
|
45
|
+
// paddingRight: theme.page.horizontal,
|
|
46
|
+
px: theme.page.horizontal,
|
|
47
|
+
scrollPaddingLeft: theme.page.horizontal,
|
|
48
|
+
scrollPaddingRight: theme.page.horizontal,
|
|
49
|
+
gridAutoColumns: responsiveVal(200, 300),
|
|
50
|
+
})}
|
|
51
|
+
>
|
|
52
|
+
{children}
|
|
53
|
+
</Scroller>
|
|
54
|
+
<Box
|
|
55
|
+
className={classes.centerLeft}
|
|
56
|
+
sx={(theme) => ({
|
|
57
|
+
display: 'grid',
|
|
58
|
+
gridAutoFlow: 'row',
|
|
59
|
+
gap: theme.spacings.xxs,
|
|
60
|
+
position: 'absolute',
|
|
61
|
+
left: theme.spacings.sm,
|
|
62
|
+
top: `calc(50% - 28px)`,
|
|
63
|
+
})}
|
|
64
|
+
>
|
|
65
|
+
<ScrollerButton
|
|
66
|
+
direction='left'
|
|
67
|
+
sx={{ display: { xs: 'none', md: 'flex' } }}
|
|
68
|
+
size='responsive'
|
|
69
|
+
>
|
|
70
|
+
<IconSvg src={iconChevronLeft} />
|
|
71
|
+
</ScrollerButton>
|
|
72
|
+
</Box>
|
|
73
|
+
<Box
|
|
74
|
+
className={classes.centerRight}
|
|
75
|
+
sx={(theme) => ({
|
|
76
|
+
display: 'grid',
|
|
77
|
+
gap: theme.spacings.xxs,
|
|
78
|
+
position: 'absolute',
|
|
79
|
+
right: theme.spacings.sm,
|
|
80
|
+
top: `calc(50% - (${size}/2))`,
|
|
81
|
+
})}
|
|
82
|
+
>
|
|
83
|
+
<ScrollerButton
|
|
84
|
+
direction='right'
|
|
85
|
+
sx={{ display: { xs: 'none', md: 'flex' } }}
|
|
86
|
+
size='responsive'
|
|
87
|
+
>
|
|
88
|
+
<IconSvg src={iconChevronRight} />
|
|
89
|
+
</ScrollerButton>
|
|
90
|
+
</Box>
|
|
91
|
+
</ScrollerProvider>
|
|
92
|
+
</Box>
|
|
93
|
+
</Box>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Scroller,
|
|
3
3
|
ScrollerButton,
|
|
4
|
-
ScrollerButtonProps
|
|
4
|
+
ScrollerButtonProps,
|
|
5
5
|
ScrollerPageCounter,
|
|
6
6
|
ScrollerProvider,
|
|
7
7
|
} from '@graphcommerce/framer-scroller'
|
|
@@ -28,7 +28,7 @@ export type SidebarSliderProps = {
|
|
|
28
28
|
children: ReactNode
|
|
29
29
|
sidebar: ReactNode
|
|
30
30
|
sx?: SxProps<Theme>
|
|
31
|
-
buttonSize?:
|
|
31
|
+
buttonSize?: ScrollerButtonProps['size']
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function SidebarSlider(props: SidebarSliderProps) {
|
package/FramerScroller/index.ts
CHANGED
|
@@ -25,7 +25,11 @@ export function LayoutOverlay(props: LayoutOverlayProps) {
|
|
|
25
25
|
const [isPresent, safeToRemove] = usePresence()
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<ScrollerProvider
|
|
28
|
+
<ScrollerProvider
|
|
29
|
+
scrollSnapTypeSm={scrollSnapTypeSm}
|
|
30
|
+
scrollSnapTypeMd={scrollSnapTypeMd}
|
|
31
|
+
_inititalSnap={false}
|
|
32
|
+
>
|
|
29
33
|
<OverlayBase
|
|
30
34
|
active={active}
|
|
31
35
|
direction={direction}
|
|
@@ -116,7 +116,7 @@ export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
|
|
|
116
116
|
sx={{
|
|
117
117
|
zIndex: 'drawer',
|
|
118
118
|
'& .LayoutOverlayBase-overlayPane': {
|
|
119
|
-
minWidth:
|
|
119
|
+
minWidth: itemWidthMd,
|
|
120
120
|
width: 'max-content',
|
|
121
121
|
overflow: 'hidden',
|
|
122
122
|
display: 'grid',
|
|
@@ -99,7 +99,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
99
99
|
)(th)
|
|
100
100
|
|
|
101
101
|
const { scrollerRef, snap } = useScrollerContext()
|
|
102
|
-
const positions = useOverlayPosition()
|
|
102
|
+
const positions = useOverlayPosition(variantSm, variantMd)
|
|
103
103
|
const scrollTo = useScrollTo()
|
|
104
104
|
const beforeRef = useRef<HTMLDivElement>(null)
|
|
105
105
|
|
|
@@ -148,7 +148,6 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
148
148
|
scroller.scrollLeft = positions.open.x.get()
|
|
149
149
|
scroller.scrollTop = positions.open.y.get()
|
|
150
150
|
}
|
|
151
|
-
|
|
152
151
|
if (positions.open.visible.get() === 0) {
|
|
153
152
|
scroller.scrollLeft = positions.closed.x.get()
|
|
154
153
|
scroller.scrollTop = positions.closed.y.get()
|
|
@@ -157,8 +156,6 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
157
156
|
|
|
158
157
|
window.addEventListener('resize', resize)
|
|
159
158
|
return () => window.removeEventListener('resize', resize)
|
|
160
|
-
// We're not checking for all deps, because that will cause rerenders.
|
|
161
|
-
// The scroller context shouldn't be changing, but at the moment it is.
|
|
162
159
|
}, [positions, scrollerRef])
|
|
163
160
|
|
|
164
161
|
// When the overlay is closed by navigating away, we're closing the overlay.
|
|
@@ -197,7 +194,13 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
197
194
|
useDomEvent(windowRef, 'keyup', handleEscape, { passive: true })
|
|
198
195
|
|
|
199
196
|
// When the overlay isn't visible anymore, we navigate back.
|
|
200
|
-
useEffect(
|
|
197
|
+
useEffect(
|
|
198
|
+
() =>
|
|
199
|
+
positions.open.visible.onChange(
|
|
200
|
+
(o) => position.get() !== OverlayPosition.OPENED && o === 0 && closeOverlay(),
|
|
201
|
+
),
|
|
202
|
+
[closeOverlay, position, positions.open.visible],
|
|
203
|
+
)
|
|
201
204
|
|
|
202
205
|
// Measure the offset of the overlay in the scroller.
|
|
203
206
|
const offsetY = useMotionValue(0)
|
|
@@ -345,7 +348,6 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
345
348
|
gridArea: 'overlay',
|
|
346
349
|
scrollSnapAlign: 'start',
|
|
347
350
|
scrollSnapStop: 'always',
|
|
348
|
-
|
|
349
351
|
[theme.breakpoints.down('md')]: {
|
|
350
352
|
justifyContent: justifySm,
|
|
351
353
|
alignItems: justifySm,
|
|
@@ -370,6 +372,9 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
370
372
|
'&.sizeMdFloating': {
|
|
371
373
|
padding: `${theme.page.vertical} ${theme.page.horizontal}`,
|
|
372
374
|
},
|
|
375
|
+
'&.sizeMdFloating.variantMdBottom': {
|
|
376
|
+
marginTop: `calc(${theme.page.vertical} * -1)`,
|
|
377
|
+
},
|
|
373
378
|
},
|
|
374
379
|
})}
|
|
375
380
|
>
|
|
@@ -380,7 +385,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
380
385
|
pointerEvents: 'all',
|
|
381
386
|
backgroundColor: theme.palette.background.paper,
|
|
382
387
|
boxShadow: theme.shadows[24],
|
|
383
|
-
|
|
388
|
+
|
|
384
389
|
[theme.breakpoints.down('md')]: {
|
|
385
390
|
minWidth: '80vw',
|
|
386
391
|
'&:not(.sizeMdFull)': {
|
|
@@ -394,6 +399,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
394
399
|
'&.variantSmBottom': {
|
|
395
400
|
borderTopLeftRadius: `${theme.shape.borderRadius * 3}px`,
|
|
396
401
|
borderTopRightRadius: `${theme.shape.borderRadius * 3}px`,
|
|
402
|
+
scrollSnapAlign: 'end',
|
|
397
403
|
},
|
|
398
404
|
'&.sizeSmFloating': {
|
|
399
405
|
borderRadius: `${theme.shape.borderRadius * 3}px`,
|
|
@@ -409,15 +415,11 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
409
415
|
},
|
|
410
416
|
},
|
|
411
417
|
[theme.breakpoints.up('md')]: {
|
|
412
|
-
'
|
|
413
|
-
minWidth: 'max(600px, 50vw)',
|
|
414
|
-
},
|
|
415
|
-
'&:not(.sizeMdFull)': {
|
|
416
|
-
width: 'max-content',
|
|
417
|
-
},
|
|
418
|
+
minWidth: '1px',
|
|
418
419
|
|
|
419
420
|
'&.sizeMdFull.variantMdBottom': {
|
|
420
421
|
minHeight: `calc(${clientSizeCssVar.y} - ${mdSpacingTop})`,
|
|
422
|
+
scrollSnapAlign: 'end',
|
|
421
423
|
},
|
|
422
424
|
'&.sizeMdFull.variantMdLeft': {
|
|
423
425
|
paddingBottom: '1px',
|
|
@@ -433,6 +435,10 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
|
|
|
433
435
|
borderTopLeftRadius: `${theme.shape.borderRadius * 4}px`,
|
|
434
436
|
borderTopRightRadius: `${theme.shape.borderRadius * 4}px`,
|
|
435
437
|
},
|
|
438
|
+
'&.variantMdLeft, &.variantMdRight': {
|
|
439
|
+
width: 'max-content',
|
|
440
|
+
},
|
|
441
|
+
|
|
436
442
|
'&.sizeMdFloating': {
|
|
437
443
|
borderRadius: `${theme.shape.borderRadius * 4}px`,
|
|
438
444
|
},
|
|
@@ -5,71 +5,101 @@ import {
|
|
|
5
5
|
useIsomorphicLayoutEffect,
|
|
6
6
|
} from '@graphcommerce/framer-utils'
|
|
7
7
|
import { motionValue } from 'framer-motion'
|
|
8
|
-
import { useEffect } from 'react'
|
|
8
|
+
import { useCallback, useEffect } from 'react'
|
|
9
|
+
import { useMatchMedia } from '../../hooks'
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
const { getScrollSnapPositions, scrollerRef } = useScrollerContext()
|
|
11
|
+
const clampRound = (value: number) => Math.round(Math.max(0, Math.min(1, value)) * 100) / 100
|
|
12
12
|
|
|
13
|
+
export function useOverlayPosition(
|
|
14
|
+
variantSm: 'left' | 'bottom' | 'right',
|
|
15
|
+
variantMd: 'left' | 'bottom' | 'right',
|
|
16
|
+
) {
|
|
17
|
+
const match = useMatchMedia()
|
|
18
|
+
const { getScrollSnapPositions, scrollerRef } = useScrollerContext()
|
|
13
19
|
const state = useConstant(() => ({
|
|
14
20
|
open: {
|
|
15
21
|
x: motionValue(0),
|
|
16
22
|
y: motionValue(0),
|
|
17
23
|
visible: motionValue(0),
|
|
18
24
|
},
|
|
19
|
-
closed: {
|
|
25
|
+
closed: {
|
|
26
|
+
x: motionValue(0),
|
|
27
|
+
y: motionValue(0),
|
|
28
|
+
},
|
|
20
29
|
}))
|
|
21
30
|
|
|
22
31
|
const scroll = useElementScroll(scrollerRef)
|
|
23
32
|
|
|
33
|
+
const variant = useCallback(
|
|
34
|
+
() => (match.up('md') ? variantMd : variantSm),
|
|
35
|
+
[match, variantMd, variantSm],
|
|
36
|
+
)
|
|
37
|
+
|
|
24
38
|
useIsomorphicLayoutEffect(() => {
|
|
25
39
|
if (!scrollerRef.current) return () => {}
|
|
26
40
|
|
|
27
41
|
const measure = () => {
|
|
28
42
|
const positions = getScrollSnapPositions()
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
if (variant() === 'left') {
|
|
45
|
+
state.open.x.set(0)
|
|
46
|
+
state.closed.x.set(positions.x[positions.x.length - 1] ?? 0)
|
|
47
|
+
}
|
|
48
|
+
if (variant() === 'right') {
|
|
49
|
+
state.open.x.set(positions.x[positions.x.length - 1] ?? 0)
|
|
50
|
+
state.closed.x.set(0)
|
|
51
|
+
}
|
|
52
|
+
if (variant() === 'bottom') {
|
|
53
|
+
state.open.y.set(positions.y[positions.y.length - 1] ?? 0)
|
|
54
|
+
state.closed.y.set(0)
|
|
55
|
+
}
|
|
34
56
|
}
|
|
35
|
-
const ro = new ResizeObserver(measure)
|
|
36
57
|
measure()
|
|
37
58
|
|
|
59
|
+
const ro = new ResizeObserver(measure)
|
|
38
60
|
ro.observe(scrollerRef.current)
|
|
61
|
+
;[...scrollerRef.current.children].forEach((child) => ro.observe(child))
|
|
62
|
+
|
|
39
63
|
return () => ro.disconnect()
|
|
40
|
-
})
|
|
64
|
+
}, [getScrollSnapPositions, scrollerRef, state, variant])
|
|
41
65
|
|
|
42
66
|
// sets a float between 0 and 1 for the visibility of the overlay
|
|
43
67
|
useEffect(() => {
|
|
68
|
+
if (!scrollerRef.current) return () => {}
|
|
44
69
|
const calc = () => {
|
|
45
|
-
const x = scroll.x.get()
|
|
46
|
-
const y = scroll.y.get()
|
|
47
|
-
|
|
48
|
-
const yC = state.closed.y.get()
|
|
49
|
-
const yO = state.open.y.get()
|
|
50
|
-
const visY = yC === yO ? 1 : Math.max(0, Math.min(1, (y - yC) / (yO - yC)))
|
|
51
|
-
|
|
52
|
-
const xC = state.closed.x.get()
|
|
53
|
-
const xO = state.open.x.get()
|
|
70
|
+
const x = scrollerRef.current?.scrollLeft ?? scroll.x.get()
|
|
71
|
+
const y = scrollerRef.current?.scrollTop ?? scroll.y.get()
|
|
54
72
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
let vis = visY * visX
|
|
58
|
-
if (xC === 0 && xO === 0 && yC === 0 && yO === 0) vis = 0
|
|
73
|
+
const positions = getScrollSnapPositions()
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
if (variant() === 'left') {
|
|
76
|
+
const closedX = positions.x[1] ?? 0
|
|
77
|
+
state.open.visible.set(closedX === 0 ? 0 : clampRound((x - closedX) / -closedX))
|
|
78
|
+
}
|
|
79
|
+
if (variant() === 'right') {
|
|
80
|
+
const openedX = positions.x[1] ?? 0
|
|
81
|
+
state.open.visible.set(openedX === 0 ? 0 : clampRound(x / openedX))
|
|
82
|
+
}
|
|
83
|
+
if (variant() === 'bottom') {
|
|
84
|
+
const openedY = positions.y[1] ?? 0
|
|
85
|
+
state.open.visible.set(openedY === 0 ? 0 : clampRound(y / openedY))
|
|
86
|
+
}
|
|
62
87
|
}
|
|
63
88
|
|
|
64
89
|
const cancelY = scroll.y.onChange(calc)
|
|
65
90
|
const cancelX = scroll.x.onChange(calc)
|
|
66
91
|
calc()
|
|
67
92
|
|
|
93
|
+
const ro = new ResizeObserver(calc)
|
|
94
|
+
ro.observe(scrollerRef.current)
|
|
95
|
+
;[...scrollerRef.current.children].forEach((child) => ro.observe(child))
|
|
96
|
+
|
|
68
97
|
return () => {
|
|
69
98
|
cancelY()
|
|
70
99
|
cancelX()
|
|
100
|
+
ro.disconnect()
|
|
71
101
|
}
|
|
72
|
-
}, [state,
|
|
102
|
+
}, [getScrollSnapPositions, scroll, scrollerRef, state, variant])
|
|
73
103
|
|
|
74
104
|
return state
|
|
75
105
|
}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import type { OptionalKeysOf, Simplify } from 'type-fest'
|
|
2
2
|
|
|
3
|
+
export type RequiredKeys<
|
|
4
|
+
T extends Record<string, unknown>,
|
|
5
|
+
Keys extends OptionalKeysOf<T>,
|
|
6
|
+
> = Simplify<
|
|
7
|
+
Omit<T, Keys> & {
|
|
8
|
+
[K in Keys]: NonNullable<T[K]>
|
|
9
|
+
}
|
|
10
|
+
>
|
|
11
|
+
|
|
3
12
|
export function filterNonNullableKeys<
|
|
4
13
|
T extends Record<string, unknown>,
|
|
5
14
|
Keys extends OptionalKeysOf<T>,
|
|
6
15
|
>(items: (T | null | undefined)[] | null | undefined, values: Keys[] = []) {
|
|
7
16
|
if (!items) return []
|
|
8
17
|
|
|
9
|
-
type ResultWithRequired = Simplify<
|
|
10
|
-
Omit<T, Keys> & {
|
|
11
|
-
[K in Keys]: NonNullable<T[K]>
|
|
12
|
-
}
|
|
13
|
-
>
|
|
14
|
-
|
|
15
18
|
const result = items.filter(
|
|
16
19
|
(item) => item !== null && typeof item !== 'undefined' && values.every((v) => item?.[v]),
|
|
17
20
|
)
|
|
18
21
|
|
|
19
|
-
return result as
|
|
22
|
+
return result as RequiredKeys<T, Keys>[]
|
|
20
23
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i18n } from '@lingui/core'
|
|
2
2
|
import {
|
|
3
3
|
Fab,
|
|
4
4
|
Snackbar,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from '@mui/material'
|
|
13
13
|
import React, { useEffect, useState } from 'react'
|
|
14
14
|
import { IconSvg } from '../IconSvg'
|
|
15
|
-
import { extendableComponent } from '../Styles'
|
|
15
|
+
import { extendableComponent, breakpointVal } from '../Styles'
|
|
16
16
|
import { iconClose, iconCheckmark, iconSadFace } from '../icons'
|
|
17
17
|
|
|
18
18
|
type Size = 'normal' | 'wide'
|
|
@@ -114,7 +114,8 @@ export default function MessageSnackbarImpl(props: MessageSnackbarImplProps) {
|
|
|
114
114
|
alignItems: 'center',
|
|
115
115
|
gap: theme.spacings.xxs,
|
|
116
116
|
gridTemplate: {
|
|
117
|
-
xs: `"icon children close"
|
|
117
|
+
xs: `"icon children close"
|
|
118
|
+
"action action action"`,
|
|
118
119
|
md: '"icon children action close"',
|
|
119
120
|
},
|
|
120
121
|
gridTemplateColumns: {
|
|
@@ -139,7 +140,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarImplProps) {
|
|
|
139
140
|
)}
|
|
140
141
|
<Fab
|
|
141
142
|
className={classes.close}
|
|
142
|
-
aria-label='Close'
|
|
143
|
+
aria-label={i18n._(/* i18n */ 'Close')}
|
|
143
144
|
size='small'
|
|
144
145
|
onClick={hideSnackbar}
|
|
145
146
|
onMouseDown={preventAnimationBubble}
|
package/index.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export * from './ActionCard
|
|
2
|
-
export * from './ActionCard/ActionCardList'
|
|
3
|
-
export * from './ActionCard/ActionCardListForm'
|
|
1
|
+
export * from './ActionCard'
|
|
4
2
|
export * from './AnimatedRow/AnimatedRow'
|
|
5
3
|
export * from './Blog/BlogAuthor/BlogAuthor'
|
|
6
4
|
export * from './Blog/BlogContent/BlogContent'
|
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.28.
|
|
5
|
+
"version": "4.28.1",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
"@emotion/react": "^11.9.3",
|
|
20
20
|
"@emotion/server": "^11.4.0",
|
|
21
21
|
"@emotion/styled": "^11.9.3",
|
|
22
|
-
"@graphcommerce/framer-next-pages": "3.3.
|
|
23
|
-
"@graphcommerce/framer-scroller": "2.1.
|
|
24
|
-
"@graphcommerce/framer-utils": "3.2.
|
|
25
|
-
"@graphcommerce/image": "3.1.
|
|
22
|
+
"@graphcommerce/framer-next-pages": "3.3.2",
|
|
23
|
+
"@graphcommerce/framer-scroller": "2.1.41",
|
|
24
|
+
"@graphcommerce/framer-utils": "3.2.1",
|
|
25
|
+
"@graphcommerce/image": "3.1.10",
|
|
26
26
|
"cookie": "^0.5.0",
|
|
27
27
|
"react-is": "^18.2.0",
|
|
28
28
|
"schema-dts": "^1.1.0"
|