@graphcommerce/next-ui 9.0.0-canary.97 → 9.0.0-canary.99

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Change Log
2
2
 
3
+ ## 9.0.0-canary.99
4
+
5
+ ## 9.0.0-canary.98
6
+
7
+ ### Patch Changes
8
+
9
+ - [#2414](https://github.com/graphcommerce-org/graphcommerce/pull/2414) [`cd8e35b`](https://github.com/graphcommerce-org/graphcommerce/commit/cd8e35ba3dfafd34221b0875bf4f83c802955d66) - Added RelativeTimeFormat and RelativeToTimeFormat components to display relative dates in all locales. ([@Vignesh-M21](https://github.com/Vignesh-M21))
10
+
11
+ - [#2414](https://github.com/graphcommerce-org/graphcommerce/pull/2414) [`8b1a5a6`](https://github.com/graphcommerce-org/graphcommerce/commit/8b1a5a62a580cc8d08746cb19c4e5a4f52bd270c) - Date strings (12-12-2012) are not supported by older Safari browser versions. must be converted (12/12/2012) in order for it to function; otherwise, it will return NaN if we attempt to access the getTime() on an object. ([@Vignesh-M21](https://github.com/Vignesh-M21))
12
+
3
13
  ## 9.0.0-canary.97
4
14
 
5
15
  ## 9.0.0-canary.96
@@ -1,6 +1,7 @@
1
1
  import { useMemo } from 'react'
2
2
  import { useLocale } from '../../hooks/useLocale'
3
3
  import { useMemoObject } from '../../hooks/useMemoObject'
4
+ import { DateValue, toDate } from './toDate'
4
5
 
5
6
  export function useDateTimeFormatter(props: Intl.DateTimeFormatOptions) {
6
7
  const locale = useLocale()
@@ -8,18 +9,12 @@ export function useDateTimeFormatter(props: Intl.DateTimeFormatOptions) {
8
9
  return useMemo(() => new Intl.DateTimeFormat(locale, memoOptions), [locale, memoOptions])
9
10
  }
10
11
 
11
- type DateValue = Date | string | number | null | undefined
12
12
  export type DateTimeFormatPropsType = { children: DateValue } & Intl.DateTimeFormatOptions
13
13
 
14
14
  export function DateTimeFormat(props: DateTimeFormatPropsType) {
15
15
  const { children } = props
16
16
  const formatter = useDateTimeFormatter({ dateStyle: 'medium', timeStyle: 'short', ...props })
17
17
 
18
- return (
19
- <span suppressHydrationWarning>
20
- {children
21
- ? formatter.format(typeof children === 'string' ? new Date(children) : children)
22
- : null}
23
- </span>
24
- )
18
+ const dateValue = useMemo(() => toDate(children), [children])
19
+ return <span suppressHydrationWarning>{dateValue ? formatter.format(dateValue) : null}</span>
25
20
  }
@@ -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,34 @@
1
+ import { forwardRef, useMemo } from 'react'
2
+ import { useLocale } from '../../hooks/useLocale'
3
+ import { useMemoObject } from '../../hooks/useMemoObject'
4
+ import { relativeTimeFormatUnitAuto } from './relativeTimeFormatAutoUnit'
5
+
6
+ export function useRelativeTimeFormatter(props: Intl.RelativeTimeFormatOptions) {
7
+ const locale = useLocale()
8
+ const memoOptions = useMemoObject(props)
9
+ return useMemo(() => new Intl.RelativeTimeFormat(locale, memoOptions), [locale, memoOptions])
10
+ }
11
+
12
+ export type RelativeTimeFormatProps = {
13
+ children: number
14
+ unit?: Intl.RelativeTimeFormatUnit
15
+ styleFormat?: Intl.RelativeTimeFormatStyle
16
+ } & Omit<Intl.RelativeTimeFormatOptions, 'style'>
17
+
18
+ /**
19
+ * Alternative: {@link file://./RelativeToTimeFormat.tsx}
20
+ */
21
+ export const RelativeTimeFormat = forwardRef<HTMLSpanElement, RelativeTimeFormatProps>(
22
+ (props, ref) => {
23
+ const { children, unit, styleFormat, localeMatcher, numeric, ...rest } = props
24
+ const formatter = useRelativeTimeFormatter({ localeMatcher, numeric, style: styleFormat })
25
+
26
+ const [value, autoUnit] = relativeTimeFormatUnitAuto({ value: children, unit })
27
+
28
+ return (
29
+ <span suppressHydrationWarning ref={ref} {...rest}>
30
+ {children ? formatter.format(value, autoUnit) : null}
31
+ </span>
32
+ )
33
+ },
34
+ )
@@ -0,0 +1,35 @@
1
+ import { forwardRef, useMemo } from 'react'
2
+ import { DateValue, toDate } from '../DateTimeFormat/toDate'
3
+ import { RelativeTimeFormat, RelativeTimeFormatProps } from './RelativeTimeFormat'
4
+
5
+ type RelativeToTimeFormatProps = Omit<RelativeTimeFormatProps, 'children'> & {
6
+ /**
7
+ * Date to format a relative value for.
8
+ */
9
+ children: DateValue
10
+ /**
11
+ * If provided, the component will format a relative value to this date.
12
+ * Else, it will format a relative value to the current date.
13
+ */
14
+ to?: DateValue
15
+ }
16
+
17
+ export const RelativeToTimeFormat = forwardRef<HTMLSpanElement, RelativeToTimeFormatProps>(
18
+ (props, ref) => {
19
+ const { children, to, ...rest } = props
20
+
21
+ const relativeTo = useMemo(() => {
22
+ const date = toDate(children)
23
+ if (!date) return 0
24
+ const toDateValue = (to && toDate(to)) || new Date()
25
+
26
+ return Math.round((date.getTime() - toDateValue.getTime()) / 1000)
27
+ }, [children, to])
28
+
29
+ return (
30
+ <RelativeTimeFormat {...rest} ref={ref}>
31
+ {relativeTo}
32
+ </RelativeTimeFormat>
33
+ )
34
+ },
35
+ )
@@ -0,0 +1,2 @@
1
+ export * from './RelativeTimeFormat'
2
+ export * from './RelativeToTimeFormat'
@@ -0,0 +1,23 @@
1
+ type UseRelativeTimeFormatUnitAutoProps = {
2
+ value: number
3
+ unit?: Intl.RelativeTimeFormatUnit
4
+ }
5
+
6
+ export function relativeTimeFormatUnitAuto(
7
+ props: UseRelativeTimeFormatUnitAutoProps,
8
+ ): [number, Intl.RelativeTimeFormatUnit] {
9
+ const { value, unit } = props
10
+
11
+ if (unit) return [value, unit]
12
+
13
+ // Calculate the absolute value once
14
+ const absValue = Math.abs(value)
15
+
16
+ if (absValue >= 60 * 60 * 24 * 365) return [Math.round(value / (60 * 60 * 24 * 365)), 'year']
17
+ if (absValue >= 60 * 60 * 24 * 30) return [Math.round(value / (60 * 60 * 24 * 30)), 'month']
18
+ if (absValue >= 60 * 60 * 24 * 7) return [Math.round(value / (60 * 60 * 24 * 7)), 'week']
19
+ if (absValue >= 60 * 60 * 24) return [Math.round(value / (60 * 60 * 24)), 'day']
20
+ if (absValue >= 60 * 60) return [Math.round(value / (60 * 60)), 'hour']
21
+ if (absValue >= 60) return [Math.round(value / 60), 'minute']
22
+ return [Math.round(value), 'second']
23
+ }
package/Intl/index.ts CHANGED
@@ -7,5 +7,5 @@ export * from './ListFormat' // Intl.ListFormat
7
7
  // export * from './Locale' // Intl.Locale
8
8
  export * from './NumberFormat' // Intl.NumberFormat
9
9
  // export * from './PluralRules' // Intl.PluralRules
10
- // export * from './RelativeTimeFormat' // Intl.RelativeTimeFormat
10
+ export * from './RelativeTimeFormat' // Intl.RelativeTimeFormat
11
11
  // export * from './Segmenter' // Intl.Segmenter
@@ -8,6 +8,9 @@ export type TimeAgoProps = {
8
8
  locale?: string
9
9
  }
10
10
 
11
+ /**
12
+ * @deprecated Use <RelativeToTimeFormat /> instead.
13
+ */
11
14
  export function TimeAgo(props: TimeAgoProps) {
12
15
  const { date } = props
13
16
  const msPerMinute = 60 * 1000
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": "9.0.0-canary.97",
5
+ "version": "9.0.0-canary.99",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -26,13 +26,13 @@
26
26
  "typescript": "5.5.3"
27
27
  },
28
28
  "peerDependencies": {
29
- "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.97",
30
- "@graphcommerce/framer-next-pages": "^9.0.0-canary.97",
31
- "@graphcommerce/framer-scroller": "^9.0.0-canary.97",
32
- "@graphcommerce/framer-utils": "^9.0.0-canary.97",
33
- "@graphcommerce/image": "^9.0.0-canary.97",
34
- "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.97",
35
- "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.97",
29
+ "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.99",
30
+ "@graphcommerce/framer-next-pages": "^9.0.0-canary.99",
31
+ "@graphcommerce/framer-scroller": "^9.0.0-canary.99",
32
+ "@graphcommerce/framer-utils": "^9.0.0-canary.99",
33
+ "@graphcommerce/image": "^9.0.0-canary.99",
34
+ "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.99",
35
+ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.99",
36
36
  "@lingui/core": "^4.2.1",
37
37
  "@lingui/macro": "^4.2.1",
38
38
  "@lingui/react": "^4.2.1",