@graphcommerce/next-ui 8.1.0-canary.44 → 8.1.0-canary.45

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.
@@ -157,15 +157,15 @@ export function ActionCard(props: ActionCardProps) {
157
157
 
158
158
  '&.variantDefault': {
159
159
  position: 'relative',
160
- '&::after': {
161
- content: '""',
162
- position: 'absolute',
163
- width: '100%',
164
- left: 0,
165
- bottom: '-1px',
166
- borderBottom: `1px solid ${theme.palette.divider}`,
167
- display: 'block',
168
- },
160
+ // '&::after': {
161
+ // content: '""',
162
+ // position: 'absolute',
163
+ // width: '100%',
164
+ // left: 0,
165
+ // bottom: '-1px',
166
+ // borderBottom: `1px solid ${theme.palette.divider}`,
167
+ // display: 'block',
168
+ // },
169
169
  '&.selected': {
170
170
  backgroundColor:
171
171
  theme.palette.mode === 'light'
@@ -179,34 +179,34 @@ export function ActionCard(props: ActionCardProps) {
179
179
  )}`,
180
180
  },
181
181
 
182
- '&.sizeSmall': {
183
- mt: { xs: '2px', sm: '3px', md: '5px' },
184
- mb: { xs: '3px', sm: '4px', md: '6px' },
185
- '&::after': {
186
- mb: { xs: '-2px', sm: '-3px', md: '-5px' },
187
- },
188
- },
189
- '&.sizeMedium': {
190
- mt: { xs: '4px', sm: '5px', md: '6px' },
191
- mb: { xs: '5px', sm: '6px', md: '7px' },
192
- '&::after': {
193
- mb: { xs: '-4px', sm: '-5px', md: '-6px' },
194
- },
195
- },
196
- '&.sizeLarge': {
197
- mt: { xs: '5px', sm: '7px', md: '8px' },
198
- mb: { xs: '6px', sm: '8px', md: '9px' },
199
- '&::after': {
200
- mb: { xs: '-5px', sm: '-7px', md: '-8px' },
201
- },
202
- },
203
- '&.sizeResponsive': {
204
- mt: responsiveVal(2, 8),
205
- mb: responsiveVal(3, 9),
206
- '&::after': {
207
- mb: responsiveVal(-2, -8),
208
- },
209
- },
182
+ // '&.sizeSmall': {
183
+ // mt: { xs: '2px', sm: '3px', md: '5px' },
184
+ // mb: { xs: '3px', sm: '4px', md: '6px' },
185
+ // '&::after': {
186
+ // mb: { xs: '-2px', sm: '-3px', md: '-5px' },
187
+ // },
188
+ // },
189
+ // '&.sizeMedium': {
190
+ // mt: { xs: '4px', sm: '5px', md: '6px' },
191
+ // mb: { xs: '5px', sm: '6px', md: '7px' },
192
+ // '&::after': {
193
+ // mb: { xs: '-4px', sm: '-5px', md: '-6px' },
194
+ // },
195
+ // },
196
+ // '&.sizeLarge': {
197
+ // mt: { xs: '5px', sm: '7px', md: '8px' },
198
+ // mb: { xs: '6px', sm: '8px', md: '9px' },
199
+ // '&::after': {
200
+ // mb: { xs: '-5px', sm: '-7px', md: '-8px' },
201
+ // },
202
+ // },
203
+ // '&.sizeResponsive': {
204
+ // mt: responsiveVal(2, 8),
205
+ // mb: responsiveVal(3, 9),
206
+ // '&::after': {
207
+ // mb: responsiveVal(-2, -8),
208
+ // },
209
+ // },
210
210
  },
211
211
 
212
212
  '&.variantOutlined': {
@@ -320,10 +320,10 @@ export function ActionCard(props: ActionCardProps) {
320
320
  <Box
321
321
  className={classes.title}
322
322
  sx={{
323
- '&.sizeSmall': { typography: 'body2' },
323
+ '&.sizeSmall': { typography: 'body1' },
324
324
  '&.sizeMedium': { typography: 'body1' },
325
325
  '&.sizeLarge': { typography: 'h6' },
326
- '&.sizeResponsive': { typography: { xs: 'body2', md: 'body1', lg: 'h6' } },
326
+ '&.sizeResponsive': { typography: { xs: 'body1', md: 'body1', lg: 'body1' } },
327
327
  }}
328
328
  >
329
329
  {title}
@@ -29,6 +29,7 @@ export function ActionCardAccordion(props: ActionCardAccordionProps) {
29
29
  '&.Mui-expanded': { my: 0 },
30
30
  '::before': { display: 'none' },
31
31
  border: 'none',
32
+ borderBottom: `1px solid ${theme.palette.divider}`,
32
33
  '&:not(.Mui-expanded)': { borderBottom: `1px solid ${theme.palette.divider}` },
33
34
  }),
34
35
  ...(Array.isArray(sx) ? sx : [sx]),
@@ -39,7 +40,7 @@ export function ActionCardAccordion(props: ActionCardAccordionProps) {
39
40
  expandIcon={<IconSvg src={iconChevronDown} />}
40
41
  sx={{
41
42
  px: 0,
42
- typography: 'h6',
43
+ typography: 'subtitle1',
43
44
  minHeight: 54,
44
45
  '& .MuiAccordionSummary-content': {
45
46
  display: 'flex',
@@ -1,6 +1,6 @@
1
1
  import { Avatar, Box, Chip, SxProps, Theme } from '@mui/material'
2
2
  import { responsiveVal } from '../../Styles/responsiveVal'
3
- import { useDateTimeFormat } from '../../hooks'
3
+ import { DateFormat } from '../../Intl/DateTimeFormat'
4
4
 
5
5
  export type BlogAuthorProps = {
6
6
  author: string
@@ -11,8 +11,6 @@ export type BlogAuthorProps = {
11
11
  export function BlogAuthor(props: BlogAuthorProps) {
12
12
  const { author, date, sx = [] } = props
13
13
 
14
- const formatter = useDateTimeFormat({ month: 'long', day: 'numeric' })
15
-
16
14
  return (
17
15
  <Box
18
16
  maxWidth='md'
@@ -48,7 +46,7 @@ export function BlogAuthor(props: BlogAuthorProps) {
48
46
  {author}
49
47
  </Box>
50
48
  <Box sx={(theme) => ({ lineHeight: 1.4, color: theme.palette.text.disabled })}>
51
- {formatter.format(new Date(date))}
49
+ <DateFormat dateStyle='long'>{date}</DateFormat>
52
50
  </Box>
53
51
  </section>
54
52
  }
@@ -2,7 +2,7 @@ import { Box, Link, SxProps, Theme, Typography } from '@mui/material'
2
2
  import React from 'react'
3
3
  import { extendableComponent } from '../../Styles'
4
4
  import { breakpointVal } from '../../Styles/breakpointVal'
5
- import { useDateTimeFormat } from '../../hooks'
5
+ import { DateFormat } from '../../Intl'
6
6
 
7
7
  export type BlogListItemProps = {
8
8
  asset: React.ReactNode
@@ -19,8 +19,6 @@ const { classes } = extendableComponent(name, parts)
19
19
  export function BlogListItem(props: BlogListItemProps) {
20
20
  const { asset, url, date, title, sx = [] } = props
21
21
 
22
- const formatter = useDateTimeFormat({ dateStyle: 'long' })
23
-
24
22
  return (
25
23
  <Box
26
24
  className={classes.item}
@@ -67,7 +65,7 @@ export function BlogListItem(props: BlogListItemProps) {
67
65
  color: theme.palette.text.secondary,
68
66
  })}
69
67
  >
70
- {formatter.format(new Date(date))}
68
+ <DateFormat dateStyle='long'>{date}</DateFormat>
71
69
  </Box>
72
70
  )}
73
71
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Change Log
2
2
 
3
+ ## 8.1.0-canary.45
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2216](https://github.com/graphcommerce-org/graphcommerce/pull/2216) [`1895176`](https://github.com/graphcommerce-org/graphcommerce/commit/1895176b5776adcf588999ceab7edd6f53cff294) - USPS would be loaded after other pages, causing uneceesasry slowdown
8
+ ([@paales](https://github.com/paales))
9
+
10
+ - [#2216](https://github.com/graphcommerce-org/graphcommerce/pull/2216) [`74eb239`](https://github.com/graphcommerce-org/graphcommerce/commit/74eb2393a64988649178d59f53b33d41fecdd769) - Added Intl components and replaced usage sites
11
+ ([@paales](https://github.com/paales))
12
+
13
+ - [#2216](https://github.com/graphcommerce-org/graphcommerce/pull/2216) [`012b865`](https://github.com/graphcommerce-org/graphcommerce/commit/012b86560d93e35dd7ca4a9469ed65e6fb83ead0) - Move arguments of responsiveVal around so the minBreakpoint can be given as third argument
14
+ ([@paales](https://github.com/paales))
15
+
16
+ - [#2216](https://github.com/graphcommerce-org/graphcommerce/pull/2216) [`223f16a`](https://github.com/graphcommerce-org/graphcommerce/commit/223f16a0e9a7718e52e81a4e2902471a8b78d35c) - LazyHydrate component now accepts a height prop that allows deferring of rendering of the child component when initially rendered on the client. This improves performance.
17
+ ([@paales](https://github.com/paales))
18
+
3
19
  ## 8.1.0-canary.44
4
20
 
5
21
  ## 8.1.0-canary.43
package/Footer/Footer.tsx CHANGED
@@ -31,7 +31,7 @@ export function Footer(props: FooterProps) {
31
31
  } = props
32
32
 
33
33
  return (
34
- <LazyHydrate>
34
+ <LazyHydrate height={300}>
35
35
  <Container
36
36
  sx={[
37
37
  (theme) => ({
@@ -156,7 +156,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
156
156
  gridTemplate: '"left" "right"',
157
157
  [theme.breakpoints.up('md')]: {
158
158
  '&:not(.variantMdOneColumn)': {
159
- gridTemplate: `"left right" / 1fr calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
159
+ gridTemplate: `"left right" / 1fr calc(${responsiveVal(300, 500, undefined, theme.breakpoints.values.lg)} + ${
160
160
  theme.page.horizontal
161
161
  } * 2)`,
162
162
  },
@@ -201,13 +201,11 @@ export function SidebarGallery(props: SidebarGalleryProps) {
201
201
  },
202
202
  [theme.breakpoints.up('md')]: {
203
203
  '&:not(.variantMdOneColumn)': {
204
- height: `calc(${dvh(100)} - ${theme.appShell.headerHeightMd} - ${
205
- theme.spacings.lg
206
- })`,
204
+ height: `max(${dvh(90)}, 40vw)`,
207
205
  '&.sticky': {
208
206
  position: 'sticky',
207
+ top: theme.appShell.headerHeightMd,
209
208
  },
210
- top: theme.appShell.headerHeightMd,
211
209
  },
212
210
  },
213
211
  },
@@ -374,10 +372,10 @@ export function SidebarGallery(props: SidebarGalleryProps) {
374
372
  },
375
373
  zoomed && {
376
374
  [theme.breakpoints.up('md')]: {
377
- marginLeft: `calc((${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
375
+ marginLeft: `calc((${responsiveVal(300, 500, undefined, theme.breakpoints.values.lg)} + ${
378
376
  theme.page.horizontal
379
377
  } * 2) * -1)`,
380
- left: `calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
378
+ left: `calc(${responsiveVal(300, 500, undefined, theme.breakpoints.values.lg)} + ${
381
379
  theme.page.horizontal
382
380
  } * 2)`,
383
381
  },
@@ -0,0 +1,10 @@
1
+ import { DateTimeFormat, DateTimeFormatPropsType } from './DateTimeFormat'
2
+
3
+ type DateFormatOptions = Omit<
4
+ DateTimeFormatPropsType,
5
+ 'timeStyle' | 'hour' | 'minute' | 'second' | 'timeZoneName'
6
+ >
7
+
8
+ export function DateFormat(props: DateFormatOptions) {
9
+ return <DateTimeFormat timeStyle={undefined} {...props} />
10
+ }
@@ -0,0 +1,25 @@
1
+ import { useMemo } from 'react'
2
+ import { useLocale } from '../../hooks/useLocale'
3
+ import { useMemoObject } from '../../hooks/useMemoObject'
4
+
5
+ export function useDateTimeFormatter(props: Intl.DateTimeFormatOptions) {
6
+ const locale = useLocale()
7
+ const memoOptions = useMemoObject(props)
8
+ return useMemo(() => new Intl.DateTimeFormat(locale, memoOptions), [locale, memoOptions])
9
+ }
10
+
11
+ type DateValue = Date | string | number | null | undefined
12
+ export type DateTimeFormatPropsType = { children: DateValue } & Intl.DateTimeFormatOptions
13
+
14
+ export function DateTimeFormat(props: DateTimeFormatPropsType) {
15
+ const { children } = props
16
+ const formatter = useDateTimeFormatter({ dateStyle: 'medium', timeStyle: 'short', ...props })
17
+
18
+ return (
19
+ <span suppressHydrationWarning>
20
+ {children
21
+ ? formatter.format(typeof children === 'string' ? new Date(children) : children)
22
+ : null}
23
+ </span>
24
+ )
25
+ }
@@ -0,0 +1,10 @@
1
+ import { DateTimeFormat, DateTimeFormatPropsType } from './DateTimeFormat'
2
+
3
+ type TimeFormatProps = Omit<
4
+ DateTimeFormatPropsType,
5
+ 'dateStyle' | 'month' | 'day' | 'era' | 'month' | 'weekday' | 'year'
6
+ >
7
+
8
+ export function TimeFormat(props: TimeFormatProps) {
9
+ return <DateTimeFormat dateStyle={undefined} {...props} />
10
+ }
@@ -0,0 +1,3 @@
1
+ export * from './DateTimeFormat'
2
+ export * from './DateFormat'
3
+ export * from './TimeFormat'
@@ -0,0 +1,22 @@
1
+ import { useMemo } from 'react'
2
+ import { useLocale } from '../../hooks/useLocale'
3
+ import { useMemoObject } from '../../hooks/useMemoObject'
4
+
5
+ export type UseDisplayNamesProps = Intl.DisplayNamesOptions
6
+
7
+ export function useDisplayNames(props: UseDisplayNamesProps) {
8
+ const locale = useLocale()
9
+ const memoOptions = useMemoObject(props)
10
+ return useMemo(() => new Intl.DisplayNames(locale, memoOptions), [locale, memoOptions])
11
+ }
12
+
13
+ type DisplayNamesProps = UseDisplayNamesProps & {
14
+ code: string
15
+ }
16
+
17
+ export function DisplayNames(props: DisplayNamesProps) {
18
+ const { code, ...options } = props
19
+ const formatter = useDisplayNames(options)
20
+
21
+ return <span suppressHydrationWarning>{formatter.of(code)}</span>
22
+ }
@@ -0,0 +1 @@
1
+ export * from './DisplayNames'
@@ -0,0 +1,32 @@
1
+ import React, { useMemo } from 'react'
2
+ import { useLocale } from '../hooks'
3
+
4
+ type ListFormatProps = {
5
+ children: React.ReactNode[]
6
+ listStyle?: Intl.ListFormatOptions['style']
7
+ } & Omit<Intl.ListFormatOptions, 'style'>
8
+
9
+ export function ListFormat(props: ListFormatProps) {
10
+ const { children, localeMatcher, listStyle: style, type } = props
11
+
12
+ const locale = useLocale()
13
+
14
+ const formatter = useMemo(
15
+ () => new Intl.ListFormat(locale, { localeMatcher, style, type }),
16
+ [locale, localeMatcher, style, type],
17
+ )
18
+
19
+ const childArray = React.Children.toArray(children)
20
+ const fauxChildren = childArray.map((child, index) => `${index}`) ?? []
21
+ const parts = formatter.formatToParts(fauxChildren)
22
+
23
+ return (
24
+ <>
25
+ {parts.map((part) => (
26
+ <React.Fragment key={part.value}>
27
+ {part.type === 'literal' ? part.value : childArray[part.value]}
28
+ </React.Fragment>
29
+ ))}
30
+ </>
31
+ )
32
+ }
@@ -0,0 +1,191 @@
1
+ import { NumberFormat, NumberFormatPropsType } from './NumberFormat'
2
+
3
+ // https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes
4
+ type Currency =
5
+ | 'AED'
6
+ | 'AFN'
7
+ | 'ALL'
8
+ | 'AMD'
9
+ | 'ANG'
10
+ | 'AOA'
11
+ | 'ARS'
12
+ | 'AUD'
13
+ | 'AWG'
14
+ | 'AZN'
15
+ | 'BAM'
16
+ | 'BBD'
17
+ | 'BDT'
18
+ | 'BGN'
19
+ | 'BHD'
20
+ | 'BIF'
21
+ | 'BMD'
22
+ | 'BND'
23
+ | 'BOB'
24
+ | 'BOV'
25
+ | 'BRL'
26
+ | 'BSD'
27
+ | 'BTN'
28
+ | 'BWP'
29
+ | 'BYN'
30
+ | 'BZD'
31
+ | 'CAD'
32
+ | 'CDF'
33
+ | 'CHE'
34
+ | 'CHF'
35
+ | 'CHW'
36
+ | 'CLF'
37
+ | 'CLP'
38
+ | 'CNY'
39
+ | 'COP'
40
+ | 'COU'
41
+ | 'CRC'
42
+ | 'CUP'
43
+ | 'CVE'
44
+ | 'CZK'
45
+ | 'DJF'
46
+ | 'DKK'
47
+ | 'DOP'
48
+ | 'DZD'
49
+ | 'EGP'
50
+ | 'ERN'
51
+ | 'ETB'
52
+ | 'EUR'
53
+ | 'FJD'
54
+ | 'FKP'
55
+ | 'GBP'
56
+ | 'GEL'
57
+ | 'GHS'
58
+ | 'GIP'
59
+ | 'GMD'
60
+ | 'GNF'
61
+ | 'GTQ'
62
+ | 'GYD'
63
+ | 'HKD'
64
+ | 'HNL'
65
+ | 'HTG'
66
+ | 'HUF'
67
+ | 'IDR'
68
+ | 'ILS'
69
+ | 'INR'
70
+ | 'IQD'
71
+ | 'IRR'
72
+ | 'ISK'
73
+ | 'JMD'
74
+ | 'JOD'
75
+ | 'JPY'
76
+ | 'KES'
77
+ | 'KGS'
78
+ | 'KHR'
79
+ | 'KMF'
80
+ | 'KPW'
81
+ | 'KRW'
82
+ | 'KWD'
83
+ | 'KYD'
84
+ | 'KZT'
85
+ | 'LAK'
86
+ | 'LBP'
87
+ | 'LKR'
88
+ | 'LRD'
89
+ | 'LSL'
90
+ | 'LYD'
91
+ | 'MAD'
92
+ | 'MDL'
93
+ | 'MGA'
94
+ | 'MKD'
95
+ | 'MMK'
96
+ | 'MNT'
97
+ | 'MOP'
98
+ | 'MRU'
99
+ | 'MUR'
100
+ | 'MVR'
101
+ | 'MWK'
102
+ | 'MXN'
103
+ | 'MXV'
104
+ | 'MYR'
105
+ | 'MZN'
106
+ | 'NAD'
107
+ | 'NGN'
108
+ | 'NIO'
109
+ | 'NOK'
110
+ | 'NPR'
111
+ | 'NZD'
112
+ | 'OMR'
113
+ | 'PAB'
114
+ | 'PEN'
115
+ | 'PGK'
116
+ | 'PHP'
117
+ | 'PKR'
118
+ | 'PLN'
119
+ | 'PYG'
120
+ | 'QAR'
121
+ | 'RON'
122
+ | 'RSD'
123
+ | 'RUB'
124
+ | 'RWF'
125
+ | 'SAR'
126
+ | 'SBD'
127
+ | 'SCR'
128
+ | 'SDG'
129
+ | 'SEK'
130
+ | 'SGD'
131
+ | 'SHP'
132
+ | 'SLE'
133
+ | 'SLL'
134
+ | 'SOS'
135
+ | 'SRD'
136
+ | 'SSP'
137
+ | 'STN'
138
+ | 'SVC'
139
+ | 'SYP'
140
+ | 'SZL'
141
+ | 'THB'
142
+ | 'TJS'
143
+ | 'TMT'
144
+ | 'TND'
145
+ | 'TOP'
146
+ | 'TRY'
147
+ | 'TTD'
148
+ | 'TWD'
149
+ | 'TZS'
150
+ | 'UAH'
151
+ | 'UGX'
152
+ | 'USD'
153
+ | 'USN'
154
+ | 'UYI'
155
+ | 'UYU'
156
+ | 'UYW'
157
+ | 'UZS'
158
+ | 'VED'
159
+ | 'VES'
160
+ | 'VND'
161
+ | 'VUV'
162
+ | 'WST'
163
+ | 'XAF'
164
+ | 'XAG'
165
+ | 'XAU'
166
+ | 'XBA'
167
+ | 'XBB'
168
+ | 'XBC'
169
+ | 'XBD'
170
+ | 'XCD'
171
+ | 'XDR'
172
+ | 'XOF'
173
+ | 'XPD'
174
+ | 'XPF'
175
+ | 'XPT'
176
+ | 'XSU'
177
+ | 'XTS'
178
+ | 'XUA'
179
+ | 'XXX'
180
+ | 'YER'
181
+ | 'ZAR'
182
+ | 'ZMW'
183
+ | 'ZWL'
184
+
185
+ export type CurrencyFormatProps = Omit<NumberFormatPropsType, 'numberStyle'> & {
186
+ currency: Currency | (string & Record<never, never>)
187
+ }
188
+
189
+ export function CurrencyFormat(props: CurrencyFormatProps) {
190
+ return <NumberFormat {...props} numberStyle='currency' />
191
+ }
@@ -0,0 +1,24 @@
1
+ import { Box, SxProps, Theme } from '@mui/material'
2
+ import { useMemo } from 'react'
3
+ import { useLocale } from '../../hooks/useLocale'
4
+ import { useMemoObject } from '../../hooks/useMemoObject'
5
+
6
+ export type NumberFormatPropsType = {
7
+ children?: number | bigint | null | undefined
8
+ numberStyle: Intl.NumberFormatOptions['style']
9
+ sx?: SxProps<Theme>
10
+ } & Omit<Intl.NumberFormatOptions, 'style'>
11
+
12
+ export function NumberFormat(props: NumberFormatPropsType) {
13
+ const { children, numberStyle, ...options } = props
14
+
15
+ const memoOptions = useMemoObject({ ...options, style: numberStyle })
16
+ const locale = useLocale()
17
+ const formatter = useMemo(() => new Intl.NumberFormat(locale, memoOptions), [locale, memoOptions])
18
+
19
+ return (
20
+ <Box component='span' suppressHydrationWarning>
21
+ {children ? formatter.format(children) : null}
22
+ </Box>
23
+ )
24
+ }
@@ -0,0 +1,5 @@
1
+ import { NumberFormat, NumberFormatPropsType } from './NumberFormat'
2
+
3
+ export function PercentFormat(props: Omit<NumberFormatPropsType, 'numberStyle'>) {
4
+ return <NumberFormat {...props} numberStyle='percent' />
5
+ }
@@ -0,0 +1,59 @@
1
+ import { NumberFormatPropsType, NumberFormat } from './NumberFormat'
2
+
3
+ /**
4
+ * https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers
5
+ */
6
+ type Unit =
7
+ | 'acre'
8
+ | 'bit'
9
+ | 'byte'
10
+ | 'celsius'
11
+ | 'centimeter'
12
+ | 'day'
13
+ | 'degree'
14
+ | 'fahrenheit'
15
+ | 'fluid-ounce'
16
+ | 'foot'
17
+ | 'gallon'
18
+ | 'gigabit'
19
+ | 'gigabyte'
20
+ | 'gram'
21
+ | 'hectare'
22
+ | 'hour'
23
+ | 'inch'
24
+ | 'kilobit'
25
+ | 'kilobyte'
26
+ | 'kilogram'
27
+ | 'kilometer'
28
+ | 'liter'
29
+ | 'megabit'
30
+ | 'megabyte'
31
+ | 'meter'
32
+ | 'microsecond'
33
+ | 'mile'
34
+ | 'mile-scandinavian'
35
+ | 'milliliter'
36
+ | 'millimeter'
37
+ | 'millisecond'
38
+ | 'minute'
39
+ | 'month'
40
+ | 'nanosecond'
41
+ | 'ounce'
42
+ | 'percent'
43
+ | 'petabyte'
44
+ | 'pound'
45
+ | 'second'
46
+ | 'stone'
47
+ | 'terabit'
48
+ | 'terabyte'
49
+ | 'week'
50
+ | 'yard'
51
+ | 'year'
52
+
53
+ export type UnitFormatProps = Omit<NumberFormatPropsType, 'numberStyle'> & {
54
+ unit: Unit | (string & Record<never, never>)
55
+ }
56
+
57
+ export function UnitFormat(props: UnitFormatProps) {
58
+ return <NumberFormat {...props} numberStyle='unit' />
59
+ }
@@ -0,0 +1,4 @@
1
+ export * from './CurrencyFormat'
2
+ export * from './NumberFormat'
3
+ export * from './UnitFormat'
4
+ export * from './PercentFormat'
package/Intl/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator
2
+ // export * from './Collator' // Intl.Collator
3
+
4
+ export * from './DisplayNames' // Intl.DisplayNames
5
+ export * from './DateTimeFormat' // Intl.DateTimeFormat
6
+ export * from './ListFormat' // Intl.ListFormat
7
+ // export * from './Locale' // Intl.Locale
8
+ export * from './NumberFormat' // Intl.NumberFormat
9
+ // export * from './PluralRules' // Intl.PluralRules
10
+ // export * from './RelativeTimeFormat' // Intl.RelativeTimeFormat
11
+ // export * from './Segmenter' // Intl.Segmenter
@@ -28,6 +28,9 @@ export type LayoutHeaderProps = FloatingProps &
28
28
  sx?: SxProps<Theme>
29
29
 
30
30
  hideBackButton?: boolean
31
+
32
+ hideSm?: boolean
33
+ hideMd?: boolean
31
34
  }
32
35
 
33
36
  type ComponentStyleProps = {
@@ -36,6 +39,8 @@ type ComponentStyleProps = {
36
39
  children: boolean
37
40
  floatingSm: boolean
38
41
  floatingMd: boolean
42
+ hideSm: boolean
43
+ hideMd: boolean
39
44
  size: 'small' | 'responsive'
40
45
  }
41
46
 
@@ -44,7 +49,7 @@ const { selectors, withState } = extendableComponent<ComponentStyleProps, 'Layou
44
49
  ['root'] as const,
45
50
  )
46
51
 
47
- export function LayoutHeader(props: LayoutHeaderProps) {
52
+ export const LayoutHeader = React.memo<LayoutHeaderProps>((props) => {
48
53
  const {
49
54
  children,
50
55
  divider,
@@ -56,6 +61,8 @@ export function LayoutHeader(props: LayoutHeaderProps) {
56
61
  size = 'responsive',
57
62
  sx = [],
58
63
  bgColor,
64
+ hideSm = false,
65
+ hideMd = false,
59
66
  } = props
60
67
  const showBack = useShowBack() && !hideBackButton
61
68
  const showClose = useShowClose()
@@ -88,6 +95,8 @@ export function LayoutHeader(props: LayoutHeaderProps) {
88
95
  children: !!children,
89
96
  divider: !!divider,
90
97
  size,
98
+ hideSm,
99
+ hideMd,
91
100
  })
92
101
 
93
102
  return (
@@ -115,6 +124,9 @@ export function LayoutHeader(props: LayoutHeaderProps) {
115
124
  '&.divider': {
116
125
  marginBottom: 0,
117
126
  },
127
+ '&.hideSm .LayoutHeaderContent-left': {
128
+ display: 'none',
129
+ },
118
130
  },
119
131
 
120
132
  [theme.breakpoints.up('md')]: {
@@ -140,7 +152,7 @@ export function LayoutHeader(props: LayoutHeaderProps) {
140
152
  '&.divider': {
141
153
  marginBottom: 0,
142
154
  },
143
- '& .LayoutHeaderContent-left': import.meta.graphCommerce.breadcrumbs && {
155
+ '&.hideMd .LayoutHeaderContent-left': {
144
156
  display: 'none',
145
157
  },
146
158
  },
@@ -162,5 +174,4 @@ export function LayoutHeader(props: LayoutHeaderProps) {
162
174
  </LayoutHeaderContent>
163
175
  </Box>
164
176
  )
165
- }
166
- LayoutHeader.selectors = selectors
177
+ })
@@ -1,4 +1,9 @@
1
- import { useUp, usePrevUp, usePageContext } from '@graphcommerce/framer-next-pages'
1
+ import {
2
+ useUp,
3
+ usePrevUp,
4
+ usePageContext,
5
+ usePrevPageRouter,
6
+ } from '@graphcommerce/framer-next-pages'
2
7
  import { i18n } from '@lingui/core'
3
8
  import { Box, SxProps, Theme } from '@mui/material'
4
9
  import { useRouter } from 'next/router'
@@ -41,6 +46,7 @@ export function LayoutHeaderBack(props: BackProps) {
41
46
  const up = useUp()
42
47
  const prevUp = usePrevUp()
43
48
  const { backSteps } = usePageContext()
49
+ const prevPageRouter = usePrevPageRouter()
44
50
 
45
51
  const backIcon = <IconSvg src={iconChevronLeft} size='medium' />
46
52
  const canClickBack = backSteps > 0 && path !== prevUp?.href
@@ -49,6 +55,8 @@ export function LayoutHeaderBack(props: BackProps) {
49
55
  if (up?.href === path && up?.title) label = up.title
50
56
  if (prevUp?.href === path && prevUp?.title) label = prevUp.title
51
57
 
58
+ if (up && prevPageRouter?.asPath === up?.href) label = up.title
59
+
52
60
  if (canClickBack) {
53
61
  return (
54
62
  <LinkOrButton
@@ -1,5 +1,12 @@
1
1
  import { Box, BoxProps } from '@mui/material'
2
- import React, { useState, useRef, startTransition, useLayoutEffect, useEffect } from 'react'
2
+ import React, {
3
+ useState,
4
+ useRef,
5
+ startTransition,
6
+ useLayoutEffect,
7
+ useEffect,
8
+ CSSProperties,
9
+ } from 'react'
3
10
 
4
11
  // Make sure the server doesn't choke on the useLayoutEffect
5
12
  export const useLayoutEffect2 = typeof window !== 'undefined' ? useLayoutEffect : useEffect
@@ -18,6 +25,14 @@ export type LazyHydrateProps = BoxProps<'div'> & {
18
25
  * - Hydrate the component on some state `<LazyHydrate hydrated={someState}>` where someState initially is false and later becomes true.
19
26
  */
20
27
  hydrated?: boolean
28
+
29
+ /**
30
+ * By default LazyHydrate does not defer the rendering of components when they are rendered client
31
+ * side, because using an IntersectionObserver on an element with no height, will cause all siblings to render at once.
32
+ *
33
+ * By proving a height, we can use the IntersectionObserver on the client as well.
34
+ */
35
+ height?: CSSProperties['height']
21
36
  }
22
37
 
23
38
  /**
@@ -26,7 +41,7 @@ export type LazyHydrateProps = BoxProps<'div'> & {
26
41
  * This can be a way to improve the TBT of a page.
27
42
  */
28
43
  export function LazyHydrate(props: LazyHydrateProps) {
29
- const { hydrated, children, ...elementProps } = props
44
+ const { hydrated, children, height, ...elementProps } = props
30
45
  const rootRef = useRef<HTMLDivElement>(null)
31
46
 
32
47
  const [isHydrated, setIsHydrated] = useState(hydrated || false)
@@ -37,7 +52,7 @@ export function LazyHydrate(props: LazyHydrateProps) {
37
52
  if (isHydrated || !rootRef.current) return undefined
38
53
 
39
54
  // If the element wasn't rendered on the server, we hydrate it immediately
40
- if (!rootRef.current?.hasAttribute('data-lazy-hydrate')) {
55
+ if (!height && !rootRef.current?.hasAttribute('data-lazy-hydrate')) {
41
56
  setIsHydrated(true)
42
57
  return undefined
43
58
  }
@@ -77,6 +92,7 @@ export function LazyHydrate(props: LazyHydrateProps) {
77
92
  dangerouslySetInnerHTML={{ __html: '' }}
78
93
  suppressHydrationWarning
79
94
  {...elementProps}
95
+ style={{ ...elementProps.style, height }}
80
96
  />
81
97
  )
82
98
  }
@@ -1,6 +1,15 @@
1
- import { LinearProgress, Fade } from '@mui/material'
1
+ import { LinearProgress, LinearProgressProps } from '@mui/material'
2
+ import { m, motionValue, useTransform } from 'framer-motion'
2
3
  import { useRouter } from 'next/router'
3
- import { useEffect, useState } from 'react'
4
+ import { forwardRef, useEffect, useState } from 'react'
5
+
6
+ export const showPageLoadIndicator = motionValue(false)
7
+
8
+ const MLinearProgress = m(
9
+ forwardRef((props: Omit<LinearProgressProps, 'style'>, ref: LinearProgressProps['ref']) => (
10
+ <LinearProgress ref={ref} {...props} />
11
+ )),
12
+ )
4
13
 
5
14
  /**
6
15
  * Creates a [LinearProgress](https://mui.com/components/progress/#linear) animation when the route
@@ -24,18 +33,20 @@ export function PageLoadIndicator() {
24
33
  }
25
34
  }, [events])
26
35
 
36
+ const opacity = useTransform(() => (showPageLoadIndicator.get() || loading ? 1 : 0))
37
+
27
38
  return (
28
- <Fade in={loading}>
29
- <LinearProgress
30
- sx={{
31
- position: 'fixed',
32
- width: '100%',
33
- top: 0,
34
- height: 3,
35
- marginBottom: '-3px',
36
- zIndex: 'tooltip',
37
- }}
38
- />
39
- </Fade>
39
+ <MLinearProgress
40
+ style={{ opacity }}
41
+ sx={{
42
+ position: 'fixed',
43
+ width: '100%',
44
+ top: 0,
45
+ height: 3,
46
+ marginBottom: '-3px',
47
+ zIndex: 'tooltip',
48
+ transition: 'opacity 0.3s',
49
+ }}
50
+ />
40
51
  )
41
52
  }
@@ -6,15 +6,14 @@
6
6
  export function responsiveVal(
7
7
  min: number,
8
8
  max: number,
9
- maxBreakpoint = 1280,
10
- ): `max(${number}px, min(${string}, ${number}px))` {
9
+ minBreakpoint = 320,
10
+ maxBreakpoint = 2560,
11
+ ): `clamp(${number}px, ${string}, ${number}px)` {
11
12
  const round = (x: number, n: number): number => Math.round(x * 10 ** n) / 10 ** n
12
13
 
13
- const minBreakpoint = 320
14
14
  const growth = (max - min) / (maxBreakpoint - minBreakpoint)
15
15
  const base = round(min - growth * minBreakpoint, 2)
16
16
  const vsize = round(growth * 100, 2)
17
17
 
18
- const calc = `(${base}px + ${vsize}vw)`
19
- return `max(${min}px, min(${calc}, ${max}px))`
18
+ return `clamp(${min}px, (${base}px + ${vsize}vw), ${max}px)`
20
19
  }
package/hooks/index.ts CHANGED
@@ -5,5 +5,5 @@ export * from './useNumberFormat'
5
5
  export * from './useMemoObject'
6
6
  export * from './useStorefrontConfig'
7
7
  export * from './useUrlQuery'
8
- export * from './useSsr'
8
+ export * from './useIsSsr'
9
9
  export * from './useLocale'
@@ -1,11 +1,11 @@
1
1
  import { useMemo } from 'react'
2
2
  import { useLocale } from './useLocale'
3
3
 
4
- export type DateTimeFormatProps = Intl.DateTimeFormatOptions
5
-
6
- export function useDateTimeFormat(props?: DateTimeFormatProps) {
4
+ /**
5
+ * @deprecated use <DateFormat/>, <TimeFormat/> or <DateTimeFormat/> instead
6
+ */
7
+ export function useDateTimeFormat(props?: Intl.DateTimeFormatOptions) {
7
8
  const locale = useLocale()
8
-
9
9
  const formatter = useMemo(() => new Intl.DateTimeFormat(locale, props), [locale, props])
10
10
  return formatter
11
11
  }
@@ -2,6 +2,9 @@ import { useSyncExternalStore } from 'react'
2
2
 
3
3
  const emptySubscribe = () => () => {}
4
4
 
5
+ /**
6
+ * This method will return true on the server and during hydration and false after that.
7
+ */
5
8
  export function useIsSSR() {
6
9
  return useSyncExternalStore(
7
10
  emptySubscribe,
@@ -1,7 +1,7 @@
1
1
  import { normalizeLocale } from '@graphcommerce/lingui-next'
2
2
  import { useStorefrontConfig } from './useStorefrontConfig'
3
3
 
4
- export function useLocale() {
4
+ export function useLocale(): Intl.BCP47LanguageTag {
5
5
  const { locale } = useStorefrontConfig()
6
6
  return normalizeLocale(locale)
7
7
  }
@@ -1,9 +1,10 @@
1
1
  import { useMemo } from 'react'
2
2
  import { useLocale } from './useLocale'
3
3
 
4
- export type NumberFormatProps = Intl.NumberFormatOptions
5
-
6
- export function useNumberFormat(props?: NumberFormatProps) {
4
+ /**
5
+ * @deprecated use <NumberFormat />, <PercentFormat /> or <UnitFormat /> instead
6
+ */
7
+ export function useNumberFormat(props?: Intl.NumberFormatOptions) {
7
8
  const locale = useLocale()
8
9
  const formatter = useMemo(() => new Intl.NumberFormat(locale, props), [locale, props])
9
10
  return formatter
package/icons.ts CHANGED
@@ -8,6 +8,7 @@ export { default as iconInvoice } from './icons/box-alt.svg'
8
8
  export { default as iconBox } from './icons/box.svg'
9
9
  export { default as iconOrderBefore } from './icons/calendar.svg'
10
10
  export { default as iconCancelAlt } from './icons/cancel-alt.svg'
11
+ export { default as iconCancel } from './icons/cancel.svg'
11
12
  export { default as iconCartAdd } from './icons/cart-add.svg'
12
13
  export { default as iconCart } from './icons/cart.svg'
13
14
  export { default as iconChat } from './icons/chat-alt.svg'
package/index.ts CHANGED
@@ -7,8 +7,8 @@ export * from './Blog/BlogList/BlogList'
7
7
  export * from './Blog/BlogListItem/BlogListItem'
8
8
  export * from './Blog/BlogTags/BlogTags'
9
9
  export * from './Blog/BlogTitle/BlogTitle'
10
- export * from './Button'
11
10
  export * from './Breadcrumbs'
11
+ export * from './Button'
12
12
  export * from './ChipMenu/ChipMenu'
13
13
  export * from './ContainerWithHeader/ContainerWithHeader'
14
14
  export * from './Fab'
@@ -61,6 +61,7 @@ export * from './UspList/UspListItem'
61
61
  export * from './hooks'
62
62
  export * from './icons'
63
63
  export * from './utils/cookie'
64
- export * from './utils/sitemap'
65
- export * from './utils/robots'
66
64
  export * from './utils/cssFlags'
65
+ export * from './utils/robots'
66
+ export * from './utils/sitemap'
67
+ export * from './Intl'
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": "8.1.0-canary.44",
5
+ "version": "8.1.0-canary.45",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -26,14 +26,14 @@
26
26
  "typescript": "5.3.3"
27
27
  },
28
28
  "peerDependencies": {
29
- "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.44",
30
- "@graphcommerce/framer-next-pages": "^8.1.0-canary.44",
31
- "@graphcommerce/framer-scroller": "^8.1.0-canary.44",
32
- "@graphcommerce/framer-utils": "^8.1.0-canary.44",
33
- "@graphcommerce/image": "^8.1.0-canary.44",
34
- "@graphcommerce/lingui-next": "^8.1.0-canary.44",
35
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.44",
36
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.44",
29
+ "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.45",
30
+ "@graphcommerce/framer-next-pages": "^8.1.0-canary.45",
31
+ "@graphcommerce/framer-scroller": "^8.1.0-canary.45",
32
+ "@graphcommerce/framer-utils": "^8.1.0-canary.45",
33
+ "@graphcommerce/image": "^8.1.0-canary.45",
34
+ "@graphcommerce/lingui-next": "^8.1.0-canary.45",
35
+ "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.45",
36
+ "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.45",
37
37
  "@lingui/core": "^4.2.1",
38
38
  "@lingui/macro": "^4.2.1",
39
39
  "@lingui/react": "^4.2.1",
@@ -45,9 +45,6 @@ export function setCssFlag(flagName: string, val: true | string) {
45
45
  saveFlags(flags)
46
46
  }
47
47
 
48
- /**
49
- * @deprecated flags are not intendend to be used in JS, so this should only be used for debugging purposes.
50
- */
51
48
  export function getCssFlag(flagName: string) {
52
49
  return loadFlags()[flagName]
53
50
  }