@graphcommerce/next-ui 8.1.0-canary.8 → 9.0.0-canary.100

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 (83) hide show
  1. package/ActionCard/ActionCard.tsx +66 -39
  2. package/ActionCard/ActionCardAccordion.tsx +2 -1
  3. package/ActionCard/ActionCardLayout.tsx +1 -1
  4. package/ActionCard/ActionCardList.tsx +11 -10
  5. package/ActionCard/index.ts +0 -1
  6. package/Blog/BlogAuthor/BlogAuthor.tsx +2 -4
  7. package/Blog/BlogListItem/BlogListItem.tsx +2 -4
  8. package/Blog/BlogTags/BlogTag.tsx +4 -1
  9. package/Breadcrumbs/Breadcrumbs.tsx +195 -0
  10. package/Breadcrumbs/BreadcrumbsJsonLd.tsx +13 -0
  11. package/Breadcrumbs/BreadcrumbsList.tsx +101 -0
  12. package/Breadcrumbs/BreadcrumbsPopper.tsx +48 -0
  13. package/Breadcrumbs/index.ts +4 -0
  14. package/Breadcrumbs/jsonLdBreadcrumb.tsx +19 -0
  15. package/Breadcrumbs/types.ts +11 -0
  16. package/CHANGELOG.md +341 -98
  17. package/Config.graphqls +5 -0
  18. package/Document/DocumentBodyEnd.tsx +7 -0
  19. package/Document/DocumentBodyStart.tsx +7 -0
  20. package/Document/DocumentHeadEnd.tsx +7 -0
  21. package/Document/DocumentHeadStart.tsx +7 -0
  22. package/Document/index.ts +4 -0
  23. package/Footer/Footer.tsx +1 -1
  24. package/FramerScroller/SidebarGallery.tsx +54 -27
  25. package/FullPageMessage/FullPageMessage.tsx +1 -0
  26. package/Intl/DateTimeFormat/DateFormat.tsx +10 -0
  27. package/Intl/DateTimeFormat/DateTimeFormat.tsx +20 -0
  28. package/Intl/DateTimeFormat/TimeFormat.tsx +10 -0
  29. package/Intl/DateTimeFormat/index.ts +3 -0
  30. package/Intl/DateTimeFormat/toDate.ts +16 -0
  31. package/Intl/DisplayNames/DisplayNames.tsx +22 -0
  32. package/Intl/DisplayNames/index.ts +1 -0
  33. package/Intl/ListFormat.tsx +33 -0
  34. package/Intl/NumberFormat/CurrencyFormat.tsx +191 -0
  35. package/Intl/NumberFormat/NumberFormat.tsx +24 -0
  36. package/Intl/NumberFormat/PercentFormat.tsx +5 -0
  37. package/Intl/NumberFormat/UnitFormat.tsx +59 -0
  38. package/Intl/NumberFormat/index.ts +4 -0
  39. package/Intl/RelativeTimeFormat/RelativeTimeFormat.tsx +34 -0
  40. package/Intl/RelativeTimeFormat/RelativeToTimeFormat.tsx +35 -0
  41. package/Intl/RelativeTimeFormat/index.ts +2 -0
  42. package/Intl/RelativeTimeFormat/relativeTimeFormatAutoUnit.ts +23 -0
  43. package/Intl/index.ts +11 -0
  44. package/JsonLd/JsonLd.tsx +3 -2
  45. package/Layout/components/LayoutHeader.tsx +30 -10
  46. package/Layout/components/LayoutHeaderBack.tsx +20 -9
  47. package/Layout/components/LayoutHeaderClose.tsx +8 -2
  48. package/Layout/components/LayoutTitle.tsx +0 -5
  49. package/LayoutDefault/components/LayoutDefault.tsx +15 -13
  50. package/LazyHydrate/LazyHydrate.tsx +19 -3
  51. package/Overlay/components/OverlayBase.tsx +31 -6
  52. package/Overlay/components/OverlayHeader.tsx +27 -0
  53. package/Overlay/components/index.ts +1 -0
  54. package/PageLoadIndicator/PageLoadIndicator.tsx +25 -14
  55. package/PageMeta/PageMeta.tsx +1 -71
  56. package/PageMeta/canonicalize.ts +78 -0
  57. package/Pagination/PaginationExtended.tsx +103 -0
  58. package/Snackbar/MessageSnackbarImpl.tsx +60 -19
  59. package/Styles/responsiveVal.tsx +4 -5
  60. package/Styles/withEmotionCache.tsx +2 -2
  61. package/Theme/DarkLightModeThemeProvider.tsx +41 -19
  62. package/Theme/MuiButton.ts +11 -3
  63. package/TimeAgo/TimeAgo.tsx +3 -0
  64. package/hooks/index.ts +2 -1
  65. package/hooks/memoDeep.ts +38 -0
  66. package/hooks/useDateTimeFormat.ts +4 -4
  67. package/hooks/{useSsr.ts → useIsSsr.ts} +3 -0
  68. package/hooks/useLocale.ts +2 -2
  69. package/hooks/useNumberFormat.ts +4 -3
  70. package/hooks/useStorefrontConfig.ts +1 -9
  71. package/hooks/useUrlQuery.ts +6 -4
  72. package/icons.ts +55 -0
  73. package/index.ts +11 -2
  74. package/package.json +14 -14
  75. package/server.ts +9 -0
  76. package/utils/cssFlags.tsx +53 -0
  77. package/utils/getCssFlagInitScript.tsx +20 -0
  78. package/utils/normalizeLocale.ts +26 -0
  79. package/utils/robots.ts +41 -0
  80. package/utils/sitemap.ts +47 -0
  81. package/utils/storefrontConfig.ts +8 -0
  82. package/ActionCard/ActionCardListForm.tsx +0 -88
  83. package/icons/index.ts +0 -48
package/Config.graphqls CHANGED
@@ -3,6 +3,11 @@ extend input GraphCommerceConfig {
3
3
  Configuration for the SidebarGallery component
4
4
  """
5
5
  sidebarGallery: SidebarGalleryConfig
6
+
7
+ """
8
+ Configuration for the SidebarGallery component
9
+ """
10
+ breadcrumbs: Boolean = false
6
11
  }
7
12
 
8
13
  """
@@ -0,0 +1,7 @@
1
+ // eslint-disable-next-line @next/next/no-document-import-in-page
2
+ import { DocumentProps } from 'next/document'
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ export function DocumentBodyEnd(props: DocumentProps) {
6
+ return null
7
+ }
@@ -0,0 +1,7 @@
1
+ // eslint-disable-next-line @next/next/no-document-import-in-page
2
+ import { DocumentProps } from 'next/document'
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ export function DocumentBodyStart(props: DocumentProps) {
6
+ return null
7
+ }
@@ -0,0 +1,7 @@
1
+ // eslint-disable-next-line @next/next/no-document-import-in-page
2
+ import { DocumentProps } from 'next/document'
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ export function DocumentHeadEnd(props: DocumentProps) {
6
+ return null
7
+ }
@@ -0,0 +1,7 @@
1
+ // eslint-disable-next-line @next/next/no-document-import-in-page
2
+ import { DocumentProps } from 'next/document'
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ export function DocumentHeadStart(props: DocumentProps) {
6
+ return null
7
+ }
@@ -0,0 +1,4 @@
1
+ export * from './DocumentBodyEnd'
2
+ export * from './DocumentBodyStart'
3
+ export * from './DocumentHeadEnd'
4
+ export * from './DocumentHeadStart'
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) => ({
@@ -31,7 +31,14 @@ import { iconChevronLeft, iconChevronRight, iconFullscreen, iconFullscreenExit }
31
31
 
32
32
  const MotionBox = styled(m.div)({})
33
33
 
34
- type OwnerState = { zoomed: boolean; disableZoom: boolean }
34
+ type SidebarGalleryVariant = 'default' | 'oneColumn'
35
+
36
+ type OwnerState = {
37
+ zoomed: boolean
38
+ disableZoom: boolean
39
+ sticky: boolean
40
+ variantMd: SidebarGalleryVariant
41
+ }
35
42
  const name = 'SidebarGallery' as const
36
43
  const parts = [
37
44
  'row',
@@ -60,6 +67,10 @@ export type SidebarGalleryProps = {
60
67
  routeHash?: string
61
68
  sx?: SxProps<Theme>
62
69
  disableZoom?: boolean
70
+ disableSticky?: boolean
71
+ variantMd?: SidebarGalleryVariant
72
+ beforeScroller?: React.ReactNode
73
+ afterScroller?: React.ReactNode
63
74
  } & Pick<ScrollerButtonProps, 'showButtons'>
64
75
 
65
76
  export function SidebarGallery(props: SidebarGalleryProps) {
@@ -71,6 +82,10 @@ export function SidebarGallery(props: SidebarGalleryProps) {
71
82
  routeHash = 'gallery',
72
83
  showButtons,
73
84
  disableZoom = false,
85
+ disableSticky = false,
86
+ variantMd = 'default',
87
+ beforeScroller,
88
+ afterScroller,
74
89
  } = props
75
90
 
76
91
  const router = useRouter()
@@ -103,7 +118,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
103
118
  }
104
119
  }
105
120
 
106
- const classes = withState({ zoomed, disableZoom })
121
+ const classes = withState({ zoomed, disableZoom, sticky: !disableSticky, variantMd })
107
122
  const theme = useTheme()
108
123
  const windowRef = useRef(typeof window !== 'undefined' ? window : null)
109
124
 
@@ -144,25 +159,31 @@ export function SidebarGallery(props: SidebarGalleryProps) {
144
159
  display: 'grid',
145
160
  gridTemplate: '"left" "right"',
146
161
  [theme.breakpoints.up('md')]: {
147
- gridTemplate: `"left right" / 1fr calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
148
- theme.page.horizontal
149
- } * 2)`,
162
+ '&:not(.variantMdOneColumn)': {
163
+ gridTemplate: `"left right" / 1fr calc(${responsiveVal(300, 500, undefined, theme.breakpoints.values.lg)} + ${
164
+ theme.page.horizontal
165
+ } * 2)`,
166
+ },
150
167
  },
151
168
  background:
152
169
  theme.palette.mode === 'light'
153
170
  ? theme.palette.background.image
154
171
  : theme.palette.background.paper,
155
- paddingRight: `calc((100% - ${theme.breakpoints.values.lg}px) / 2)`,
156
- },
157
- zoomed && {
158
- position: 'relative',
159
- zIndex: theme.zIndex.modal,
160
- marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
161
- [theme.breakpoints.up('md')]: {
162
- marginTop: `calc(${theme.appShell.headerHeightMd} * -1 - ${theme.spacings.lg})`,
163
- gridTemplateColumns: '1fr auto',
172
+
173
+ '&:not(.variantMdOneColumn)': {
174
+ paddingRight: `calc((100% - ${theme.breakpoints.values.lg}px) / 2)`,
175
+ },
176
+
177
+ '&.zoomed': {
178
+ position: 'relative',
179
+ zIndex: theme.zIndex.modal,
180
+ marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
181
+ [theme.breakpoints.up('md')]: {
182
+ marginTop: `calc(${theme.appShell.headerHeightMd} * -1 - ${theme.spacings.lg})`,
183
+ gridTemplateColumns: '1fr auto',
184
+ },
185
+ paddingRight: 0,
164
186
  },
165
- paddingRight: 0,
166
187
  },
167
188
  ]}
168
189
  >
@@ -183,11 +204,13 @@ export function SidebarGallery(props: SidebarGalleryProps) {
183
204
  width: '100%',
184
205
  },
185
206
  [theme.breakpoints.up('md')]: {
186
- height: `calc(${dvh(100)} - ${theme.appShell.headerHeightMd} - ${
187
- theme.spacings.lg
188
- })`,
189
- position: 'sticky',
190
- top: theme.appShell.headerHeightMd,
207
+ '&:not(.variantMdOneColumn)': {
208
+ height: `max(${dvh(90)}, 40vw)`,
209
+ '&.sticky': {
210
+ position: 'sticky',
211
+ top: theme.appShell.headerHeightMd,
212
+ },
213
+ },
191
214
  },
192
215
  },
193
216
  zoomed && {
@@ -201,6 +224,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
201
224
  if (!zoomed) document.body.style.overflow = ''
202
225
  }}
203
226
  >
227
+ {beforeScroller}
204
228
  <Scroller
205
229
  className={classes.scroller}
206
230
  hideScrollbar
@@ -242,6 +266,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
242
266
  />
243
267
  ))}
244
268
  </Scroller>
269
+ {afterScroller}
245
270
  <MotionBox
246
271
  layout='position'
247
272
  layoutDependency={zoomed}
@@ -333,9 +358,9 @@ export function SidebarGallery(props: SidebarGalleryProps) {
333
358
  >
334
359
  {import.meta.graphCommerce.sidebarGallery?.paginationVariant ===
335
360
  'THUMBNAILS_BOTTOM' ? (
336
- <ScrollerThumbnails images={images} />
361
+ <ScrollerThumbnails layoutDependency={zoomed} images={images} />
337
362
  ) : (
338
- <ScrollerDots />
363
+ <ScrollerDots layout />
339
364
  )}
340
365
  </Box>
341
366
  </MotionBox>
@@ -353,10 +378,10 @@ export function SidebarGallery(props: SidebarGalleryProps) {
353
378
  },
354
379
  zoomed && {
355
380
  [theme.breakpoints.up('md')]: {
356
- marginLeft: `calc((${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
381
+ marginLeft: `calc((${responsiveVal(300, 500, undefined, theme.breakpoints.values.lg)} + ${
357
382
  theme.page.horizontal
358
383
  } * 2) * -1)`,
359
- left: `calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
384
+ left: `calc(${responsiveVal(300, 500, undefined, theme.breakpoints.values.lg)} + ${
360
385
  theme.page.horizontal
361
386
  } * 2)`,
362
387
  },
@@ -370,9 +395,11 @@ export function SidebarGallery(props: SidebarGalleryProps) {
370
395
  sx={{
371
396
  boxSizing: 'border-box',
372
397
  width: '100%',
373
- padding: `${theme.spacings.lg} ${theme.page.horizontal}`,
374
- [theme.breakpoints.up('md')]: {
375
- paddingLeft: theme.spacings.lg,
398
+ '&:not(.variantMdOneColumn)': {
399
+ padding: `${theme.spacings.lg} ${theme.page.horizontal}`,
400
+ [theme.breakpoints.up('md')]: {
401
+ paddingLeft: theme.spacings.lg,
402
+ },
376
403
  },
377
404
  }}
378
405
  >
@@ -36,6 +36,7 @@ export function FullPageMessage(props: FullPageMessageProps) {
36
36
  }),
37
37
  !disableMargin && {
38
38
  marginTop: responsiveVal(50, 250),
39
+ marginBottom: responsiveVal(50, 250),
39
40
  },
40
41
  ...(Array.isArray(sx) ? sx : [sx]),
41
42
  ]}
@@ -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,20 @@
1
+ import { useMemo } from 'react'
2
+ import { useLocale } from '../../hooks/useLocale'
3
+ import { useMemoObject } from '../../hooks/useMemoObject'
4
+ import { DateValue, toDate } from './toDate'
5
+
6
+ export function useDateTimeFormatter(props: Intl.DateTimeFormatOptions) {
7
+ const locale = useLocale()
8
+ const memoOptions = useMemoObject(props)
9
+ return useMemo(() => new Intl.DateTimeFormat(locale, memoOptions), [locale, memoOptions])
10
+ }
11
+
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
+ const dateValue = useMemo(() => toDate(children), [children])
19
+ return <span suppressHydrationWarning>{dateValue ? formatter.format(dateValue) : null}</span>
20
+ }
@@ -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,16 @@
1
+ export type DateValue = Date | string | number | null | undefined
2
+
3
+ export function toDate(value: DateValue): Date | undefined {
4
+ let date: Date | undefined
5
+
6
+ if (value instanceof Date) {
7
+ date = value
8
+ } else if (typeof value === 'string') {
9
+ date = new Date(value.replace(/-/g, '/'))
10
+ } else if (typeof value === 'number') {
11
+ date = new Date(value)
12
+ }
13
+
14
+ if (date && Number.isNaN(date.getTime())) return undefined
15
+ return date
16
+ }
@@ -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,33 @@
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, index) => (
26
+ // eslint-disable-next-line react/no-array-index-key
27
+ <React.Fragment key={index}>
28
+ {part.type === 'literal' ? part.value : childArray[part.value]}
29
+ </React.Fragment>
30
+ ))}
31
+ </>
32
+ )
33
+ }
@@ -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'