@graphcommerce/next-ui 8.1.0-canary.9 → 9.0.0-canary.55

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 (65) hide show
  1. package/ActionCard/ActionCard.tsx +60 -36
  2. package/ActionCard/ActionCardAccordion.tsx +2 -1
  3. package/ActionCard/ActionCardList.tsx +3 -2
  4. package/ActionCard/ActionCardListForm.tsx +1 -1
  5. package/Blog/BlogAuthor/BlogAuthor.tsx +2 -4
  6. package/Blog/BlogListItem/BlogListItem.tsx +2 -4
  7. package/Breadcrumbs/Breadcrumbs.tsx +195 -0
  8. package/Breadcrumbs/BreadcrumbsJsonLd.tsx +13 -0
  9. package/Breadcrumbs/BreadcrumbsList.tsx +101 -0
  10. package/Breadcrumbs/BreadcrumbsPopper.tsx +48 -0
  11. package/Breadcrumbs/index.ts +4 -0
  12. package/Breadcrumbs/jsonLdBreadcrumb.tsx +19 -0
  13. package/Breadcrumbs/types.ts +11 -0
  14. package/CHANGELOG.md +162 -0
  15. package/Config.graphqls +5 -0
  16. package/Document/DocumentBodyEnd.tsx +7 -0
  17. package/Document/DocumentBodyStart.tsx +7 -0
  18. package/Document/DocumentHeadEnd.tsx +7 -0
  19. package/Document/DocumentHeadStart.tsx +7 -0
  20. package/Document/index.ts +4 -0
  21. package/Footer/Footer.tsx +1 -1
  22. package/FramerScroller/SidebarGallery.tsx +54 -27
  23. package/Intl/DateTimeFormat/DateFormat.tsx +10 -0
  24. package/Intl/DateTimeFormat/DateTimeFormat.tsx +25 -0
  25. package/Intl/DateTimeFormat/TimeFormat.tsx +10 -0
  26. package/Intl/DateTimeFormat/index.ts +3 -0
  27. package/Intl/DisplayNames/DisplayNames.tsx +22 -0
  28. package/Intl/DisplayNames/index.ts +1 -0
  29. package/Intl/ListFormat.tsx +32 -0
  30. package/Intl/NumberFormat/CurrencyFormat.tsx +191 -0
  31. package/Intl/NumberFormat/NumberFormat.tsx +24 -0
  32. package/Intl/NumberFormat/PercentFormat.tsx +5 -0
  33. package/Intl/NumberFormat/UnitFormat.tsx +59 -0
  34. package/Intl/NumberFormat/index.ts +4 -0
  35. package/Intl/index.ts +11 -0
  36. package/JsonLd/JsonLd.tsx +3 -2
  37. package/Layout/components/LayoutHeader.tsx +17 -3
  38. package/Layout/components/LayoutHeaderBack.tsx +9 -1
  39. package/Layout/components/LayoutTitle.tsx +0 -5
  40. package/LazyHydrate/LazyHydrate.tsx +19 -3
  41. package/PageLoadIndicator/PageLoadIndicator.tsx +25 -14
  42. package/PageMeta/PageMeta.tsx +1 -76
  43. package/PageMeta/canonicalize.ts +78 -0
  44. package/Pagination/PaginationExtended.tsx +103 -0
  45. package/Snackbar/MessageSnackbarImpl.tsx +60 -19
  46. package/Styles/responsiveVal.tsx +4 -5
  47. package/Styles/withEmotionCache.tsx +2 -2
  48. package/Theme/DarkLightModeThemeProvider.tsx +41 -19
  49. package/hooks/index.ts +1 -1
  50. package/hooks/useDateTimeFormat.ts +4 -4
  51. package/hooks/{useSsr.ts → useIsSsr.ts} +3 -0
  52. package/hooks/useLocale.ts +2 -2
  53. package/hooks/useNumberFormat.ts +4 -3
  54. package/hooks/useStorefrontConfig.ts +1 -9
  55. package/hooks/useUrlQuery.ts +6 -4
  56. package/icons.ts +55 -0
  57. package/index.ts +10 -3
  58. package/package.json +8 -9
  59. package/server.ts +8 -0
  60. package/utils/cssFlags.tsx +53 -0
  61. package/utils/getCssFlagInitScript.tsx +20 -0
  62. package/utils/normalizeLocale.ts +26 -0
  63. package/utils/sitemap.ts +1 -1
  64. package/utils/storefrontConfig.ts +8 -0
  65. package/icons/index.ts +0 -48
@@ -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
package/JsonLd/JsonLd.tsx CHANGED
@@ -2,14 +2,15 @@ import Head from 'next/head'
2
2
  import { safeJsonLdReplacer } from './safeJsonLdReplacer'
3
3
 
4
4
  export function JsonLd<T extends { '@type': string }>(props: {
5
+ keyVal?: string
5
6
  item: T & { '@context': 'https://schema.org' }
6
7
  }) {
7
- const { item } = props
8
+ const { item, keyVal } = props
8
9
 
9
10
  return (
10
11
  <Head>
11
12
  <script
12
- key='jsonld'
13
+ key={keyVal ?? 'jsonld'}
13
14
  type='application/ld+json'
14
15
  // eslint-disable-next-line react/no-danger
15
16
  dangerouslySetInnerHTML={{ __html: JSON.stringify(item, safeJsonLdReplacer) }}
@@ -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,6 +152,9 @@ export function LayoutHeader(props: LayoutHeaderProps) {
140
152
  '&.divider': {
141
153
  marginBottom: 0,
142
154
  },
155
+ '&.hideMd .LayoutHeaderContent-left': {
156
+ display: 'none',
157
+ },
143
158
  },
144
159
  }),
145
160
  ...(Array.isArray(sx) ? sx : [sx]),
@@ -159,5 +174,4 @@ export function LayoutHeader(props: LayoutHeaderProps) {
159
174
  </LayoutHeaderContent>
160
175
  </Box>
161
176
  )
162
- }
163
- 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
@@ -40,12 +40,7 @@ export const LayoutTitle = React.forwardRef<HTMLDivElement, TitleProps>((props,
40
40
  alignItems: 'center',
41
41
  justifyContent: 'center',
42
42
  gap: `6px`,
43
- flexFlow: 'unset',
44
- [theme.breakpoints.up('md')]: {
45
- flexFlow: 'column',
46
- },
47
43
  '&.sizeSmall': {
48
- flexFlow: 'unset',
49
44
  overflow: 'hidden',
50
45
  '& svg': {
51
46
  width: responsiveVal(24, 28),
@@ -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
  }
@@ -1,13 +1,7 @@
1
1
  import { usePageContext } from '@graphcommerce/framer-next-pages'
2
- import { addBasePath } from 'next/dist/client/add-base-path'
3
- import { addLocale } from 'next/dist/client/add-locale'
4
- import { getDomainLocale } from 'next/dist/client/get-domain-locale'
5
- import { resolveHref } from 'next/dist/client/resolve-href'
6
- import { NextRouter } from 'next/dist/shared/lib/router/router'
7
2
  import Head from 'next/head'
8
- import { useRouter } from 'next/router'
9
3
  import type {} from '@graphcommerce/next-config'
10
- import { storefrontConfig } from '../hooks'
4
+ import { Canonical, useCanonical } from './canonicalize'
11
5
 
12
6
  // https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives
13
7
  export type MetaRobots =
@@ -23,8 +17,6 @@ export type MetaRobots =
23
17
  | `max-video-preview:${number}`
24
18
  type MetaRobotsAll = ['all' | 'none']
25
19
 
26
- type Canonical = `http://${string}` | `https://${string}` | `/${string}` | string
27
-
28
20
  export type PageMetaProps = {
29
21
  title: string
30
22
  canonical?: Canonical
@@ -36,73 +28,6 @@ export type PageMetaProps = {
36
28
  ogType?: string | null
37
29
  }
38
30
 
39
- type PartialNextRouter = Pick<
40
- NextRouter,
41
- 'pathname' | 'locale' | 'locales' | 'isLocaleDomain' | 'domainLocales' | 'defaultLocale'
42
- >
43
- export function canonicalize(router: PartialNextRouter, incoming?: Canonical) {
44
- let canonical = incoming
45
-
46
- if (!canonical) return canonical
47
-
48
- if (!canonical.startsWith('http') && !canonical.startsWith('/')) {
49
- if (process.env.NODE_ENV !== 'production') {
50
- console.warn(
51
- `canonical is relative (${canonical}), a canonical must start with '/', 'http://' or 'https://'`,
52
- )
53
- }
54
- canonical = `/${canonical}`
55
- }
56
-
57
- if (canonical.startsWith('/')) {
58
- let [href, as = href] = resolveHref(router as NextRouter, canonical, true)
59
-
60
- const curLocale = router.locale
61
-
62
- // Copied from here https://github.com/vercel/next.js/blob/213c42f446874d29d07fa2cca6e6b11fc9c3b711/packages/next/client/link.tsx#L512
63
- const localeDomain = getDomainLocale(
64
- as,
65
- curLocale,
66
- router && router.locales,
67
- router.domainLocales,
68
- )
69
-
70
- if (localeDomain) {
71
- canonical = localeDomain
72
- } else {
73
- const conf = storefrontConfig(router.locale)
74
-
75
- href = addBasePath(
76
- addLocale(as, curLocale, conf?.domain ? conf.locale : router.defaultLocale),
77
- )
78
-
79
- let siteUrl = conf?.canonicalBaseUrl || import.meta.graphCommerce.canonicalBaseUrl
80
-
81
- if (conf?.domain && !conf?.canonicalBaseUrl) siteUrl = `https://${conf.domain}`
82
-
83
- if (siteUrl.endsWith('/')) siteUrl = siteUrl.slice(0, -1)
84
-
85
- canonical = `${siteUrl}${href}`
86
- }
87
- }
88
-
89
- if (!canonical.startsWith('http')) {
90
- if (process.env.NODE_ENV !== 'production') {
91
- throw new Error(
92
- `canonical must start with '/', 'http://' or 'https://', '${canonical}' given`,
93
- )
94
- }
95
- canonical = undefined
96
- }
97
-
98
- return canonical
99
- }
100
-
101
- export function useCanonical(incoming?: Canonical) {
102
- const router = useRouter()
103
- return canonicalize(router, incoming)
104
- }
105
-
106
31
  export function PageMeta(props: PageMetaProps) {
107
32
  const { active } = usePageContext()
108
33
  const {