kopia lustrzana https://github.com/shoelace-style/shoelace
Finish up sl-relative-time
rodzic
410b39ee59
commit
058e8fa08e
docs/components
src
components/relative-time
|
@ -2,49 +2,46 @@
|
|||
|
||||
[component-header:sl-relative-time]
|
||||
|
||||
Outputs a localized time phrase relative to the current time.
|
||||
Outputs a localized time phrase relative to the current date and time.
|
||||
|
||||
Localization is handled by the browser's [Intl.RelativeTimeFormat API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) so there'e no need to load language packs.
|
||||
Localization is handled by the browser's [`Intl.RelativeTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat). No language packs are required.
|
||||
|
||||
```html preview
|
||||
<sl-relative-time date="2011-11-11T16:56:20-04:00"></sl-relative-time><br>
|
||||
<!-- Shoelace 2 release date 🎉 -->
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00"></sl-relative-time><br>
|
||||
```
|
||||
|
||||
The `date` prop must be a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object or a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret. When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure proper parsing.
|
||||
The `date` prop determines when the date/time is calculated from. It must be a string that [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) can interpret or a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object set via JavaScript. When using strings, avoid ambiguous dates such as `03/04/2020` which can be interpreted as March 4 or April 3 depending on the user's browser and locale. Instead, always use a valid [ISO 8601 date time string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Date_Time_String_Format) to ensure the date will be parsed properly by all clients.
|
||||
|
||||
?> [The Intl.RelativeTimeFormat API is available in all major browsers](https://caniuse.com/mdn-javascript_builtins_intl_relativetimeformat), but it first became available to Safari in version 14. If you need to support Safari 13, you'll need to [use a polyfill](https://github.com/catamphetamine/relative-time-format).
|
||||
?> [The `Intl.RelativeTimeFormat` API is available in all major browsers](https://caniuse.com/mdn-javascript_builtins_intl_relativetimeformat), but it first became available to Safari in version 14. If you need to support Safari 13, you'll need to [use a polyfill](https://github.com/catamphetamine/relative-time-format).
|
||||
|
||||
## Examples
|
||||
|
||||
### Keeping Time in Sync
|
||||
|
||||
Use the `sync` attribute to update the displayed value as time passes.
|
||||
Use the `sync` attribute to update the displayed value automatically as time passes.
|
||||
|
||||
```html preview
|
||||
<div class="relative-time-sync">
|
||||
<sl-relative-time sync></sl-relative-time>
|
||||
<br><br>
|
||||
<sl-button>Reset</sl-button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.relative-time-sync');
|
||||
const button = container.querySelector('sl-button');
|
||||
const relativeTime = container.querySelector('sl-relative-time');
|
||||
|
||||
relativeTime.date = new Date();
|
||||
button.addEventListener('click', () => relativeTime.date = new Date());
|
||||
relativeTime.date = new Date(new Date().getTime() - 60000);
|
||||
</script>
|
||||
```
|
||||
|
||||
### Formatting Styles
|
||||
|
||||
You can change the way times are formatted with the `format` attribute. Note that some locales may show the same result for `narrow` and `short` formats.
|
||||
You can change how the time is displayed using the `format` attribute. Note that some locales may display the same values for `narrow` and `short` formats.
|
||||
|
||||
```html preview
|
||||
<sl-relative-time date="2020-07-15T09:17:00" format="narrow"></sl-relative-time><br>
|
||||
<sl-relative-time date="2020-07-15T09:17:00" format="short"></sl-relative-time><br>
|
||||
<sl-relative-time date="2020-07-15T09:17:00" format="long"></sl-relative-time>
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="narrow"></sl-relative-time><br>
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="short"></sl-relative-time><br>
|
||||
<sl-relative-time date="2020-07-15T09:17:00-04:00" format="long"></sl-relative-time>
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
@ -52,10 +49,11 @@ You can change the way times are formatted with the `format` attribute. Note tha
|
|||
Use the `locale` attribute to set the desired locale.
|
||||
|
||||
```html preview
|
||||
English: <sl-relative-time date="2020-07-15T09:17:00" locale="en-US"></sl-relative-time><br>
|
||||
Chinese: <sl-relative-time date="2020-07-15T09:17:00" locale="zh-CN"></sl-relative-time><br>
|
||||
German: <sl-relative-time date="2020-07-15T09:17:00" locale="de-DE"></sl-relative-time><br>
|
||||
Russian: <sl-relative-time date="2020-07-15T09:17:00" locale="ru-RU"></sl-relative-time>
|
||||
English: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="en-US"></sl-relative-time><br>
|
||||
Chinese: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="zh-CN"></sl-relative-time><br>
|
||||
German: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="de"></sl-relative-time><br>
|
||||
Greek: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="el"></sl-relative-time><br>
|
||||
Russian: <sl-relative-time date="2020-07-15T09:17:00-04:00" locale="ru"></sl-relative-time>
|
||||
```
|
||||
|
||||
[component-metadata:sl-relative-time]
|
||||
|
|
|
@ -914,6 +914,10 @@ export namespace Components {
|
|||
* When `auto`, values such as "yesterday" and "tomorrow" will be shown when possible. When `always`, values such as "1 day ago" and "in 1 day" will be shown.
|
||||
*/
|
||||
"numeric": 'always' | 'auto';
|
||||
/**
|
||||
* Keep the displayed value up to date as time passes.
|
||||
*/
|
||||
"sync": boolean;
|
||||
}
|
||||
interface SlResizeObserver {
|
||||
}
|
||||
|
@ -2519,6 +2523,10 @@ declare namespace LocalJSX {
|
|||
* When `auto`, values such as "yesterday" and "tomorrow" will be shown when possible. When `always`, values such as "1 day ago" and "in 1 day" will be shown.
|
||||
*/
|
||||
"numeric"?: 'always' | 'auto';
|
||||
/**
|
||||
* Keep the displayed value up to date as time passes.
|
||||
*/
|
||||
"sync"?: boolean;
|
||||
}
|
||||
interface SlResizeObserver {
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, Prop, State, h } from '@stencil/core';
|
||||
import { Component, Prop, State, Watch, h } from '@stencil/core';
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
|
@ -10,7 +10,11 @@ import { Component, Prop, State, h } from '@stencil/core';
|
|||
shadow: true
|
||||
})
|
||||
export class RelativeTime {
|
||||
@State() displayTime = '';
|
||||
updateTimeout: any;
|
||||
|
||||
@State() isoTime = '';
|
||||
@State() relativeTime = '';
|
||||
@State() titleTime = '';
|
||||
|
||||
/** The date from which to calculate time from. */
|
||||
@Prop() date: Date | string;
|
||||
|
@ -27,19 +31,96 @@ export class RelativeTime {
|
|||
*/
|
||||
@Prop() numeric: 'always' | 'auto' = 'auto';
|
||||
|
||||
render() {
|
||||
/** Keep the displayed value up to date as time passes. */
|
||||
@Prop() sync = false;
|
||||
|
||||
connectedCallback() {
|
||||
this.updateTime();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
clearTimeout(this.updateTimeout);
|
||||
}
|
||||
|
||||
@Watch('date')
|
||||
@Watch('locale')
|
||||
@Watch('format')
|
||||
@Watch('numeric')
|
||||
@Watch('sync')
|
||||
updateTime() {
|
||||
const now = new Date();
|
||||
const date = new Date(this.date);
|
||||
|
||||
if (isNaN(date.getSeconds())) {
|
||||
return '';
|
||||
// Check for an invalid date
|
||||
if (isNaN(date.getMilliseconds())) {
|
||||
this.relativeTime = '';
|
||||
this.isoTime = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// // @ts-ignore - https://caniuse.com/mdn-javascript_builtins_intl_relativetimeformat
|
||||
// new Intl.RelativeTimeFormat(this.locale, {
|
||||
// numeric: this.numeric,
|
||||
// style: this.type
|
||||
// }).format(this.value, this.unit);
|
||||
const diff = +date - +now;
|
||||
const availableUnits = [
|
||||
{ max: 2760000, value: 60000, unit: 'minute' }, // max 46 minutes
|
||||
{ max: 72000000, value: 3600000, unit: 'hour' }, // max 20 hours
|
||||
{ max: 518400000, value: 86400000, unit: 'day' }, // max 6 days
|
||||
{ max: 2419200000, value: 604800000, unit: 'week' }, // max 28 days
|
||||
{ max: 28512000000, value: 2592000000, unit: 'month' }, // max 11 months
|
||||
{ max: Infinity, value: 31536000000, unit: 'year' }
|
||||
];
|
||||
const { unit, value } = availableUnits.find(unit => Math.abs(diff) < unit.max);
|
||||
|
||||
return <time dateTime={date.toISOString()}>{this.displayTime}</time>;
|
||||
this.isoTime = date.toISOString();
|
||||
this.titleTime = new Intl.DateTimeFormat(this.locale, {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
timeZoneName: 'short'
|
||||
}).format(date);
|
||||
|
||||
// @ts-ignore - https://github.com/microsoft/TypeScript/issues/29129
|
||||
this.relativeTime = new Intl.RelativeTimeFormat(this.locale, {
|
||||
numeric: this.numeric,
|
||||
style: this.format
|
||||
}).format(Math.round(diff / value), unit);
|
||||
|
||||
// If sync is enabled, update as time passes
|
||||
clearTimeout(this.updateTimeout);
|
||||
if (this.sync) {
|
||||
// Calculates the number of milliseconds until the next respective unit changes. This ensures that all components
|
||||
// update at the same time which is less distracting than updating independently.
|
||||
const getTimeUntilNextUnit = (unit: 'second' | 'minute' | 'hour' | 'day') => {
|
||||
const units = { second: 1000, minute: 60000, hour: 3600000, day: 86400000 };
|
||||
const value = units[unit];
|
||||
return value - (now.getTime() % value);
|
||||
};
|
||||
|
||||
let nextInterval: number;
|
||||
|
||||
// NOTE: this could be optimized to determine when the next update should actually occur, but the size and cost of
|
||||
// that logic probably isn't worth the performance benefit
|
||||
if (unit === 'minute') {
|
||||
nextInterval = getTimeUntilNextUnit('second');
|
||||
} else if (unit === 'hour') {
|
||||
nextInterval = getTimeUntilNextUnit('minute');
|
||||
} else if (unit === 'day') {
|
||||
nextInterval = getTimeUntilNextUnit('hour');
|
||||
} else {
|
||||
// Cap updates at once per day. It's unlikely a user will reach this value, plus setTimeout has a limit on the
|
||||
// value it can accept. https://stackoverflow.com/a/3468650/567486
|
||||
nextInterval = getTimeUntilNextUnit('day'); // next day
|
||||
}
|
||||
|
||||
this.updateTimeout = setTimeout(() => this.updateTime(), nextInterval);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<time dateTime={this.isoTime} title={this.titleTime}>
|
||||
{this.relativeTime}
|
||||
</time>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue