@graphcommerce/next-ui 8.1.0-canary.3 → 8.1.0-canary.30

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.
Files changed (38) hide show
  1. package/ActionCard/ActionCard.tsx +27 -3
  2. package/ActionCard/ActionCardListForm.tsx +41 -33
  3. package/Breadcrumbs/Breadcrumbs.tsx +195 -0
  4. package/Breadcrumbs/BreadcrumbsJsonLd.tsx +13 -0
  5. package/Breadcrumbs/BreadcrumbsList.tsx +101 -0
  6. package/Breadcrumbs/BreadcrumbsPopper.tsx +48 -0
  7. package/Breadcrumbs/index.ts +4 -0
  8. package/Breadcrumbs/jsonLdBreadcrumb.tsx +19 -0
  9. package/Breadcrumbs/types.ts +11 -0
  10. package/CHANGELOG.md +203 -2
  11. package/Config.graphqls +5 -0
  12. package/FramerScroller/SidebarGallery.tsx +48 -28
  13. package/JsonLd/JsonLd.tsx +3 -2
  14. package/Layout/components/LayoutHeader.tsx +3 -0
  15. package/Layout/components/LayoutTitle.tsx +0 -5
  16. package/LayoutOverlay/test/LayoutOverlayDemo.tsx +1 -0
  17. package/LazyHydrate/LazyHydrate.tsx +17 -7
  18. package/Navigation/components/NavigationOverlay.tsx +1 -0
  19. package/Overlay/components/OverlayBase.tsx +5 -3
  20. package/Overlay/components/OverlaySsr.tsx +1 -0
  21. package/PageMeta/PageMeta.tsx +9 -4
  22. package/Styles/createEmotionCache.ts +1 -1
  23. package/Theme/DarkLightModeThemeProvider.tsx +41 -19
  24. package/TimeAgo/TimeAgo.tsx +8 -2
  25. package/hooks/index.ts +2 -0
  26. package/hooks/useDateTimeFormat.ts +3 -7
  27. package/hooks/useLocale.ts +7 -0
  28. package/hooks/useMatchMedia.ts +20 -1
  29. package/hooks/useNumberFormat.ts +3 -8
  30. package/hooks/useSsr.ts +11 -0
  31. package/icons.ts +50 -0
  32. package/index.ts +4 -0
  33. package/package.json +10 -9
  34. package/types.d.ts +0 -6
  35. package/utils/cssFlags.tsx +76 -0
  36. package/utils/robots.ts +41 -0
  37. package/utils/sitemap.ts +47 -0
  38. package/icons/index.ts +0 -48
@@ -13,7 +13,7 @@ import { extendableComponent, responsiveVal } from '../Styles'
13
13
  import { breakpointVal } from '../Styles/breakpointVal'
14
14
 
15
15
  type Variants = 'outlined' | 'default'
16
- type Size = 'large' | 'medium' | 'small'
16
+ type Size = 'large' | 'medium' | 'small' | 'responsive'
17
17
  type Color = 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'
18
18
  type Layout = 'inline' | 'grid' | 'list' | 'stack'
19
19
 
@@ -47,6 +47,7 @@ export type ActionCardProps = {
47
47
 
48
48
  const parts = [
49
49
  'root',
50
+ 'rootInner',
50
51
  'image',
51
52
  'title',
52
53
  'action',
@@ -77,6 +78,13 @@ const { withState, selectors } = extendableComponent<StateProps, typeof name, ty
77
78
 
78
79
  export const actionCardSelectors = selectors
79
80
 
81
+ export const actionCardImageSizes = {
82
+ small: responsiveVal(60, 80),
83
+ medium: responsiveVal(60, 80),
84
+ large: responsiveVal(100, 120),
85
+ responsive: responsiveVal(60, 120),
86
+ }
87
+
80
88
  export function ActionCard(props: ActionCardProps) {
81
89
  const {
82
90
  title,
@@ -92,7 +100,7 @@ export function ActionCard(props: ActionCardProps) {
92
100
  selected = false,
93
101
  reset,
94
102
  disabled = false,
95
- size = 'medium',
103
+ size = 'responsive',
96
104
  color = 'primary',
97
105
  variant = 'outlined',
98
106
  layout = 'list',
@@ -140,6 +148,12 @@ export function ActionCard(props: ActionCardProps) {
140
148
  py: responsiveVal(12, 14),
141
149
  display: 'block',
142
150
  },
151
+ '&.sizeResponsive': {
152
+ px: responsiveVal(8, 16),
153
+ py: responsiveVal(4, 14),
154
+ display: { xs: 'flex', md: 'block', lg: 'block' },
155
+ [theme.breakpoints.down('md')]: { typography: 'body2' },
156
+ },
143
157
 
144
158
  '&.variantDefault': {
145
159
  position: 'relative',
@@ -186,6 +200,13 @@ export function ActionCard(props: ActionCardProps) {
186
200
  mb: { xs: '-5px', sm: '-7px', md: '-8px' },
187
201
  },
188
202
  },
203
+ '&.sizeResponsive': {
204
+ mt: responsiveVal(2, 8),
205
+ mb: responsiveVal(3, 9),
206
+ '&::after': {
207
+ mb: responsiveVal(-2, -8),
208
+ },
209
+ },
189
210
  },
190
211
 
191
212
  '&.variantOutlined': {
@@ -261,6 +282,7 @@ export function ActionCard(props: ActionCardProps) {
261
282
  ]}
262
283
  >
263
284
  <Box
285
+ className={classes.rootInner}
264
286
  sx={{
265
287
  display: 'flex',
266
288
  flexDirection: 'row',
@@ -301,6 +323,7 @@ export function ActionCard(props: ActionCardProps) {
301
323
  '&.sizeSmall': { typography: 'body2' },
302
324
  '&.sizeMedium': { typography: 'body1' },
303
325
  '&.sizeLarge': { typography: 'h6' },
326
+ '&.sizeResponsive': { typography: { xs: 'body2', md: 'body1', lg: 'h6' } },
304
327
  }}
305
328
  >
306
329
  {title}
@@ -341,8 +364,9 @@ export function ActionCard(props: ActionCardProps) {
341
364
  sx={{
342
365
  textAlign: 'right',
343
366
  typography: 'body1',
344
- '&.sizeMedium': { typographty: 'subtitle1' },
367
+ '&.sizeMedium': { typography: 'subtitle1' },
345
368
  '&.sizeLarge': { typography: 'h6' },
369
+ '&.sizeResponsive': { typography: { xs: 'body1', md: 'subtitle1', lg: 'h6' } },
346
370
  }}
347
371
  >
348
372
  {price}
@@ -1,5 +1,10 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies */
2
- import { Controller, ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
2
+ import {
3
+ Controller,
4
+ ControllerProps,
5
+ FieldValues,
6
+ useController,
7
+ } from '@graphcommerce/react-hook-form'
3
8
  import React, { MouseEventHandler } from 'react'
4
9
  import { ActionCardProps } from './ActionCard'
5
10
  import { ActionCardList, ActionCardListProps } from './ActionCardList'
@@ -43,38 +48,41 @@ export function ActionCardListForm<
43
48
  : selectValues === itemValue
44
49
  }
45
50
 
51
+ const {
52
+ field: { onChange, value, ref },
53
+ fieldState,
54
+ formState,
55
+ } = useController({
56
+ ...props,
57
+ control,
58
+ name,
59
+ defaultValue,
60
+ rules: { required: errorMessage || required, ...rules },
61
+ })
62
+
46
63
  return (
47
- <Controller
48
- {...props}
49
- control={control}
50
- name={name}
51
- defaultValue={defaultValue}
52
- rules={{ required: errorMessage || required, ...rules }}
53
- render={({ field: { onChange, value, ref }, fieldState, formState }) => (
54
- <ActionCardList
55
- {...other}
56
- multiple={multiple}
57
- required={required}
58
- value={value}
59
- ref={ref}
60
- onChange={(_, incomming) => onChange(incomming)}
61
- error={formState.isSubmitted && !!fieldState.error}
62
- errorMessage={fieldState.error?.message}
63
- >
64
- {items.map((item) => (
65
- <RenderItem
66
- {...item}
67
- key={item.value ?? ''}
68
- value={item.value}
69
- selected={onSelect(item.value, value)}
70
- onReset={(e) => {
71
- e.preventDefault()
72
- onChange(null)
73
- }}
74
- />
75
- ))}
76
- </ActionCardList>
77
- )}
78
- />
64
+ <ActionCardList
65
+ {...other}
66
+ multiple={multiple}
67
+ required={required}
68
+ value={value}
69
+ ref={ref}
70
+ onChange={(_, incomming) => onChange(incomming)}
71
+ error={formState.isSubmitted && !!fieldState.error}
72
+ errorMessage={fieldState.error?.message}
73
+ >
74
+ {items.map((item) => (
75
+ <RenderItem
76
+ {...item}
77
+ key={item.value ?? ''}
78
+ value={item.value}
79
+ selected={onSelect(item.value, value)}
80
+ onReset={(e) => {
81
+ e.preventDefault()
82
+ onChange(null)
83
+ }}
84
+ />
85
+ ))}
86
+ </ActionCardList>
79
87
  )
80
88
  }
@@ -0,0 +1,195 @@
1
+ import { Trans } from '@lingui/react'
2
+ import {
3
+ Box,
4
+ Breadcrumbs as MuiBreadcrumbs,
5
+ BreadcrumbsProps as MuiBreadcrumbProps,
6
+ ClickAwayListener,
7
+ IconButton,
8
+ Link,
9
+ Typography,
10
+ useEventCallback,
11
+ useTheme,
12
+ SxProps,
13
+ Theme,
14
+ LinkProps,
15
+ } from '@mui/material'
16
+ import dynamic from 'next/dynamic'
17
+ import { useState, MouseEvent } from 'react'
18
+ import { IconSvg } from '../IconSvg'
19
+ import { iconClose, iconEllypsis } from '../icons'
20
+ import type { BreadcrumbsType } from './types'
21
+ import { i18n } from '@lingui/core'
22
+ import { Button } from '../Button'
23
+
24
+ const BreadcrumbsPopper = dynamic(
25
+ async () => (await import('./BreadcrumbsPopper')).BreadcrumbsPopper,
26
+ )
27
+
28
+ export type BreadcrumbsProps = BreadcrumbsType &
29
+ Omit<MuiBreadcrumbProps, 'children'> & {
30
+ maxItems?: number
31
+ lastIsLink?: boolean
32
+ breadcrumbsAmountDesktop?: number
33
+ breadcrumbsAmountMobile?: number
34
+ itemSx?: SxProps<Theme>
35
+ linkProps?: Omit<LinkProps, 'href'>
36
+ }
37
+
38
+ export function Breadcrumbs(props: BreadcrumbsProps) {
39
+ const {
40
+ breadcrumbs,
41
+ sx,
42
+ breadcrumbsAmountDesktop = 4,
43
+ breadcrumbsAmountMobile = 2,
44
+ lastIsLink = false,
45
+ maxItems,
46
+ itemSx = [],
47
+ linkProps,
48
+ ...rest
49
+ } = props
50
+ const [anchorElement, setAnchorElement] = useState<HTMLButtonElement | null>(null)
51
+ const theme = useTheme()
52
+
53
+ const isDefaultMobile = breadcrumbsAmountMobile === 0
54
+ const showButtonMobile = breadcrumbs.length > breadcrumbsAmountMobile && !isDefaultMobile
55
+ const isDefaultDesktop = breadcrumbsAmountDesktop === 0
56
+ const showButtonDesktop = breadcrumbs.length > breadcrumbsAmountDesktop && !isDefaultDesktop
57
+
58
+ const handleClick = useEventCallback((event: MouseEvent<HTMLButtonElement>) => {
59
+ setAnchorElement((el) => (el !== event.currentTarget ? event.currentTarget : null))
60
+ })
61
+
62
+ const handleClose = () => setAnchorElement(null)
63
+
64
+ const breadcrumbLinks = [...(lastIsLink ? breadcrumbs : breadcrumbs.slice(0, -1))]
65
+ const last = lastIsLink ? null : breadcrumbs[breadcrumbs.length - 1]
66
+
67
+ return (
68
+ <MuiBreadcrumbs
69
+ {...rest}
70
+ aria-label={i18n._(/* i18n*/ `Breadcrumbs`)}
71
+ maxItems={maxItems}
72
+ color='inherit'
73
+ sx={[
74
+ {},
75
+ !maxItems && {
76
+ '& .MuiBreadcrumbs-ol': {
77
+ flexWrap: 'nowrap',
78
+ '& .MuiBreadcrumbs-li': {
79
+ '&:nth-of-type(1)': {
80
+ display: {
81
+ xs: showButtonMobile ? 'flex' : 'none',
82
+ md: showButtonDesktop ? 'flex' : 'none',
83
+ },
84
+ },
85
+ '&:nth-last-of-type(1)': {
86
+ display: 'inline-flex',
87
+ overflowX: 'hidden',
88
+ },
89
+ },
90
+ },
91
+ '& .MuiBreadcrumbs-separator': {
92
+ '&:nth-of-type(2)': {
93
+ display: {
94
+ xs: !showButtonMobile && 'none',
95
+ md: !showButtonDesktop && 'none',
96
+ },
97
+ },
98
+ },
99
+
100
+ [theme.breakpoints.down('md')]: showButtonMobile && {
101
+ '& .MuiBreadcrumbs-li, & .MuiBreadcrumbs-separator': {
102
+ display: 'none',
103
+ [`&:nth-last-of-type(-n+${breadcrumbsAmountMobile * 2})`]: {
104
+ display: 'flex',
105
+ },
106
+ },
107
+ },
108
+
109
+ [theme.breakpoints.up('md')]: showButtonDesktop && {
110
+ '& .MuiBreadcrumbs-li, & .MuiBreadcrumbs-separator': {
111
+ display: 'none',
112
+ [`&:nth-last-of-type(-n+${breadcrumbsAmountDesktop * 2})`]: {
113
+ display: 'flex',
114
+ },
115
+ },
116
+ },
117
+ },
118
+ ...(Array.isArray(sx) ? sx : [sx]),
119
+ ]}
120
+ >
121
+ {!maxItems && (
122
+ <ClickAwayListener
123
+ mouseEvent='onMouseDown'
124
+ touchEvent='onTouchStart'
125
+ onClickAway={handleClose}
126
+ >
127
+ <Box sx={{ position: 'relative', display: 'flex' }}>
128
+ <Button
129
+ aria-describedby={anchorElement ? 'breadcrumb-list' : undefined}
130
+ color='inherit'
131
+ variant='pill'
132
+ size='small'
133
+ onClick={handleClick}
134
+ sx={{
135
+ minWidth: 0,
136
+ // borderRadius: 2,
137
+ // boxShadow: 6,
138
+ // color: 'text.primary',
139
+ // px: 1,
140
+ // py: { xs: 0.3, md: 0.5 },
141
+ // typography: 'caption',
142
+ // backgroundColor: 'background.paper',
143
+ }}
144
+ >
145
+ <IconSvg src={anchorElement ? iconClose : iconEllypsis} />
146
+ </Button>
147
+ <BreadcrumbsPopper
148
+ breadcrumbs={breadcrumbs}
149
+ anchorElement={anchorElement}
150
+ onClose={handleClose}
151
+ showDesktopAmount={breadcrumbsAmountDesktop}
152
+ showMobileAmount={breadcrumbsAmountMobile}
153
+ />
154
+ </Box>
155
+ </ClickAwayListener>
156
+ )}
157
+
158
+ <Link
159
+ href='/'
160
+ color='inherit'
161
+ underline='hover'
162
+ {...linkProps}
163
+ sx={[...(Array.isArray(itemSx) ? itemSx : [itemSx])]}
164
+ >
165
+ <Trans id='Home' />
166
+ </Link>
167
+ {breadcrumbLinks.map((breadcrumb) => (
168
+ <Link
169
+ key={breadcrumb.href}
170
+ color='inherit'
171
+ underline='hover'
172
+ {...breadcrumb}
173
+ {...linkProps}
174
+ sx={[
175
+ ...(Array.isArray(breadcrumb.sx) ? breadcrumb.sx : [breadcrumb.sx]),
176
+ ...(Array.isArray(itemSx) ? itemSx : [itemSx]),
177
+ ]}
178
+ >
179
+ {breadcrumb.name}
180
+ </Link>
181
+ ))}
182
+
183
+ {last && (
184
+ <Typography
185
+ component='span'
186
+ noWrap
187
+ color='inherit'
188
+ sx={[{ fontWeight: '600' }, ...(Array.isArray(itemSx) ? itemSx : [itemSx])]}
189
+ >
190
+ {last.name}
191
+ </Typography>
192
+ )}
193
+ </MuiBreadcrumbs>
194
+ )
195
+ }
@@ -0,0 +1,13 @@
1
+ import { JsonLd } from '../JsonLd/JsonLd'
2
+ import type { BreadcrumbsType } from './types'
3
+
4
+ type BreadcrumbsJsonLdProps<T extends { '@type': string }> = BreadcrumbsType & {
5
+ render: (breadcrumbs: BreadcrumbsType['breadcrumbs']) => T & { '@context': 'https://schema.org' }
6
+ }
7
+
8
+ export function BreadcrumbsJsonLd<T extends { '@type': string }>(props: BreadcrumbsJsonLdProps<T>) {
9
+ const { render, breadcrumbs } = props
10
+
11
+ if (!breadcrumbs.length) return null
12
+ return <JsonLd<T> item={render(breadcrumbs)} keyVal='breadcrumb-jsonld' />
13
+ }
@@ -0,0 +1,101 @@
1
+ import { Trans } from '@lingui/react'
2
+ import { Box, Link, alpha, useTheme } from '@mui/material'
3
+ import { useEffect, useRef, KeyboardEvent } from 'react'
4
+ import type { BreadcrumbsType } from './types'
5
+
6
+ type PopperBreadcrumbsListProps = {
7
+ autoFocus: boolean
8
+ breadcrumbs: BreadcrumbsType['breadcrumbs']
9
+ showDesktopAmount?: number
10
+ showMobileAmount?: number
11
+ onClose: () => void
12
+ }
13
+
14
+ export function BreadcrumbsList(props: PopperBreadcrumbsListProps) {
15
+ const { autoFocus, breadcrumbs, showDesktopAmount = 4, showMobileAmount = 3, onClose } = props
16
+ const listRef = useRef<HTMLDivElement | null>(null)
17
+ const theme = useTheme()
18
+
19
+ const handleKeyDown = (event: KeyboardEvent) => {
20
+ if (event.key === 'Escape') {
21
+ onClose()
22
+ }
23
+ }
24
+
25
+ useEffect(() => {
26
+ if (autoFocus) {
27
+ listRef.current?.focus()
28
+ }
29
+ }, [autoFocus])
30
+
31
+ return (
32
+ <Box
33
+ ref={listRef}
34
+ onKeyDown={handleKeyDown}
35
+ tabIndex={-1}
36
+ sx={{
37
+ backgroundColor: 'background.paper',
38
+ borderRadius: 3,
39
+ boxShadow: 12,
40
+ display: 'flex',
41
+ flexDirection: 'column',
42
+ overflow: 'hidden auto',
43
+ py: `calc(${theme.spacings.xxs} / 2)`,
44
+ [theme.breakpoints.up('md')]: {
45
+ '& .MuiLink-root': {
46
+ [`:nth-last-of-type(-n+${showDesktopAmount})`]: {
47
+ display: 'none',
48
+ },
49
+ },
50
+ },
51
+ [theme.breakpoints.down('md')]: {
52
+ '& .MuiLink-root': {
53
+ [`:nth-last-of-type(-n+${showMobileAmount})`]: {
54
+ display: 'none',
55
+ },
56
+ },
57
+ },
58
+ }}
59
+ >
60
+ <Link
61
+ href='/'
62
+ underline='none'
63
+ color='text.primary'
64
+ variant='body1'
65
+ noWrap
66
+ onClick={onClose}
67
+ tabIndex={0}
68
+ sx={{
69
+ flex: 1,
70
+ padding: `calc(${theme.spacings.xxs} / 2) ${theme.spacings.xs}`,
71
+ '&:hover': {
72
+ backgroundColor: alpha(theme.palette.action.hover, 0.025),
73
+ },
74
+ }}
75
+ >
76
+ <Trans id='Home' />
77
+ </Link>
78
+ {breadcrumbs.slice(0, breadcrumbs.length).map((breadcrumb) => (
79
+ <Link
80
+ {...breadcrumb}
81
+ key={breadcrumb.href}
82
+ underline='none'
83
+ color='text.primary'
84
+ variant='body1'
85
+ noWrap
86
+ onClick={onClose}
87
+ tabIndex={0}
88
+ sx={{
89
+ flex: 1,
90
+ padding: `calc(${theme.spacings.xxs} / 2) ${theme.spacings.xs}`,
91
+ '&:hover': {
92
+ backgroundColor: alpha(theme.palette.action.hover, 0.025),
93
+ },
94
+ }}
95
+ >
96
+ {breadcrumb.name}
97
+ </Link>
98
+ ))}
99
+ </Box>
100
+ )
101
+ }
@@ -0,0 +1,48 @@
1
+ import { Box, Popper } from '@mui/material'
2
+ import { BreadcrumbsList } from './BreadcrumbsList'
3
+ import type { BreadcrumbsType } from './types'
4
+
5
+ export type BreadcrumbsPopperType = BreadcrumbsType & {
6
+ anchorElement: HTMLButtonElement | null
7
+ showDesktopAmount?: number
8
+ showMobileAmount?: number
9
+ onClose: () => void
10
+ }
11
+
12
+ export function BreadcrumbsPopper(props: BreadcrumbsPopperType) {
13
+ const { anchorElement, breadcrumbs, showDesktopAmount, showMobileAmount, onClose } = props
14
+
15
+ return (
16
+ <Popper
17
+ anchorEl={anchorElement}
18
+ open={Boolean(anchorElement)}
19
+ disablePortal
20
+ placement='bottom-start'
21
+ modifiers={[
22
+ { name: 'offset', options: { offset: [0, 10] } },
23
+ {
24
+ name: 'preventOverflow',
25
+ enabled: true,
26
+ options: { altBoundary: false, padding: 10 },
27
+ },
28
+ ]}
29
+ sx={(theme) => ({
30
+ maxWidth: {
31
+ md: `calc(100vw - ${theme.spacings.xxl} * 2)`,
32
+ xs: `calc(100vw - ${theme.page.horizontal} * 2)`,
33
+ },
34
+ zIndex: 100,
35
+ })}
36
+ >
37
+ <Box>
38
+ <BreadcrumbsList
39
+ autoFocus={Boolean(anchorElement)}
40
+ breadcrumbs={breadcrumbs}
41
+ showDesktopAmount={showDesktopAmount}
42
+ showMobileAmount={showMobileAmount}
43
+ onClose={onClose}
44
+ />
45
+ </Box>
46
+ </Popper>
47
+ )
48
+ }
@@ -0,0 +1,4 @@
1
+ export * from './Breadcrumbs'
2
+ export * from './types'
3
+ export * from './BreadcrumbsJsonLd'
4
+ export * from './jsonLdBreadcrumb'
@@ -0,0 +1,19 @@
1
+ import { NextRouter } from 'next/router'
2
+ import type { BreadcrumbList } from 'schema-dts'
3
+ import { canonicalize } from '../PageMeta/PageMeta'
4
+ import type { BreadcrumbsType } from './types'
5
+
6
+ export function jsonLdBreadcrumb(
7
+ breadcrumbs: BreadcrumbsType['breadcrumbs'],
8
+ router: NextRouter,
9
+ ): BreadcrumbList {
10
+ return {
11
+ '@type': 'BreadcrumbList',
12
+ itemListElement: breadcrumbs.map(({ name, href }, index) => ({
13
+ '@type': 'ListItem',
14
+ position: index + 1,
15
+ name,
16
+ item: canonicalize(router, href),
17
+ })),
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ import { Theme } from '@emotion/react'
2
+ import { SxProps } from '@mui/material'
3
+
4
+ export type BreadcrumbItem = {
5
+ name: string
6
+ href: string
7
+ }
8
+
9
+ export type BreadcrumbsType = {
10
+ breadcrumbs: ({ sx?: SxProps<Theme> } & BreadcrumbItem)[]
11
+ }