@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
@@ -14,6 +14,7 @@ import { useRouter } from 'next/router'
14
14
  import { createContext, useContext, useEffect, useMemo, useState } from 'react'
15
15
  import { IconSvg } from '../IconSvg'
16
16
  import { iconMoon, iconSun } from '../icons'
17
+ import { getCssFlag, setCssFlag } from '../utils/cssFlags'
17
18
 
18
19
  type Mode = 'dark' | 'light'
19
20
  type UserMode = 'auto' | Mode
@@ -22,6 +23,7 @@ type ColorModeContext = {
22
23
  userMode: UserMode
23
24
  browserMode: Mode
24
25
  currentMode: Mode
26
+ isSingleMode: boolean
25
27
  toggle: () => void
26
28
  }
27
29
 
@@ -29,48 +31,59 @@ export const colorModeContext = createContext(undefined as unknown as ColorModeC
29
31
  colorModeContext.displayName = 'ColorModeContext'
30
32
 
31
33
  type ThemeProviderProps = {
32
- // eslint-disable-next-line react/no-unused-prop-types
33
- light: Theme
34
- // eslint-disable-next-line react/no-unused-prop-types
35
- dark: Theme
36
-
37
34
  children: React.ReactNode
38
- }
35
+ ssrMode?: Mode
36
+ listenToBrowser?: boolean
37
+ } & (
38
+ | { light: Theme; dark?: undefined }
39
+ | { light?: undefined; dark: Theme }
40
+ | { light: Theme; dark: Theme }
41
+ )
39
42
 
40
43
  /**
41
44
  * Wrapper around `import { ThemeProvider } from '@mui/material'`
42
45
  *
43
46
  * The multi DarkLightModeThemeProvider allows switching between light and dark mode based on URL
44
47
  * and on user input.
45
- *
46
- * If you _just_ wan't a single theme, use the import { ThemeProvider } from '@mui/material' instead.
47
48
  */
48
49
  export function DarkLightModeThemeProvider(props: ThemeProviderProps) {
49
- const { children, light, dark } = props
50
+ const { children, light, dark, ssrMode = 'light', listenToBrowser = true } = props
51
+ const [configuredMode, setConfiguredMode] = useState<UserMode>(listenToBrowser ? 'auto' : ssrMode)
52
+ const setThemeMode = (mode: UserMode) => {
53
+ setConfiguredMode(mode)
54
+ setCssFlag('color-scheme', mode)
55
+ }
50
56
 
51
- // todo: Save this in local storage
52
- const [userMode, setUserMode] = useState<UserMode>('auto')
53
57
  const browserMode: Mode = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light'
54
58
 
55
59
  // If the user has set a mode, use that. Otherwise, use the browser mode.
56
- const currentMode = userMode === 'auto' ? browserMode : userMode
57
- const theme = currentMode === 'light' ? light : dark
60
+ const currentMode = configuredMode === 'auto' ? browserMode : configuredMode
61
+
62
+ let theme: Theme = light || dark // Default
63
+ if (light && currentMode === 'light') theme = light
64
+ else if (dark) theme = dark
65
+
66
+ useEffect(() => {
67
+ const flag = getCssFlag('color-scheme') as Mode
68
+ if (flag) setConfiguredMode(flag)
69
+ }, [setConfiguredMode])
58
70
 
59
71
  // If a URL parameter is present, switch from auto to light or dark mode
60
72
  const { asPath } = useRouter()
61
73
  useEffect(() => {
62
- if (asPath.includes('darkmode')) setUserMode('dark')
74
+ if (asPath.includes('darkmode')) setConfiguredMode('dark')
63
75
  }, [asPath])
64
76
 
65
77
  // Create the context
66
78
  const colorContext: ColorModeContext = useMemo(
67
79
  () => ({
68
80
  browserMode,
69
- userMode,
81
+ userMode: configuredMode,
70
82
  currentMode,
71
- toggle: () => setUserMode(currentMode === 'light' ? 'dark' : 'light'),
83
+ isSingleMode: !light || !dark,
84
+ toggle: () => setThemeMode(currentMode === 'light' ? 'dark' : 'light'),
72
85
  }),
73
- [browserMode, currentMode, userMode],
86
+ [browserMode, configuredMode, currentMode, light, dark],
74
87
  )
75
88
 
76
89
  return (
@@ -85,7 +98,12 @@ export function useColorMode() {
85
98
  }
86
99
 
87
100
  export function DarkLightModeToggleFab(props: Omit<FabProps, 'onClick'>) {
88
- const { currentMode, toggle } = useColorMode()
101
+ const { currentMode, isSingleMode, toggle } = useColorMode()
102
+
103
+ if (isSingleMode) {
104
+ return null
105
+ }
106
+
89
107
  return (
90
108
  <Fab size='large' color='inherit' onClick={toggle} {...props}>
91
109
  <IconSvg src={currentMode === 'light' ? iconMoon : iconSun} size='large' />
@@ -100,7 +118,11 @@ export function DarkLightModeToggleFab(props: Omit<FabProps, 'onClick'>) {
100
118
  */
101
119
  export function DarkLightModeMenuSecondaryItem(props: ListItemButtonProps) {
102
120
  const { sx = [] } = props
103
- const { currentMode, toggle } = useColorMode()
121
+ const { currentMode, isSingleMode, toggle } = useColorMode()
122
+
123
+ if (isSingleMode) {
124
+ return null
125
+ }
104
126
 
105
127
  return (
106
128
  <ListItemButton {...props} sx={[{}, ...(Array.isArray(sx) ? sx : [sx])]} dense onClick={toggle}>
@@ -1,17 +1,23 @@
1
+ import { useLocale } from '../hooks/useLocale'
2
+
1
3
  export type TimeAgoProps = {
2
4
  date: Date
5
+ /**
6
+ * @deprecated No longer used
7
+ */
3
8
  locale?: string
4
9
  }
5
10
 
6
11
  export function TimeAgo(props: TimeAgoProps) {
7
- const { date, locale = 'en' } = props
12
+ const { date } = props
8
13
  const msPerMinute = 60 * 1000
9
14
  const msPerHour = msPerMinute * 60
10
15
  const msPerDay = msPerHour * 24
11
16
 
12
17
  const timestamp = date.getTime()
13
18
  const elapsed = Date.now() - timestamp
14
- const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
19
+
20
+ const rtf = new Intl.RelativeTimeFormat(useLocale(), { numeric: 'auto' })
15
21
 
16
22
  if (elapsed < msPerMinute) {
17
23
  return <span>{rtf.format(-Math.floor(elapsed / 1000), 'seconds')}</span>
package/hooks/index.ts CHANGED
@@ -5,3 +5,5 @@ export * from './useNumberFormat'
5
5
  export * from './useMemoObject'
6
6
  export * from './useStorefrontConfig'
7
7
  export * from './useUrlQuery'
8
+ export * from './useSsr'
9
+ export * from './useLocale'
@@ -1,15 +1,11 @@
1
- import { normalizeLocale } from '@graphcommerce/lingui-next'
2
- import { useRouter } from 'next/router'
3
1
  import { useMemo } from 'react'
2
+ import { useLocale } from './useLocale'
4
3
 
5
4
  export type DateTimeFormatProps = Intl.DateTimeFormatOptions
6
5
 
7
6
  export function useDateTimeFormat(props?: DateTimeFormatProps) {
8
- const { locale } = useRouter()
7
+ const locale = useLocale()
9
8
 
10
- const formatter = useMemo(
11
- () => new Intl.DateTimeFormat(normalizeLocale(locale), props),
12
- [locale, props],
13
- )
9
+ const formatter = useMemo(() => new Intl.DateTimeFormat(locale, props), [locale, props])
14
10
  return formatter
15
11
  }
@@ -0,0 +1,7 @@
1
+ import { normalizeLocale } from '@graphcommerce/lingui-next'
2
+ import { useStorefrontConfig } from './useStorefrontConfig'
3
+
4
+ export function useLocale() {
5
+ const { locale } = useStorefrontConfig()
6
+ return normalizeLocale(locale)
7
+ }
@@ -1,5 +1,24 @@
1
1
  import { Breakpoint, useTheme } from '@mui/material'
2
- import { useMemo } from 'react'
2
+ import { useMotionValue } from 'framer-motion'
3
+ import { useEffect, useMemo } from 'react'
4
+
5
+ export function useMatchMediaMotionValue(
6
+ direction: 'up' | 'down',
7
+ breakpointKey: number | Breakpoint,
8
+ ) {
9
+ const matchValue = useMotionValue(false)
10
+ const theme = useTheme()
11
+ const query = theme.breakpoints[direction](breakpointKey).replace(/^@media( ?)/m, '')
12
+
13
+ useEffect(() => {
14
+ const mql = globalThis?.matchMedia(query)
15
+ const matcher = (e: MediaQueryListEvent) => matchValue.set(e.matches)
16
+ mql.addEventListener('change', matcher)
17
+ return () => mql.removeEventListener('change', matcher)
18
+ }, [matchValue, query])
19
+
20
+ return matchValue
21
+ }
3
22
 
4
23
  export function useMatchMedia() {
5
24
  const theme = useTheme()
@@ -1,15 +1,10 @@
1
- import { normalizeLocale } from '@graphcommerce/lingui-next'
2
- import { useRouter } from 'next/router'
3
1
  import { useMemo } from 'react'
2
+ import { useLocale } from './useLocale'
4
3
 
5
4
  export type NumberFormatProps = Intl.NumberFormatOptions
6
5
 
7
6
  export function useNumberFormat(props?: NumberFormatProps) {
8
- const { locale } = useRouter()
9
-
10
- const formatter = useMemo(
11
- () => new Intl.NumberFormat(normalizeLocale(locale), props),
12
- [locale, props],
13
- )
7
+ const locale = useLocale()
8
+ const formatter = useMemo(() => new Intl.NumberFormat(locale, props), [locale, props])
14
9
  return formatter
15
10
  }
@@ -0,0 +1,11 @@
1
+ import { useSyncExternalStore } from 'react'
2
+
3
+ const emptySubscribe = () => () => {}
4
+
5
+ export function useIsSSR() {
6
+ return useSyncExternalStore(
7
+ emptySubscribe,
8
+ () => false,
9
+ () => true,
10
+ )
11
+ }
package/icons.ts ADDED
@@ -0,0 +1,50 @@
1
+ export { default as iconArrowDown } from './icons/arrow-down.svg'
2
+ export { default as iconArrowBack } from './icons/arrow-left.svg'
3
+ export { default as iconArrowForward } from './icons/arrow-right.svg'
4
+ export { default as iconArrowUp } from './icons/arrow-up.svg'
5
+ export { default as iconShoppingBag } from './icons/bag.svg'
6
+ export { default as iconInvoice } from './icons/box-alt.svg'
7
+ export { default as iconBox } from './icons/box.svg'
8
+ export { default as iconOrderBefore } from './icons/calendar.svg'
9
+ export { default as iconCancelAlt } from './icons/cancel-alt.svg'
10
+ export { default as iconCartAdd } from './icons/cart-add.svg'
11
+ export { default as iconCart } from './icons/cart.svg'
12
+ export { default as iconChat } from './icons/chat-alt.svg'
13
+ export { default as iconCustomerService } from './icons/chat.svg'
14
+ export { default as iconChevronDown } from './icons/chevron-down.svg'
15
+ export { default as iconChevronBack, default as iconChevronLeft } from './icons/chevron-left.svg'
16
+ export { default as iconChevronRight } from './icons/chevron-right.svg'
17
+ export { default as iconChevronUp } from './icons/chevron-up.svg'
18
+ export { default as iconCirle } from './icons/circle.svg'
19
+ export { default as iconClose } from './icons/close.svg'
20
+ export { default as iconCompare } from './icons/compare-arrows.svg'
21
+ export { default as iconCreditCard, default as iconId } from './icons/credit-card.svg'
22
+ export { default as iconEllypsis } from './icons/ellypsis.svg'
23
+ export { default as iconEmail, default as iconEmailOutline } from './icons/envelope-alt.svg'
24
+ export { default as icon404 } from './icons/explore.svg'
25
+ export { default as iconEyeClosed } from './icons/eye-closed.svg'
26
+ export { default as iconEyeCrossed } from './icons/eye-crossed.svg'
27
+ export { default as iconEye } from './icons/eye.svg'
28
+ export { default as iconHeart } from './icons/favourite.svg'
29
+ export { default as iconMenu } from './icons/hamburger.svg'
30
+ export { default as iconParty } from './icons/happy-face.svg'
31
+ export { default as iconAddresses, default as iconHome } from './icons/home-alt.svg'
32
+ export { default as iconLanguage } from './icons/language.svg'
33
+ export { default as iconLocation } from './icons/location.svg'
34
+ export { default as iconLock } from './icons/lock.svg'
35
+ export { default as iconFullscreen } from './icons/maximise.svg'
36
+ export { default as iconFullscreenExit } from './icons/minimise.svg'
37
+ export { default as iconMin } from './icons/minus.svg'
38
+ export { default as iconMoon } from './icons/moon.svg'
39
+ export { default as iconNewspaper } from './icons/news.svg'
40
+ export { default as iconCheckmark } from './icons/ok.svg'
41
+ export { default as iconPerson } from './icons/person-alt.svg'
42
+ export { default as iconPlay } from './icons/play.svg'
43
+ export { default as iconPlus } from './icons/plus.svg'
44
+ export { default as iconShutdown } from './icons/power.svg'
45
+ export { default as iconRefresh } from './icons/refresh.svg'
46
+ export { default as iconSadFace } from './icons/sad-face.svg'
47
+ export { default as iconSearch } from './icons/search.svg'
48
+ export { default as iconPhone } from './icons/smartphone.svg'
49
+ export { default as iconStar } from './icons/star.svg'
50
+ export { default as iconSun } from './icons/sun.svg'
package/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from './Blog/BlogListItem/BlogListItem'
8
8
  export * from './Blog/BlogTags/BlogTags'
9
9
  export * from './Blog/BlogTitle/BlogTitle'
10
10
  export * from './Button'
11
+ export * from './Breadcrumbs'
11
12
  export * from './ChipMenu/ChipMenu'
12
13
  export * from './ContainerWithHeader/ContainerWithHeader'
13
14
  export * from './Fab'
@@ -59,3 +60,6 @@ export * from './UspList/UspListItem'
59
60
  export * from './hooks'
60
61
  export * from './icons'
61
62
  export * from './utils/cookie'
63
+ export * from './utils/sitemap'
64
+ export * from './utils/robots'
65
+ export * from './utils/cssFlags'
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.3",
5
+ "version": "8.1.0-canary.30",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -17,6 +17,7 @@
17
17
  "@emotion/server": "^11.11.0",
18
18
  "@emotion/styled": "^11.11.0",
19
19
  "cookie": "^0.6.0",
20
+ "next-sitemap": "4.2.3",
20
21
  "react-is": "^18.2.0"
21
22
  },
22
23
  "devDependencies": {
@@ -25,14 +26,14 @@
25
26
  "typescript": "5.3.3"
26
27
  },
27
28
  "peerDependencies": {
28
- "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.3",
29
- "@graphcommerce/framer-next-pages": "^8.1.0-canary.3",
30
- "@graphcommerce/framer-scroller": "^8.1.0-canary.3",
31
- "@graphcommerce/framer-utils": "^8.1.0-canary.3",
32
- "@graphcommerce/image": "^8.1.0-canary.3",
33
- "@graphcommerce/lingui-next": "^8.1.0-canary.3",
34
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.3",
35
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.3",
29
+ "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.30",
30
+ "@graphcommerce/framer-next-pages": "^8.1.0-canary.30",
31
+ "@graphcommerce/framer-scroller": "^8.1.0-canary.30",
32
+ "@graphcommerce/framer-utils": "^8.1.0-canary.30",
33
+ "@graphcommerce/image": "^8.1.0-canary.30",
34
+ "@graphcommerce/lingui-next": "^8.1.0-canary.30",
35
+ "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.30",
36
+ "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.30",
36
37
  "@lingui/core": "^4.2.1",
37
38
  "@lingui/macro": "^4.2.1",
38
39
  "@lingui/react": "^4.2.1",
package/types.d.ts CHANGED
@@ -6,12 +6,6 @@ import './Theme/createTheme'
6
6
  // eslint-disable-next-line react/no-typos
7
7
  import 'react'
8
8
 
9
- declare module 'react' {
10
- interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
11
- inert?: 'true'
12
- }
13
- }
14
-
15
9
  declare module '*.po' {
16
10
  const messages: Record<
17
11
  string,
@@ -0,0 +1,76 @@
1
+ export const FLAGS_STORAGE_KEY = 'gc-flags'
2
+ export const FLAG_PREFIX = 'data'
3
+
4
+ export function getCssFlagsInitScript() {
5
+ return (
6
+ <script
7
+ id='init-gc-flags'
8
+ key='mui-color-scheme-init'
9
+ // eslint-disable-next-line react/no-danger
10
+ dangerouslySetInnerHTML={{
11
+ __html: `(function() {
12
+ try {
13
+ const flags = JSON.parse(localStorage.getItem('${FLAGS_STORAGE_KEY}') || '{}')
14
+ Object.entries(flags).forEach(([key, val]) => {
15
+ document.documentElement.setAttribute('data-' +key, typeof val === 'boolean' ? '' : val)
16
+ })
17
+ } catch(e){}})();`,
18
+ }}
19
+ />
20
+ )
21
+ }
22
+
23
+ function loadFlags() {
24
+ const flags = JSON.parse(localStorage.getItem(FLAGS_STORAGE_KEY) || '{}')
25
+ if (typeof flags !== 'object' && flags !== null) return {}
26
+ return flags as Record<string, true | string>
27
+ }
28
+
29
+ function saveFlags(flags: Record<string, true | string>) {
30
+ window.localStorage?.setItem(FLAGS_STORAGE_KEY, JSON.stringify(flags))
31
+ }
32
+
33
+ export function removeCssFlag(flagName: string) {
34
+ const flags = loadFlags()
35
+ delete flags[flagName]
36
+ document.documentElement.removeAttribute(`data-${flagName}`)
37
+ saveFlags(flags)
38
+ }
39
+
40
+ export function setCssFlag(flagName: string, val: true | string) {
41
+ document.documentElement.setAttribute(`data-${flagName}`, typeof val === 'boolean' ? '' : val)
42
+
43
+ const flags = loadFlags()
44
+ flags[flagName] = val
45
+ saveFlags(flags)
46
+ }
47
+
48
+ /**
49
+ * @deprecated flags are not intendend to be used in JS, so this should only be used for debugging purposes.
50
+ */
51
+ export function getCssFlag(flagName: string) {
52
+ return loadFlags()[flagName]
53
+ }
54
+
55
+ /**
56
+ * Easily create a CSS selector that only applies when a flag is set.
57
+ *
58
+ * Example:
59
+ *
60
+ * ```tsx
61
+ * <Box sx={{ [cssFlagSelector('dark')]: { color: 'white' } }} />
62
+ * ```
63
+ */
64
+ export const cssFlag = <T extends string>(flagName: T) => `html[data-${flagName}] &` as const
65
+
66
+ /**
67
+ * Easily create a CSS selector that only applies when a flag is not set.
68
+ *
69
+ * Example:
70
+ *
71
+ * ```tsx
72
+ * <Box sx={{ [cssNotFlagSelector('dark')]: { color: 'black' } }} />
73
+ * ```
74
+ */
75
+ export const cssNotFlag = <T extends string>(flagName: T) =>
76
+ `html:not([data-${flagName}]) &` as const
@@ -0,0 +1,41 @@
1
+ import { GetServerSidePropsContext } from 'next'
2
+
3
+ type Stringifyable = boolean | string | number | null | undefined
4
+
5
+ /**
6
+ * Tagged template literal for robots.txt that will automatically stringify values and indent them correctly.
7
+ * https://developers.google.com/search/docs/crawling-indexing/robots/robots_txt#syntax
8
+ */
9
+ export function robotsTxt(strings: TemplateStringsArray, ...values: Stringifyable[]) {
10
+ return strings
11
+ .reduce((acc, str, i) => {
12
+ let value = values[i]
13
+
14
+ if (Array.isArray(value)) {
15
+ const [conditional, val] = value
16
+ value = conditional ? val : null
17
+ }
18
+ if (!value) value = ''
19
+ if (typeof value === 'boolean') value = value ? 'true' : 'false'
20
+ if (typeof value === 'number') value = String(value)
21
+
22
+ return acc + str + value
23
+ }, '')
24
+ .split('\n')
25
+ .map((v) => v.trim())
26
+ .join('\n')
27
+ .trim()
28
+ }
29
+
30
+ // eslint-disable-next-line @typescript-eslint/require-await
31
+ export async function getServerSidePropsRobotsTxt(
32
+ context: GetServerSidePropsContext,
33
+ robots: string,
34
+ ) {
35
+ context.res.setHeader('Content-Type', 'text/plain')
36
+ context.res.write(robots)
37
+ context.res.end()
38
+ context.res.setHeader('Cache-Control', `public, s-maxage=${60 * 60 * 2}`)
39
+
40
+ return { props: {} }
41
+ }
@@ -0,0 +1,47 @@
1
+ import { GetServerSidePropsContext, GetStaticPathsResult } from 'next'
2
+ import { getServerSideSitemapLegacy, ISitemapField } from 'next-sitemap'
3
+ import { canonicalize } from '../PageMeta/PageMeta'
4
+
5
+ export function excludeSitemap(excludes: string[]) {
6
+ const regexp = excludes.map((exclude) => new RegExp(exclude.replace(/\*/g, '.*?')))
7
+
8
+ return (path: string) => !regexp.some((pattern) => pattern.test(`/${path}`))
9
+ }
10
+
11
+ export function staticPathsToString(
12
+ path: GetStaticPathsResult<{ url: string[] | string }>['paths'][0],
13
+ ) {
14
+ if (typeof path === 'string') return path
15
+ if (typeof path.params.url === 'string') return path.params.url
16
+ return path.params.url.join('/')
17
+ }
18
+
19
+ export function toSitemapFields(
20
+ context: GetServerSidePropsContext,
21
+ paths: string[],
22
+ ): ISitemapField[] {
23
+ const lastmod = new Date().toISOString()
24
+ const options = {
25
+ locale: context.locale,
26
+ defaultLocale: context.defaultLocale,
27
+ pathname: '/',
28
+ isLocaleDomain: false,
29
+ }
30
+
31
+ const sitemapPaths = paths.map<ISitemapField>((path) => ({
32
+ loc: canonicalize(options, `/${path}`) ?? '',
33
+ lastmod,
34
+ changefreq: 'daily',
35
+ priority: 0.7,
36
+ }))
37
+
38
+ return sitemapPaths
39
+ }
40
+
41
+ export function getServerSidePropsSitemap(
42
+ context: GetServerSidePropsContext,
43
+ paths: ISitemapField[],
44
+ ) {
45
+ context.res.setHeader('Cache-Control', `public, s-maxage=${60 * 60 * 2}`)
46
+ return getServerSideSitemapLegacy(context, paths)
47
+ }
package/icons/index.ts DELETED
@@ -1,48 +0,0 @@
1
- export { default as iconArrowBack } from './arrow-left.svg'
2
- export { default as iconArrowForward } from './arrow-right.svg'
3
- export { default as iconShoppingBag } from './bag.svg'
4
- export { default as iconInvoice } from './box-alt.svg'
5
- export { default as iconBox } from './box.svg'
6
- export { default as iconOrderBefore } from './calendar.svg'
7
- export { default as iconCancelAlt } from './cancel-alt.svg'
8
- export { default as iconCartAdd } from './cart-add.svg'
9
- export { default as iconCart } from './cart.svg'
10
- export { default as iconChat } from './chat-alt.svg'
11
- export { default as iconCustomerService } from './chat.svg'
12
- export { default as iconChevronDown } from './chevron-down.svg'
13
- export { default as iconChevronBack, default as iconChevronLeft } from './chevron-left.svg'
14
- export { default as iconChevronRight } from './chevron-right.svg'
15
- export { default as iconChevronUp } from './chevron-up.svg'
16
- export { default as iconCirle } from './circle.svg'
17
- export { default as iconClose } from './close.svg'
18
- export { default as iconCreditCard, default as iconId } from './credit-card.svg'
19
- export { default as iconEllypsis } from './ellypsis.svg'
20
- export { default as iconEmail, default as iconEmailOutline } from './envelope-alt.svg'
21
- export { default as icon404 } from './explore.svg'
22
- export { default as iconEyeClosed } from './eye-closed.svg'
23
- export { default as iconEyeCrossed } from './eye-crossed.svg'
24
- export { default as iconEye } from './eye.svg'
25
- export { default as iconHeart } from './favourite.svg'
26
- export { default as iconMenu } from './hamburger.svg'
27
- export { default as iconParty } from './happy-face.svg'
28
- export { default as iconAddresses, default as iconHome } from './home-alt.svg'
29
- export { default as iconLanguage } from './language.svg'
30
- export { default as iconLocation } from './location.svg'
31
- export { default as iconLock } from './lock.svg'
32
- export { default as iconFullscreen } from './maximise.svg'
33
- export { default as iconFullscreenExit } from './minimise.svg'
34
- export { default as iconMin } from './minus.svg'
35
- export { default as iconMoon } from './moon.svg'
36
- export { default as iconNewspaper } from './news.svg'
37
- export { default as iconCheckmark } from './ok.svg'
38
- export { default as iconPerson } from './person-alt.svg'
39
- export { default as iconPlay } from './play.svg'
40
- export { default as iconPlus } from './plus.svg'
41
- export { default as iconShutdown } from './power.svg'
42
- export { default as iconRefresh } from './refresh.svg'
43
- export { default as iconSadFace } from './sad-face.svg'
44
- export { default as iconSearch } from './search.svg'
45
- export { default as iconPhone } from './smartphone.svg'
46
- export { default as iconStar } from './star.svg'
47
- export { default as iconSun } from './sun.svg'
48
- export { default as iconCompare } from './compare-arrows.svg'