GrDuration
Берут, когда сколько заняло.
Обзор
Когда брать
- сколько заняло — длительность звонка, время сборки, простой оборудования: «2 ч 30 мин» читается быстрее, чем «9000»;
- сколько идёт прямо сейчас — передайте момент начала одной датой, и метка станет живой: таймер общий на всё приложение и снимается на скрытой вкладке;
- промежуток между двумя моментами — пара дат считается сама, вычитать миллисекунды руками не нужно;
- значение уезжает в разметку —
datetimeзаполняется точной длиной в ISO 8601, даже когда текст сокращён до двух единиц.
Когда взять другое
| Нужно | Берите |
|---|---|
| Показать, давно ли это было | GrRelativeTime |
| Выбрать момент, а не показать длину | GrDatePicker / GrTimePicker |
| Ввести длительность, а не показать | GrNumberInput |
| Показать долю прошедшего от целого | GrProgressBar |
Примеры 3 у каждого — живое демо и его код
Сколько шла сборка
Каждый запуск — пара
Date, как её отдаёт API CI: измеренный промежуток, вычитать миллисекунды руками не нужно.max-units="2"сwidth="narrow"дают «23 мин 29 с», а остаток отбрасывается, а не округляется вверх. Слот по умолчанию сохраняет текст и рисует полосу против самой долгой сборки, а<time>вокруг по-прежнему несёт точную длину в ISO 8601.billing-service · pipeline runsToday · the budget for main is 15 minutes - #4182 · mainpasseda96c453
- #4181 · feat/sepa-instantfailed13b92a7
- #4180 · mainpassed3a71377
- #4179 · fix/dunning-localepassedca5d8d1
Звонок, который ещё идёт
Одна дата — момент начала звонка: с
liveметка сама считает до «сейчас» на общем таймере и тикает раз в секунду только потому, что показаны секунды. Завершённый звонок передаёт пару дат — тот же компонент, фиксированная длина. История ниже — числа в секундах, загрублённыеsmallest-unit="minute".AROn call · Ana RibeiroRefund for INV-0419 · +351 912 004 118Earlier todayAverage- Ben HartleyVAT number on invoices
- Chloé MoreauCard declined on renewal
- Diego GarcíaSeats after a downgrade
Фазы инцидента
Инцидент длиннее суток те, кто его пережил, всё равно считают в часах:
largest-unit="hour"оставляет «27 ч 15 мин» вместо «1 д 3 ч». Уведомление клиентам — проза, поэтому единицы называются полностью черезwidth="long", а каждая страница статуса, английская и немецкая, называет свой язык черезlocale, на каком бы языке ни была админка вокруг. Там дата начала меряется отbase, а не от часов, и текст на сервере и в браузере совпадает.INC-2291 · card and SEPA payments degradedPost-mortem · 28–30 September - Detectionfrom the first failed charge to the page
- Mitigationcard payments routed to the backup provider
- RecoverySEPA backlog re-sent and reconciled
- Total
Status page · English Card payments were unavailable for ; no charge was lost.
Status page · German Kartenzahlungen waren lang nicht verfügbar; keine Zahlung ging verloren.
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
locale | string | undefined | undefined | Локаль показа. Не задана — из адаптера i18n приложения. |
value | number | Date | readonly [Date, Date] | null | undefined | undefined | Что мерить. Три формы, каждая различима по типу: - **число** — секунды; - **пара дат** — промежуток между ними; - **одна дата** — от неё до «сейчас»; такая метка живая и тикает. |
base | Date | undefined | undefined | С чем сравнивать вместо «сейчас» — для формы с одной датой. Задан — часы не читаются вовсе: ни таймера, ни расхождения серверного рендера с клиентским. Это же делает воспроизводимыми тесты и снимки. |
live | boolean | undefined | undefined | Обновляться живьём. Такт компонент выбирает сам по младшей показанной единице. |
maxUnits | number | undefined | undefined | Сколько единиц показывать подряд: `2` даёт «2 ч 30 мин». |
largestUnit | DurationUnit | undefined | undefined | Крупнее этой единицы не дробить: остаток копится в ней. |
smallestUnit | DurationUnit | undefined | undefined | Мельче этой не спускаться. |
width | NameWidth | undefined | undefined | Длина имён единиц: «2 часа 30 минут» против «2 ч 30 мин». |
Слоты
| Slot | Type | Описание |
|---|---|---|
default | { text: string; datetime: string; seconds: number; } | Своя разметка вместо текста: значения те же, что компонент рисует сам. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrDurationConfigurableProps, GrDurationProps } from '@feugene/granularity-chrono/components/GrDuration'Файлdist/types/components/GrDuration/config.d.ts
const grDurationConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDuration">;Файлdist/types/components/GrDuration/defaults.d.ts
Пропы GrDuration, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrDuration: { … } }">.
Ровно то, что задают один раз на приложение: подробность и длина имён
единиц. Само значение — всегда на месте.
interface GrDurationConfigurableProps {
width: NameWidth;
maxUnits: number;
largestUnit: DurationUnit;
smallestUnit: DurationUnit;
live: boolean;
}Файлdist/types/components/GrDuration/safelist.d.ts
const grDurationSafelist: string[];Файлdist/types/components/GrDuration/GrDuration.vue.d.ts
GrDuration — «2 ч 30 мин»: сколько длилось, а не когда случилось.
Имена единиц и склонение числительного знает платформа, поэтому своих строк
у компонента нет ни одной — он говорит на всех языках, которые знает движок.
Разметка — <time> с машинным datetime в формате ISO 8601: сокращённый
текст читает человек, точная длина остаётся машине.
interface GrDurationProps {
/**
* Что мерить. Три формы, каждая различима по типу:
*
* - **число** — секунды;
* - **пара дат** — промежуток между ними;
* - **одна дата** — от неё до «сейчас»; такая метка живая и тикает.
*/
value?: number | Date | readonly [Date, Date] | null;
/**
* С чем сравнивать вместо «сейчас» — для формы с одной датой.
*
* Задан — часы не читаются вовсе: ни таймера, ни расхождения серверного
* рендера с клиентским. Это же делает воспроизводимыми тесты и снимки.
*/
base?: Date;
/** Обновляться живьём. Такт компонент выбирает сам по младшей показанной единице. */
live?: boolean;
/** Сколько единиц показывать подряд: `2` даёт «2 ч 30 мин». */
maxUnits?: number;
/** Крупнее этой единицы не дробить: остаток копится в ней. */
largestUnit?: DurationUnit;
/** Мельче этой не спускаться. */
smallestUnit?: DurationUnit;
/** Длина имён единиц: «2 часа 30 минут» против «2 ч 30 мин». */
width?: NameWidth;
/** Локаль показа. Не задана — из адаптера i18n приложения. */
locale?: string;
}Использование из прозы библиотеки
Единиц четыре, и крупнее не будет
Сутки, часы, минуты, секунды. Месяца и года здесь нет намеренно: они
календарные — в феврале и в июле разной длины, — и вывести их из числа секунд
нельзя, не соврав. Разрыв, который правда меряется календарём, показывает
GrRelativeTime, а считает differenceInMonths.
Потолок, а не квота
maxUnits ограничивает число единиц сверху и ничего не добирает: ровно два часа
это «2 ч», а не «2 ч 0 мин». Младшее при этом отбрасывается, а не
округляется — «2 ч 59 мин 30 с» остаётся «2 ч 59 мин». Округление подняло бы
показ выше реально прошедшего времени, а этим значением обычно меряют именно
его.
Ноль — единственное исключение из отбрасывания: «0 с» печатается, потому что пустая строка читается как «нет данных», а ноль означает другое.
Живая метка — только у формы с одной датой
Число и пара дат задают длину целиком, тикать в них нечему. Момент начала — другое дело: до «сейчас» время идёт, и метка пересчитывается сама. Такт выбирается по младшей показанной единице, поэтому метка в часах не будит вкладку каждую секунду.
Живой показ выведен из часов, а значит серверный рендер и клиентский разойдутся.
Компонент помечает это data-allow-mismatch, и убирается пометка не флагом, а
данными: передайте base — рендер станет детерминированным.
Границы
Обратного отсчёта здесь нет: «осталось 2 ч 30 мин» — это та же длина, но дедлайн, ноль и переход в отрицательные значения принадлежат сценарию, а не метке. Считайте остаток сами и передавайте числом.
Пропы этого примера