diff --git a/README.md b/README.md index 59ff70c..5c399f7 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,13 @@ Formats a timestamp as a localized string or as relative text that auto-updates This allows the server to cache HTML fragments containing dates and lets the browser choose how to localize the displayed time according to the user's preferences. For example, the server may have cached the following generated markup: ```html - - April 1, 2014 4:30pm - + April 1, 2014 4:30pm ``` Every visitor is served the same markup from the server's cache. When it reaches the browser, the custom `relative-time` JavaScript localizes the element's text into the local timezone and formatting. ```html - - 1 Apr 2014 21:30 - + 1 Apr 2014 21:30 ``` Dates are displayed before months, and a 24-hour clock is used, according to the user's browser settings. @@ -37,9 +33,7 @@ This element uses the `Intl.DateTimeFormat` & `Intl.RelativeTimeFormat` APIs, wh Add a `` element to your markup. Provide a default formatted date as the element's text content (e.g. April 1, 2014). It also MUST have a `datetime` attribute set to an ISO 8601 formatted timestamp. ```html - - April 1, 2014 - + April 1, 2014 ``` Depending on how far in the future this is being viewed, the element's text will be replaced with one of the following formats: @@ -62,34 +56,34 @@ So, a relative date phrase is used for up to a month and then the actual date is #### Attributes -| Property Name | Attribute Name | Possible Values | Default Value | -|:---------------|:-----------------|:--------------------------------------------------------------------------------------------|:---------------------------------| -| `datetime` | `datetime` | `string` | - | -| `format` | `format` | `'datetime'\|'relative'\|'duration'` | `'auto'` | -| `date` | - | `Date \| null` | - | -| `tense` | `tense` | `'auto'\|'past'\|'future'` | `'auto'` | -| `precision` | `precision` | `'year'\|'month'\|'day'\|'hour'\|'minute'\|'second'` | `'second'` | -| `threshold` | `threshold` | `string` | `'P30D'` | -| `prefix` | `prefix` | `string` | `'on'` | -| `formatStyle` | `format-style` | `'long'\|'short'\|'narrow'` | * | -| `second` | `second` | `'numeric'\|'2-digit'\|undefined` | `undefined` | -| `minute` | `minute` | `'numeric'\|'2-digit'\|undefined` | `undefined` | -| `hour` | `hour` | `'numeric'\|'2-digit'\|undefined` | `undefined` | -| `weekday` | `weekday` | `'short'\|'long'\|'narrow'\|undefined` | ** | -| `day` | `day` | `'numeric'\|'2-digit'\|undefined` | `'numeric'` | -| `month` | `month` | `'numeric'\|'2-digit'\|'short'\|'long'\|'narrow'\|undefined` | *** | -| `year` | `year` | `'numeric'\|'2-digit'\|undefined` | **** | -| `timeZoneName` | `time-zone-name` | `'long'\|'short'\|'shortOffset'\|'longOffset'` `\|'shortGeneric'\|'longGeneric'\|undefined` | `undefined` | -| `timeZone` | `time-zone` | `string\|undefined` | Browser default time zone | -| `noTitle` | `no-title` | `-` | `-` | - -*: If unspecified, `formatStyle` will return `'narrow'` if `format` is `'elapsed'` or `'micro'`, `'short'` if the format is `'relative'` or `'datetime'`, otherwise it will be `'long'`. - -**: If unspecified, `month` will return the same value as `formatStyle` whenever `format` is `'datetime'`, otherwise it will be `'short'`. - -***: If unspecified, `weekday` will return the same value as `formatStyle` whenever `format` is `'datetime'`, otherwise it will be `undefined`. - -****: If unspecified, `year` will return `'numeric'` if `datetime` represents the same year as the current year. It will return `undefined` if unspecified and if `datetime` represents a different year to the current year. +| Property Name | Attribute Name | Possible Values | Default Value | +| :------------- | :--------------- | :------------------------------------------------------------------------------------------ | :------------------------ | +| `datetime` | `datetime` | `string` | - | +| `format` | `format` | `'datetime'\|'relative'\|'duration'` | `'auto'` | +| `date` | - | `Date \| null` | - | +| `tense` | `tense` | `'auto'\|'past'\|'future'` | `'auto'` | +| `precision` | `precision` | `'year'\|'month'\|'day'\|'hour'\|'minute'\|'second'` | `'second'` | +| `threshold` | `threshold` | `string` | `'P30D'` | +| `prefix` | `prefix` | `string` | `'on'` | +| `formatStyle` | `format-style` | `'long'\|'short'\|'narrow'` | \* | +| `second` | `second` | `'numeric'\|'2-digit'\|undefined` | `undefined` | +| `minute` | `minute` | `'numeric'\|'2-digit'\|undefined` | `undefined` | +| `hour` | `hour` | `'numeric'\|'2-digit'\|undefined` | `undefined` | +| `weekday` | `weekday` | `'short'\|'long'\|'narrow'\|undefined` | \*\* | +| `day` | `day` | `'numeric'\|'2-digit'\|undefined` | `'numeric'` | +| `month` | `month` | `'numeric'\|'2-digit'\|'short'\|'long'\|'narrow'\|undefined` | \*\*\* | +| `year` | `year` | `'numeric'\|'2-digit'\|undefined` | \*\*\*\* | +| `timeZoneName` | `time-zone-name` | `'long'\|'short'\|'shortOffset'\|'longOffset'` `\|'shortGeneric'\|'longGeneric'\|undefined` | `undefined` | +| `timeZone` | `time-zone` | `string\|undefined` | Browser default time zone | +| `noTitle` | `no-title` | `-` | `-` | + +\*: If unspecified, `formatStyle` will return `'narrow'` if `format` is `'elapsed'` or `'micro'`, `'short'` if the format is `'relative'` or `'datetime'`, otherwise it will be `'long'`. + +\*\*: If unspecified, `month` will return the same value as `formatStyle` whenever `format` is `'datetime'`, otherwise it will be `'short'`. + +\*\*\*: If unspecified, `weekday` will return the same value as `formatStyle` whenever `format` is `'datetime'`, otherwise it will be `undefined`. + +\*\*\*\*: If unspecified, `year` will return `'numeric'` if `datetime` represents the same year as the current year. It will return `undefined` if unspecified and if `datetime` represents a different year to the current year. ##### datetime (`string`) @@ -97,13 +91,14 @@ This is the datetime that the element is meant to represent. This must be a vali ```html - April 1, 2038 + April 1, 2038 + ``` @@ -119,18 +114,18 @@ The `datetime` format will display a localised datetime, based on the other prop Unless specified, it will consider `weekday` to be `'long'`, `month` to be `'long'`, and `'year'` to be `numeric` if the `datetime` is the same as the given year. Overriding `formatStyle` will change both `weekday` and `month` default values. Examples of this format with the default options and an `en` locale: - - `Wed, 26 Aug 2021` - - `Sat, 31 Dec` (assuming the `datetime` is same year as the current year) +- `Wed, 26 Aug 2021` +- `Sat, 31 Dec` (assuming the `datetime` is same year as the current year) ###### `format=relative` The default `relative` format will display dates relative to the current time (unless they are past the `threshold` value - see below). The values are rounded to display a single unit, for example if the time between the given `datetime` and the current wall clock time exceeds a day, then the format will _only_ output in days, and will not display hours, minutes or seconds. Some examples of this format with the default options and an `en` locale: - - `in 20 days` - - `20 days ago` - - `in 1 minute` - - `on 31 Aug` (assuming the current date is the same year as the current year, and is more than 30 days away from 31 Aug) - - `on 26 Aug 2021` (assuming the current date is more than 30 days away from 26 Aug 2021) +- `in 20 days` +- `20 days ago` +- `in 1 minute` +- `on 31 Aug` (assuming the current date is the same year as the current year, and is more than 30 days away from 31 Aug) +- `on 26 Aug 2021` (assuming the current date is more than 30 days away from 26 Aug 2021) ###### `format=duration` @@ -142,9 +137,10 @@ The `duration` format will display the time remaining (or elapsed time) from the ##### time-zone (`string`) -The`time-zone` attribute allows you to specify the IANA time zone name (e.g., `America/New_York`, `Europe/London`) used for formatting the date and time. +The`time-zone` attribute allows you to specify the IANA time zone name (e.g., `America/New_York`, `Europe/London`) used for formatting the date and time. You can set the time zone either as an attribute or property: + ```html June 1, 2024 8:00am EDT @@ -157,7 +153,7 @@ If the individual element does not have a `time-zone` attribute then it will tra ###### `format=elapsed` -This is similar to the `format=duration`, except the `formatStyle` defaults to `narrow`. Code that uses `format=elapsed` should migrate to `format=duration formatStyle=narrow`, as it will be removed in a later version. +This is similar to the `format=duration`, except the `formatStyle` defaults to `narrow`. Code that uses `format=elapsed` should migrate to `format=duration formatStyle=narrow`, as it will be removed in a later version. ###### `format=auto` @@ -171,13 +167,13 @@ Code that uses `format=micro` should consider migrating to `format=relative` (pe ###### Cheatsheet -| `format=datetime` | `format=relative` | `format=duration` | `format=micro` | `format=elapsed` | -|:-----------------:|:-----------------:|:------------------------------------------------:|:-----------------:|:----------------:| -| Wed 26 May 2024 | in 2 years | 2 years, 10 days, 3 hours, 20 minutes, 8 seconds | 2y | 2y 10d 3h 20m 8s | -| Wed 26 Aug 2021 | 2 years ago | 2 years, 10 days, 3 hours, 8 seconds | 2y | 2y 10d 3h 8s | -| Jan 15 2023 | in 30 days | 30 days, 4 hours, 20 minutes, 8 seconds | 30d | 30d 4h 20m 8s | -| Dec 15 2022 | 21 minutes ago | 21 minutes, 30 seconds | 21m | 21m 30s | -| Dec 15 2022 | 37 seconds ago | 37 seconds | 1m | 37s | +| `format=datetime` | `format=relative` | `format=duration` | `format=micro` | `format=elapsed` | +| :---------------: | :---------------: | :----------------------------------------------: | :------------: | :--------------: | +| Wed 26 May 2024 | in 2 years | 2 years, 10 days, 3 hours, 20 minutes, 8 seconds | 2y | 2y 10d 3h 20m 8s | +| Wed 26 Aug 2021 | 2 years ago | 2 years, 10 days, 3 hours, 8 seconds | 2y | 2y 10d 3h 8s | +| Jan 15 2023 | in 30 days | 30 days, 4 hours, 20 minutes, 8 seconds | 30d | 30d 4h 20m 8s | +| Dec 15 2022 | 21 minutes ago | 21 minutes, 30 seconds | 21m | 21m 30s | +| Dec 15 2022 | 37 seconds ago | 37 seconds | 1m | 37s | ##### tense (`'auto'|'past'|'future'`, default: `auto`) @@ -187,21 +183,23 @@ Tense can be used to prevent `duration` or `relative` formatted dates displaying For example when the given `datetime` is 40 seconds behind of the current date: -| `tense=`| format=duration | format=relative | -|:-------:|:----------------:|:---------------:| -| future | 0s | now | -| past | 40s | 40s ago | -| auto | 40s | 40s ago | +| `tense=` | format=duration | format=relative | +| :------: | :-------------: | :-------------: | +| future | 0s | now | +| past | 40s | 40s ago | +| auto | 40s | 40s ago | ```html - April 1, 2038 + April 1, 2038 + ``` ```html - April 1, 2038 + April 1, 2038 + ``` @@ -211,24 +209,23 @@ If `format` is `datetime` then this value will be ignored. Precision can be used to limit the display of an `relative` or `duration` formatted time. By default times will display down to the `second` level of precision. Changing this value will truncate the display by zeroing out any unit lower than the given unit, as such units smaller than the given unit won't be displayed during `duration`, and `relative` will display `now` if the time away from the current time is less than the given precision unit. -| `precision=` | format=duration | -|:-------------:|:-------------------:| -| second | 2y 6m 10d 3h 20m 8s | -| minute | 2y 6m 10d 3h 20m | -| hour | 2y 6m 10d 3h | -| day | 2y 6m 10d | -| month | 2y 6m | -| year | 2y | - -| `precision=` | format=relative | -|:-------------:|:-------------------:| -| second | 25 seconds | -| minute | now | -| hour | now | -| day | now | -| month | now | -| year | now | - +| `precision=` | format=duration | +| :----------: | :-----------------: | +| second | 2y 6m 10d 3h 20m 8s | +| minute | 2y 6m 10d 3h 20m | +| hour | 2y 6m 10d 3h | +| day | 2y 6m 10d | +| month | 2y 6m | +| year | 2y | + +| `precision=` | format=relative | +| :----------: | :-------------: | +| second | 25 seconds | +| minute | now | +| hour | now | +| day | now | +| month | now | +| year | now | ##### threshold (`string`, default: `P30D`) @@ -266,14 +263,14 @@ When formatting an absolute date (see above `threshold` for more details) it can This will used to determine the length of the unit names. This value is passed to the `Intl` objects as the `style` option. Some examples of how this can be used: -| `format=` | `formatStyle=` | Display | -|:----------:|:--------------:|:------------------------:| -| relative | long | in 1 month | -| relative | short | in 1 mo. | -| relative | narrow | in 1 mo. | -| duration | long | 1 month, 2 days, 4 hours | -| duration | short | 1 mth, 2 days, 4 hr | -| duration | narrow | 1mo 2d 4h | +| `format=` | `formatStyle=` | Display | +| :-------: | :------------: | :----------------------: | +| relative | long | in 1 month | +| relative | short | in 1 mo. | +| relative | narrow | in 1 mo. | +| duration | long | 1 month, 2 days, 4 hours | +| duration | short | 1 mth, 2 days, 4 hr | +| duration | narrow | 1mo 2d 4h | ##### second, minute, hour, weekday, day, month, year, timeZoneName @@ -303,6 +300,23 @@ Browsers without native support for [`Intl.RelativeTimeFormat`][relativetimeform [datetimeformat]: https://caniuse.com/mdn-javascript_builtins_intl_datetimeformat_format [ce-polyfill]: https://github.com/webcomponents/custom-elements +## Usage with React + +You can use the `` element in a React project by importing the `RelativeTimeElement` class and declaring it under `React.JSX.IntrinsicElements`. For example: + +```ts +import {RelativeTimeElement} from '@github/relative-time-element' + +declare module 'react' { + namespace JSX { + interface IntrinsicElements { + 'relative-time': React.DetailedHTMLProps, RelativeTimeElement> & + Partial> + } + } +} +``` + ## See Also Most of this implementation is based on Basecamp's [local_time](https://github.com/basecamp/local_time) component. Thanks to @javan for open sourcing that work and allowing for others to build on top of it. diff --git a/src/relative-time-element-define.ts b/src/relative-time-element-define.ts index 34e5a59..989e434 100644 --- a/src/relative-time-element-define.ts +++ b/src/relative-time-element-define.ts @@ -12,21 +12,14 @@ try { } } -type JSXBase = JSX.IntrinsicElements extends {span: unknown} - ? JSX.IntrinsicElements - : Record> declare global { interface Window { RelativeTimeElement: typeof RelativeTimeElement } + interface HTMLElementTagNameMap { 'relative-time': RelativeTimeElement } - namespace JSX { - interface IntrinsicElements { - ['relative-time']: JSXBase['span'] & Partial> - } - } } export default RelativeTimeElement