From 0bd18331cf8f57efb5f1266aace6b2dc8a5f8ef2 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:00:53 +0700 Subject: [PATCH 01/14] docs(time-range): update article text per copy review (#DS-4918) --- packages/components/time-range/time-range.en.md | 10 +++++----- packages/components/time-range/time-range.ru.md | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/components/time-range/time-range.en.md b/packages/components/time-range/time-range.en.md index fa8699fba3..80556ec1b3 100644 --- a/packages/components/time-range/time-range.en.md +++ b/packages/components/time-range/time-range.en.md @@ -1,27 +1,27 @@ -Time range selection menu. The user can choose one of the preset values or specify the range manually. +Time range selection menu. The user can choose one of the preset values or specify the time range manually. ### Trigger element -The menu can drop down from different trigger elements, and the window can have a pointer. +The component allows you to customize the trigger element. For example, it can be a button, a field, or a pseudo-link. ### Custom presets -When there are no preset values, only the From and To fields remain in the dialog: +When there are no preset values, only the start and end of the period remain in the window. ### Selection restriction -It is possible to configure the minimum and maximum date: +You can configure the minimum and maximum date. For example, a range from 2015 to 2017. ### Custom time ranges -The option to configure interval selection is available: +The component allows you to set custom time range options. diff --git a/packages/components/time-range/time-range.ru.md b/packages/components/time-range/time-range.ru.md index 7dccca577a..c011bbbb7a 100644 --- a/packages/components/time-range/time-range.ru.md +++ b/packages/components/time-range/time-range.ru.md @@ -1,27 +1,27 @@ -Меню выбора периода времени. Пользователь может выбрать одно из предустановленных значений или самостоятельно указать период. +Меню выбора периода. Пользователь может выбрать одно из предустановленных значений или самостоятельно указать временной диапазон. ### Триггерный элемент -Меню может выпадать из разных триггерных элементов, а окно может не иметь стрелку-указатель. +Компонент позволяет настроить триггерный элемент. Это может быть, например, кнопка, поле или псевдоссылка. ### Пользовательские предустановки -Когда нет предустановленных значений, в диалоге остаются только поля **С** и **По**: +Когда нет предустановленных значений, в окне остается только начало и конец периода. ### Ограничение выбора -Доступна настройка минимальной и максимальной даты: +Доступна настройка минимальной и максимальной даты. Например, диапазон с 2015 по 2017 год. ### Настройка промежутков -Доступна настройка выбора промежутков: +Компонент позволяет задать варианты промежутков времени. From a8fa25382456e4208c0faa4857d11963c379b7ff Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:47:00 +0700 Subject: [PATCH 02/14] fix(time-range): capitalize preset labels and localize overview example caption (#DS-4918) --- packages/components/core/locales/en-US.ts | 33 +++++++------ packages/components/core/locales/ru-RU.ts | 49 ++++++++++--------- packages/components/core/locales/types.ts | 6 +++ .../__snapshots__/time-range.spec.ts.snap | 4 +- .../time-range/time-range-editor.html | 6 +-- .../time-range-overview-example.ts | 21 ++++++-- tools/public_api_guard/components/core.api.md | 9 ++++ 7 files changed, 82 insertions(+), 46 deletions(-) diff --git a/packages/components/core/locales/en-US.ts b/packages/components/core/locales/en-US.ts index bad707dba2..855ad776b9 100644 --- a/packages/components/core/locales/en-US.ts +++ b/packages/components/core/locales/en-US.ts @@ -135,10 +135,13 @@ export const enUSLocaleData = { to: 'to', apply: 'Apply', cancel: 'Cancel', - rangeLabel: 'for period', + rangeLabel: 'Period', allTime: 'for all time', currentQuarter: 'for the current quarter', - currentYear: 'for the current year' + currentYear: 'for the current year', + allTimeOption: 'All time', + currentQuarterOption: 'Current quarter', + currentYearOption: 'Current year' }, durationTemplate: { title: { @@ -180,31 +183,31 @@ export const enUSLocaleData = { LAST_PART_SEPARATOR: '', YEARS: `{years, plural, one {# year} - other {last # years} + other {Last # years} }`, MONTHS: `{months, plural, - one {last month} - other {last # months} + one {Last month} + other {Last # months} }`, WEEKS: `{weeks, plural, - one {last week} - other {last # weeks} + one {Last week} + other {Last # weeks} }`, DAYS: `{days, plural, - one {last day} - other {last # days} + one {Last day} + other {Last # days} }`, HOURS: `{hours, plural, - one {last hour} - other {last # hours} + one {Last hour} + other {Last # hours} }`, MINUTES: `{minutes, plural, - one {last minute} - other {last # minutes} + one {Last minute} + other {Last # minutes} }`, SECONDS: `{seconds, plural, - one {last second} - other {last # seconds} + one {Last second} + other {Last # seconds} }`, YEARS_FRACTION: `{years} years`, MONTHS_FRACTION: `{months} months` diff --git a/packages/components/core/locales/ru-RU.ts b/packages/components/core/locales/ru-RU.ts index d9755d3054..fc558096a9 100644 --- a/packages/components/core/locales/ru-RU.ts +++ b/packages/components/core/locales/ru-RU.ts @@ -135,10 +135,13 @@ export const ruRULocaleData = { to: 'по', apply: 'Применить', cancel: 'Отмена', - rangeLabel: 'за период', + rangeLabel: 'Период', allTime: 'за все время', currentQuarter: 'за текущий квартал', - currentYear: 'за текущий год' + currentYear: 'за текущий год', + allTimeOption: 'Все время', + currentQuarterOption: 'Текущий квартал', + currentYearOption: 'Текущий год' }, durationTemplate: { title: { @@ -186,39 +189,39 @@ export const ruRULocaleData = { SEPARATOR: ' ', LAST_PART_SEPARATOR: 'и', YEARS: `{years, plural, - one {последний год} - few {последние # лет} - other {последние # лет} + one {Последний год} + few {Последние # лет} + other {Последние # лет} }`, MONTHS: `{months, plural, - one {последний месяц} - few {последние # месяца} - other {последние # месяцев} + one {Последний месяц} + few {Последние # месяца} + other {Последние # месяцев} }`, WEEKS: `{weeks, plural, - one {последняя неделя} - few {последние # недели} - other {последние # недель} + one {Последняя неделя} + few {Последние # недели} + other {Последние # недель} }`, DAYS: `{days, plural, - one {последний день} - few {последние # дня} - other {последние # дней} + one {Последний день} + few {Последние # дня} + other {Последние # дней} }`, HOURS: `{hours, plural, - one {последний час} - few {последние # часа} - other {последние # часов} + one {Последний час} + few {Последние # часа} + other {Последние # часов} }`, MINUTES: `{minutes, plural, - one {последняя минута} - few {последние # минуты} - other {последние # минут} + one {Последняя минута} + few {Последние # минуты} + other {Последние # минут} }`, SECONDS: `{seconds, plural, - one {последняя секунда} - few {последние # секунды} - other {последние # секунд} + one {Последняя секунда} + few {Последние # секунды} + other {Последние # секунд} }`, YEARS_FRACTION: `{years} лет`, MONTHS_FRACTION: `{months} месяцев` diff --git a/packages/components/core/locales/types.ts b/packages/components/core/locales/types.ts index 489cd8c251..25510db2e9 100644 --- a/packages/components/core/locales/types.ts +++ b/packages/components/core/locales/types.ts @@ -62,6 +62,12 @@ export type KbqTimeRangeLocaleConfig = { allTime: string; currentQuarter: string; currentYear: string; + /** Label for the `allTime` option in the presets list, as opposed to the trigger label in `allTime` */ + allTimeOption?: string; + /** Label for the `currentQuarter` option in the presets list, as opposed to the trigger label in `currentQuarter` */ + currentQuarterOption?: string; + /** Label for the `currentYear` option in the presets list, as opposed to the trigger label in `currentYear` */ + currentYearOption?: string; }; durationTemplate: { title: FormatterDurationTemplate; diff --git a/packages/components/time-range/__snapshots__/time-range.spec.ts.snap b/packages/components/time-range/__snapshots__/time-range.spec.ts.snap index f253e1a62e..02efc9c512 100644 --- a/packages/components/time-range/__snapshots__/time-range.spec.ts.snap +++ b/packages/components/time-range/__snapshots__/time-range.spec.ts.snap @@ -10,7 +10,7 @@ exports[`KbqTimeRange Component initialization should apply custom option templa " Currentquarter ", " Currentyear ", " Alltime ", - "за период", + "Период", ] `; @@ -25,7 +25,7 @@ exports[`KbqTimeRange Component initialization should check range as default if exports[`KbqTimeRange Component initialization should check selected radio if external value provided 1`] = ` { - "checkedRadio": " за текущий год ", + "checkedRadio": " Текущий год ", "trigger": "за текущий год", } `; diff --git a/packages/components/time-range/time-range-editor.html b/packages/components/time-range/time-range-editor.html index 4b233e4826..f4c719e7b3 100644 --- a/packages/components/time-range/time-range-editor.html +++ b/packages/components/time-range/time-range-editor.html @@ -80,13 +80,13 @@ @case ('other') { @switch (context.type) { @case ('allTime') { - {{ localeConfig.editor.allTime }} + {{ localeConfig.editor.allTimeOption ?? localeConfig.editor.allTime }} } @case ('currentQuarter') { - {{ localeConfig.editor.currentQuarter }} + {{ localeConfig.editor.currentQuarterOption ?? localeConfig.editor.currentQuarter }} } @case ('currentYear') { - {{ localeConfig.editor.currentYear }} + {{ localeConfig.editor.currentYearOption ?? localeConfig.editor.currentYear }} } } } diff --git a/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts b/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts index 8ed3e1f12e..aaa7949bb6 100644 --- a/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts +++ b/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts @@ -1,8 +1,17 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, InjectionToken } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter'; -import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core'; +import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE, KBQ_LOCALE_SERVICE } from '@koobiq/components/core'; import { KbqTimeRange, KbqTimeRangeRange, KbqTimeRangeType } from '@koobiq/components/time-range'; +import { of } from 'rxjs'; + +const ExampleLocalizedData = new InjectionToken>('ExampleLocalizedData', { + factory: () => ({ + 'ru-RU': 'События', + default: 'Events' + }) +}); /** * @title Time range overview @@ -15,6 +24,7 @@ import { KbqTimeRange, KbqTimeRangeRange, KbqTimeRangeType } from '@koobiq/compo LuxonDateModule ], template: ` + {{ caption() }} `, providers: [ @@ -22,10 +32,15 @@ import { KbqTimeRange, KbqTimeRangeRange, KbqTimeRangeType } from '@koobiq/compo ], changeDetection: ChangeDetectionStrategy.OnPush, host: { - class: 'layout-flex layout-row layout-align-center-center layout-gap-3xl' + class: 'layout-flex layout-row layout-align-center-center layout-gap-xxs' } }) export class TimeRangeOverviewExample { + private readonly data = inject(ExampleLocalizedData); + private readonly localeId = toSignal(inject(KBQ_LOCALE_SERVICE, { optional: true })?.changes || of('')); + + protected readonly caption = computed(() => this.data[this.localeId() || 'default'] ?? this.data.default); + protected readonly control = new FormControl( { type: 'last5Minutes' diff --git a/tools/public_api_guard/components/core.api.md b/tools/public_api_guard/components/core.api.md index e8018147e6..47003eaf56 100644 --- a/tools/public_api_guard/components/core.api.md +++ b/tools/public_api_guard/components/core.api.md @@ -1848,6 +1848,9 @@ export function KBQ_DEFAULT_LOCALE_DATA_FACTORY(): { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -3421,6 +3424,9 @@ export type KbqTimeRangeLocaleConfig = { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption?: string; + currentQuarterOption?: string; + currentYearOption?: string; }; durationTemplate: { title: FormatterDurationTemplate; @@ -4409,6 +4415,9 @@ export const ruRULocaleData: { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { From 565ba909c2f3ff431bcd7914d4ac060352417680 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 19:24:58 +0700 Subject: [PATCH 03/14] docs(time-range): drop preposition in empty-type-list example trigger label (#DS-4918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В третьем примере (time-range-empty-type-list) добавлен кастомный titleTemplate, чтобы подпись выбранного периода шла без предлога «за»/«for» — только в этом примере, глобальное поведение компонента не тронуто. --- .../time-range-empty-type-list-example.ts | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts b/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts index ceddfcb3c5..5cebcf117a 100644 --- a/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts +++ b/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts @@ -1,7 +1,9 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter'; import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core'; +import { KbqIconModule } from '@koobiq/components/icon'; +import { KbqLinkModule } from '@koobiq/components/link'; import { KbqTimeRange, KbqTimeRangeType } from '@koobiq/components/time-range'; /** @@ -12,10 +14,27 @@ import { KbqTimeRange, KbqTimeRangeType } from '@koobiq/components/time-range'; imports: [ ReactiveFormsModule, KbqTimeRange, - LuxonDateModule + LuxonDateModule, + KbqLinkModule, + KbqIconModule ], template: ` - + + + + {{ + dateFormatter.rangeLongDate( + dateAdapter.deserialize(context.startDateTime), + dateAdapter.deserialize(context.endDateTime) + ) + }} + + + + + + + `, providers: [ { provide: DateFormatter, deps: [DateAdapter, KBQ_DATE_LOCALE] } @@ -26,5 +45,8 @@ import { KbqTimeRange, KbqTimeRangeType } from '@koobiq/components/time-range'; } }) export class TimeRangeEmptyTypeListExample { + protected readonly dateAdapter = inject(DateAdapter); + protected readonly dateFormatter = inject(DateFormatter); + protected readonly availableTimeRangeTypes: KbqTimeRangeType[] = []; } From 9d3c3d812e5d682bcb9e8050bfda555087299194 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 19:57:14 +0700 Subject: [PATCH 04/14] docs(time-range): tweak min-max example placeholder and trigger labels (#DS-4918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Убрана подпись о диапазоне под примером. Текст незаполненного поля изменён на «Выбрать период»/«Select period». Для произвольного периода убран предлог «за»/«for», а для остальных значений первая буква теперь заглавная. Правки только в этом примере, остальные примеры не затронуты. --- .../time-range-min-max-example.ts | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts b/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts index d7a745379c..c7452152b3 100644 --- a/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts +++ b/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts @@ -1,9 +1,20 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, InjectionToken } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { ReactiveFormsModule } from '@angular/forms'; import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter'; -import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core'; +import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE, KBQ_LOCALE_SERVICE } from '@koobiq/components/core'; +import { KbqIconModule } from '@koobiq/components/icon'; +import { KbqLinkModule } from '@koobiq/components/link'; import { KbqTimeRange } from '@koobiq/components/time-range'; import { DateTime } from 'luxon'; +import { of } from 'rxjs'; + +const ExampleLocalizedData = new InjectionToken>('ExampleLocalizedData', { + factory: () => ({ + 'ru-RU': 'Выбрать период', + default: 'Select period' + }) +}); /** * @title Time range min max @@ -13,13 +24,33 @@ import { DateTime } from 'luxon'; imports: [ ReactiveFormsModule, KbqTimeRange, - LuxonDateModule + LuxonDateModule, + KbqLinkModule, + KbqIconModule ], template: ` -
-
From 2017 to 2025
- -
+ + + + @if (!context.type) { + {{ placeholder() }} + } @else if (context.type === 'range') { + {{ + dateFormatter.rangeLongDate( + dateAdapter.deserialize(context.startDateTime), + dateAdapter.deserialize(context.endDateTime) + ) + }} + } @else { + {{ capitalize(context.formattedDate) }} + } + + + + + + + `, providers: [ { provide: DateFormatter, deps: [DateAdapter, KBQ_DATE_LOCALE] } @@ -30,8 +61,18 @@ import { DateTime } from 'luxon'; } }) export class TimeRangeMinMaxExample { - protected readonly adapter = inject>(DateAdapter); + protected readonly dateAdapter = inject>(DateAdapter); + protected readonly dateFormatter = inject(DateFormatter); + + protected readonly minDate = this.dateAdapter.createDate(2017, 0, 1); + protected readonly maxDate = this.dateAdapter.today(); - protected readonly minDate = this.adapter.createDate(2017, 0, 1); - protected readonly maxDate = this.adapter.today(); + private readonly data = inject(ExampleLocalizedData); + private readonly localeId = toSignal(inject(KBQ_LOCALE_SERVICE, { optional: true })?.changes || of('')); + + protected readonly placeholder = computed(() => this.data[this.localeId() || 'default'] ?? this.data.default); + + protected capitalize(text: string): string { + return text.charAt(0).toUpperCase() + text.slice(1); + } } From aa2423a6351521a34d4b603baa79d590d0e1d9cb Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:17:16 +0700 Subject: [PATCH 05/14] docs(time-range): localize custom-range-types example labels (#DS-4918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Подписи выбранных значений в примере теперь локализованы: без предлога «за»/«for» и с заглавной буквы (например, «Последние 3 минуты»). Текст незаполненного поля изменён на «Период»/«Period». Для произвольного периода подпись показывает не только даты, но и время. Правки только в этом примере, остальные примеры и компонент не затронуты. --- .../time-range-custom-range-types-example.ts | 84 ++++++++++++++++--- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts b/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts index 2564f7d856..f218ffcad2 100644 --- a/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts +++ b/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts @@ -1,16 +1,19 @@ -import { TitleCasePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, InjectionToken } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { ReactiveFormsModule } from '@angular/forms'; import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter'; -import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core'; +import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE, KBQ_LOCALE_SERVICE } from '@koobiq/components/core'; import { KbqFormFieldModule } from '@koobiq/components/form-field'; import { KbqIconModule } from '@koobiq/components/icon'; import { KBQ_CUSTOM_TIME_RANGE_TYPES, KBQ_DEFAULT_TIME_RANGE_TYPES, KbqCustomTimeRangeType, - KbqTimeRangeModule + KbqTimeRangeModule, + KbqTimeRangeTranslationType, + KbqTimeRangeUnits } from '@koobiq/components/time-range'; +import { of } from 'rxjs'; const customTypes: KbqCustomTimeRangeType[] = [ { type: 'last3Minutes', units: { minutes: -3 }, translationType: 'minutes' }, @@ -20,6 +23,21 @@ const customTypes: KbqCustomTimeRangeType[] = [ const customDefaultTypes = customTypes.map(({ type }) => type); +const customTypeUnits: Record = Object.fromEntries( + customTypes.map(({ type, units }) => [type, units]) +); + +const customTypeTranslation: Record = Object.fromEntries( + customTypes.map(({ type, translationType }) => [type, translationType]) +); + +const ExampleLocalizedData = new InjectionToken>('ExampleLocalizedData', { + factory: () => ({ + 'ru-RU': 'Период', + default: 'Period' + }) +}); + /** * @title Time range custom range types */ @@ -30,28 +48,39 @@ const customDefaultTypes = customTypes.map(({ type }) => type); KbqTimeRangeModule, LuxonDateModule, KbqIconModule, - KbqFormFieldModule, - TitleCasePipe + KbqFormFieldModule ], template: ` - {{ context.type | titlecase }} + {{ optionLabel(context.type) }} @if (!context.type) { - {{ context.formattedDate }} + {{ placeholder() }} + } @else if (context.type === 'range') { + {{ + dateFormatter.rangeShortDateTime( + dateAdapter.deserialize(context.startDateTime), + dateAdapter.deserialize(context.endDateTime) + ) + }} } @else { - {{ context.type | titlecase }} + {{ optionLabel(context.type) }} } - + `, providers: [ { provide: DateFormatter, deps: [DateAdapter, KBQ_DATE_LOCALE] }, @@ -63,4 +92,37 @@ const customDefaultTypes = customTypes.map(({ type }) => type); class: 'layout-flex layout-row layout-align-center-center layout-gap-3xl' } }) -export class TimeRangeCustomRangeTypesExample {} +export class TimeRangeCustomRangeTypesExample { + protected readonly dateAdapter = inject(DateAdapter); + protected readonly dateFormatter = inject(DateFormatter); + + private readonly localeService = inject(KBQ_LOCALE_SERVICE, { optional: true }); + private readonly localeId = toSignal(this.localeService?.changes || of('')); + + private readonly localeConfig = computed(() => { + this.localeId(); + + return this.localeService?.getParams('timeRange'); + }); + + private readonly data = inject(ExampleLocalizedData); + protected readonly placeholder = computed(() => this.data[this.localeId() || 'default'] ?? this.data.default); + + protected optionLabel(type: string): string { + const localeConfig = this.localeConfig(); + + if (!localeConfig) return type; + + const units = customTypeUnits[type]; + const translationType = customTypeTranslation[type]; + const startDate = this.dateAdapter.addCalendarUnits(this.dateAdapter.today(), units); + + return this.dateFormatter.duration( + startDate, + this.dateAdapter.today(), + [translationType as Exclude], + false, + localeConfig.durationTemplate.option + ); + } +} From b59e3ff922f09138b8c0733cc367d38954253139 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:29:03 +0700 Subject: [PATCH 06/14] chore(core): regenerate api report for time-range EN locale fields (#DS-4918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI-проверка check-api падала: enUSLocaleData в core.api.md не содержал новые allTimeOption/currentQuarterOption/currentYearOption поля, добавленные ранее в en-US.ts. Файл сгенерирован заново через approve-api. --- tools/public_api_guard/components/core.api.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/public_api_guard/components/core.api.md b/tools/public_api_guard/components/core.api.md index 47003eaf56..b9a4e7fbab 100644 --- a/tools/public_api_guard/components/core.api.md +++ b/tools/public_api_guard/components/core.api.md @@ -512,6 +512,9 @@ export const enUSLocaleData: { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -1211,6 +1214,9 @@ export function KBQ_DEFAULT_LOCALE_DATA_FACTORY(): { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { From 52ecadcd9657bd1bc1cbc19621a6cd29a1db9976 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 21:29:08 +0700 Subject: [PATCH 07/14] docs(time-range): move All time preset to the end in overview example (#DS-4918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В примере overview опция «Все время» теперь идёт последней в списке пресетов — после «Текущий квартал» и «Текущий год». --- .../time-range-overview/time-range-overview-example.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts b/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts index aaa7949bb6..ecd80917ef 100644 --- a/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts +++ b/packages/docs-examples/components/time-range/time-range-overview/time-range-overview-example.ts @@ -61,9 +61,9 @@ export class TimeRangeOverviewExample { 'last30Days', 'last3Months', 'last12Months', - 'allTime', 'currentQuarter', 'currentYear', + 'allTime', 'range' ]; } From 9fbc8c514bd14a5fb2e5d46a5d5eb5ac038dda0a Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Wed, 8 Jul 2026 22:30:47 +0700 Subject: [PATCH 08/14] fix(time-range): keep custom period selection after reopening popover (#DS-4918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В примере custom-range-types список типов для DI (KBQ_DEFAULT_TIME_RANGE_TYPES) не включал 'range', поэтому компонент считал выбранный произвольный период невалидным при повторном открытии и сбрасывал выбор на первый пресет. Добавлен 'range' в список типов примера — правка только в примере. --- .../time-range-custom-range-types-example.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts b/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts index f218ffcad2..62f6e87f86 100644 --- a/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts +++ b/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts @@ -11,6 +11,7 @@ import { KbqCustomTimeRangeType, KbqTimeRangeModule, KbqTimeRangeTranslationType, + KbqTimeRangeType, KbqTimeRangeUnits } from '@koobiq/components/time-range'; import { of } from 'rxjs'; @@ -21,7 +22,7 @@ const customTypes: KbqCustomTimeRangeType[] = [ { type: 'last3Years', units: { years: -3 }, translationType: 'months' } ]; -const customDefaultTypes = customTypes.map(({ type }) => type); +const customDefaultTypes = [...customTypes.map(({ type }) => type), 'range'] as KbqTimeRangeType[]; const customTypeUnits: Record = Object.fromEntries( customTypes.map(({ type, units }) => [type, units]) From 46c5eb95ff4e7879897b1581374bc2932c10b015 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:21:32 +0700 Subject: [PATCH 09/14] fix(time-range): fill preset option labels in es-LA, pt-BR and tk-TM locales (#DS-4918) --- packages/components/core/locales/es-LA.ts | 33 ++++++++++++----------- packages/components/core/locales/pt-BR.ts | 33 ++++++++++++----------- packages/components/core/locales/tk-TM.ts | 19 +++++++------ 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/packages/components/core/locales/es-LA.ts b/packages/components/core/locales/es-LA.ts index 94471ba698..f134f66d60 100644 --- a/packages/components/core/locales/es-LA.ts +++ b/packages/components/core/locales/es-LA.ts @@ -134,10 +134,13 @@ export const esLALocaleData = { to: 'a', apply: 'Aplicar', cancel: 'Cancelar', - rangeLabel: 'para el período', + rangeLabel: 'Período', allTime: 'por todo el tiempo', currentQuarter: 'por el trimestre actual', - currentYear: 'por el año actual' + currentYear: 'por el año actual', + allTimeOption: 'Todo el tiempo', + currentQuarterOption: 'Trimestre actual', + currentYearOption: 'Año actual' }, durationTemplate: { title: { @@ -179,31 +182,31 @@ export const esLALocaleData = { LAST_PART_SEPARATOR: '', YEARS: `{years, plural, one {# año} - other {últimos # años} + other {Últimos # años} }`, MONTHS: `{months, plural, - one {último mes} - other {últimos # meses} + one {Último mes} + other {Últimos # meses} }`, WEEKS: `{weeks, plural, - one {última semana} - other {últimas # semanas} + one {Última semana} + other {Últimas # semanas} }`, DAYS: `{days, plural, - one {último día} - other {últimos # días} + one {Último día} + other {Últimos # días} }`, HOURS: `{hours, plural, - one {última hora} - other {últimas # horas} + one {Última hora} + other {Últimas # horas} }`, MINUTES: `{minutes, plural, - one {último minuto} - other {últimos # minutos} + one {Último minuto} + other {Últimos # minutos} }`, SECONDS: `{seconds, plural, - one {último segundo} - other {últimos # segundos} + one {Último segundo} + other {Últimos # segundos} }`, YEARS_FRACTION: `{years} años`, MONTHS_FRACTION: `{months} meses` diff --git a/packages/components/core/locales/pt-BR.ts b/packages/components/core/locales/pt-BR.ts index 45368cfcd1..7882cb4e24 100644 --- a/packages/components/core/locales/pt-BR.ts +++ b/packages/components/core/locales/pt-BR.ts @@ -134,10 +134,13 @@ export const ptBRLocaleData = { to: 'até', apply: 'Aplicar', cancel: 'Cancelar', - rangeLabel: 'para o período', + rangeLabel: 'Período', allTime: 'por todo o tempo', currentQuarter: 'pelo trimestre atual', - currentYear: 'pelo ano atual' + currentYear: 'pelo ano atual', + allTimeOption: 'Todo o tempo', + currentQuarterOption: 'Trimestre atual', + currentYearOption: 'Ano atual' }, durationTemplate: { title: { @@ -179,31 +182,31 @@ export const ptBRLocaleData = { LAST_PART_SEPARATOR: '', YEARS: `{years, plural, one {# ano} - other {últimos # anos} + other {Últimos # anos} }`, MONTHS: `{months, plural, - one {último mês} - other {últimos # meses} + one {Último mês} + other {Últimos # meses} }`, WEEKS: `{weeks, plural, - one {última semana} - other {últimas # semanas} + one {Última semana} + other {Últimas # semanas} }`, DAYS: `{days, plural, - one {último dia} - other {últimos # dias} + one {Último dia} + other {Últimos # dias} }`, HOURS: `{hours, plural, - one {última hora} - other {últimas # horas} + one {Última hora} + other {Últimas # horas} }`, MINUTES: `{minutes, plural, - one {último minuto} - other {últimos # minutos} + one {Último minuto} + other {Últimos # minutos} }`, SECONDS: `{seconds, plural, - one {último segundo} - other {últimos # segundos} + one {Último segundo} + other {Últimos # segundos} }`, YEARS_FRACTION: `{years} anos`, MONTHS_FRACTION: `{months} meses` diff --git a/packages/components/core/locales/tk-TM.ts b/packages/components/core/locales/tk-TM.ts index e335bfe187..1aff94826d 100644 --- a/packages/components/core/locales/tk-TM.ts +++ b/packages/components/core/locales/tk-TM.ts @@ -135,10 +135,13 @@ export const tkTMLocaleData = { to: 'şuňa', apply: 'Ulanmak', cancel: 'Ýatyrmak', - rangeLabel: 'şu döwürde', + rangeLabel: 'Döwür', allTime: 'hemme döwür üçin', currentQuarter: 'şu çärýek üçin', - currentYear: 'şu ýyl üçin' + currentYear: 'şu ýyl üçin', + allTimeOption: 'Hemme döwür', + currentQuarterOption: 'Şu çärýek', + currentYearOption: 'Şu ýyl' }, durationTemplate: { title: { @@ -183,27 +186,27 @@ export const tkTMLocaleData = { other {# ýyl} }`, MONTHS: `{months, plural, - one {aý} + one {Aý} other {# aý} }`, WEEKS: `{weeks, plural, - one {hepde} + one {Hepde} other {# hepde} }`, DAYS: `{days, plural, - one {gün} + one {Gün} other {# gün} }`, HOURS: `{hours, plural, - one {sagat} + one {Sagat} other {# sagat} }`, MINUTES: `{minutes, plural, - one {minut} + one {Minut} other {# minut} }`, SECONDS: `{seconds, plural, - one {sekunt} + one {Sekunt} other {# sekunt} }`, YEARS_FRACTION: `{years} ýyl`, From f04e50a0438ad6c41861fdc988fda893527962eb Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:21:33 +0700 Subject: [PATCH 10/14] chore(core): regenerate api report for remaining time-range locale fields (#DS-4918) --- tools/public_api_guard/components/core.api.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/public_api_guard/components/core.api.md b/tools/public_api_guard/components/core.api.md index b9a4e7fbab..b2206b4396 100644 --- a/tools/public_api_guard/components/core.api.md +++ b/tools/public_api_guard/components/core.api.md @@ -753,6 +753,9 @@ export const esLALocaleData: { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -1430,6 +1433,9 @@ export function KBQ_DEFAULT_LOCALE_DATA_FACTORY(): { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -1639,6 +1645,9 @@ export function KBQ_DEFAULT_LOCALE_DATA_FACTORY(): { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -2068,6 +2077,9 @@ export function KBQ_DEFAULT_LOCALE_DATA_FACTORY(): { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -3996,6 +4008,9 @@ export const ptBRLocaleData: { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { @@ -4770,6 +4785,9 @@ export const tkTMLocaleData: { allTime: string; currentQuarter: string; currentYear: string; + allTimeOption: string; + currentQuarterOption: string; + currentYearOption: string; }; durationTemplate: { title: { From 3abf02e0dd35488a7399d92cfa7553f8c641649c Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:21:33 +0700 Subject: [PATCH 11/14] fix(time-range): use correct russian plural form for years in duration templates (#DS-4918) --- packages/components/core/locales/ru-RU.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/core/locales/ru-RU.ts b/packages/components/core/locales/ru-RU.ts index fc558096a9..6761d0724b 100644 --- a/packages/components/core/locales/ru-RU.ts +++ b/packages/components/core/locales/ru-RU.ts @@ -149,7 +149,7 @@ export const ruRULocaleData = { LAST_PART_SEPARATOR: 'и', YEARS: `{years, plural, one {# год} - few {последние # лет} + few {последние # года} other {последние # лет} }`, MONTHS: `{months, plural, @@ -190,7 +190,7 @@ export const ruRULocaleData = { LAST_PART_SEPARATOR: 'и', YEARS: `{years, plural, one {Последний год} - few {Последние # лет} + few {Последние # года} other {Последние # лет} }`, MONTHS: `{months, plural, From f2268ee6c0f2bd434d3c558ef32c227cff87e9d3 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:22:00 +0700 Subject: [PATCH 12/14] docs(time-range): align min-max example date limits with article text (#DS-4918) --- .../time-range-min-max/time-range-min-max-example.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts b/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts index c7452152b3..5c66e6d8ed 100644 --- a/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts +++ b/packages/docs-examples/components/time-range/time-range-min-max/time-range-min-max-example.ts @@ -64,8 +64,8 @@ export class TimeRangeMinMaxExample { protected readonly dateAdapter = inject>(DateAdapter); protected readonly dateFormatter = inject(DateFormatter); - protected readonly minDate = this.dateAdapter.createDate(2017, 0, 1); - protected readonly maxDate = this.dateAdapter.today(); + protected readonly minDate = this.dateAdapter.createDate(2015, 0, 1); + protected readonly maxDate = this.dateAdapter.createDate(2017, 11, 31); private readonly data = inject(ExampleLocalizedData); private readonly localeId = toSignal(inject(KBQ_LOCALE_SERVICE, { optional: true })?.changes || of('')); From 26ae5268ed61cb4f02c9b373a3414a5a4eaa8dc3 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:22:01 +0700 Subject: [PATCH 13/14] docs(time-range): fix years translation type and guard empty range value in examples (#DS-4918) --- .../time-range-custom-range-types-example.ts | 2 +- .../time-range-empty-type-list-example.ts | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts b/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts index 62f6e87f86..4aa531622b 100644 --- a/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts +++ b/packages/docs-examples/components/time-range/time-range-custom-range-types/time-range-custom-range-types-example.ts @@ -19,7 +19,7 @@ import { of } from 'rxjs'; const customTypes: KbqCustomTimeRangeType[] = [ { type: 'last3Minutes', units: { minutes: -3 }, translationType: 'minutes' }, { type: 'last3Weeks', units: { weeks: -3 }, translationType: 'weeks' }, - { type: 'last3Years', units: { years: -3 }, translationType: 'months' } + { type: 'last3Years', units: { years: -3 }, translationType: 'years' } ]; const customDefaultTypes = [...customTypes.map(({ type }) => type), 'range'] as KbqTimeRangeType[]; diff --git a/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts b/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts index 5cebcf117a..1067f209b2 100644 --- a/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts +++ b/packages/docs-examples/components/time-range/time-range-empty-type-list/time-range-empty-type-list-example.ts @@ -22,12 +22,16 @@ import { KbqTimeRange, KbqTimeRangeType } from '@koobiq/components/time-range'; - {{ - dateFormatter.rangeLongDate( - dateAdapter.deserialize(context.startDateTime), - dateAdapter.deserialize(context.endDateTime) - ) - }} + @if (context.startDateTime && context.endDateTime) { + {{ + dateFormatter.rangeLongDate( + dateAdapter.deserialize(context.startDateTime), + dateAdapter.deserialize(context.endDateTime) + ) + }} + } @else { + {{ context.formattedDate }} + } From 6b003568cdf47f4cf5efee42b29a80ca688bf8c7 Mon Sep 17 00:00:00 2001 From: Roman Turov <31649219+rmnturov@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:22:01 +0700 Subject: [PATCH 14/14] docs(time-range): localize period placeholder in as-form-field and custom-option examples (#DS-4918) --- .../time-range-as-form-field-example.ts | 22 +++++++++++++++---- .../time-range-custom-option-example.ts | 22 +++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/docs-examples/components/time-range/time-range-as-form-field/time-range-as-form-field-example.ts b/packages/docs-examples/components/time-range/time-range-as-form-field/time-range-as-form-field-example.ts index bcd1ef2f7e..273d558936 100644 --- a/packages/docs-examples/components/time-range/time-range-as-form-field/time-range-as-form-field-example.ts +++ b/packages/docs-examples/components/time-range/time-range-as-form-field/time-range-as-form-field-example.ts @@ -1,11 +1,20 @@ import { TitleCasePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, InjectionToken } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { ReactiveFormsModule } from '@angular/forms'; import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter'; -import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core'; +import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE, KBQ_LOCALE_SERVICE } from '@koobiq/components/core'; import { KbqFormFieldModule } from '@koobiq/components/form-field'; import { KbqIconModule } from '@koobiq/components/icon'; import { KbqTimeRangeModule } from '@koobiq/components/time-range'; +import { of } from 'rxjs'; + +const ExampleLocalizedData = new InjectionToken>('ExampleLocalizedData', { + factory: () => ({ + 'ru-RU': 'Период', + default: 'Period' + }) +}); /** * @title Time range as form field @@ -25,7 +34,7 @@ import { KbqTimeRangeModule } from '@koobiq/components/time-range'; @if (!context.type) { - {{ context.formattedDate }} + {{ placeholder() }} } @else { {{ context.formattedDate[0] | titlecase }}{{ context.formattedDate.slice(1) }} } @@ -44,4 +53,9 @@ import { KbqTimeRangeModule } from '@koobiq/components/time-range'; class: 'layout-flex layout-row layout-align-center-center layout-gap-3xl' } }) -export class TimeRangeAsFormFieldExample {} +export class TimeRangeAsFormFieldExample { + private readonly data = inject(ExampleLocalizedData); + private readonly localeId = toSignal(inject(KBQ_LOCALE_SERVICE, { optional: true })?.changes || of('')); + + protected readonly placeholder = computed(() => this.data[this.localeId() || 'default'] ?? this.data.default); +} diff --git a/packages/docs-examples/components/time-range/time-range-custom-option/time-range-custom-option-example.ts b/packages/docs-examples/components/time-range/time-range-custom-option/time-range-custom-option-example.ts index 9cefc3d66d..8a009fbf4c 100644 --- a/packages/docs-examples/components/time-range/time-range-custom-option/time-range-custom-option-example.ts +++ b/packages/docs-examples/components/time-range/time-range-custom-option/time-range-custom-option-example.ts @@ -1,8 +1,9 @@ import { TitleCasePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, InjectionToken } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { ReactiveFormsModule } from '@angular/forms'; import { LuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter'; -import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE } from '@koobiq/components/core'; +import { DateAdapter, DateFormatter, KBQ_DATE_LOCALE, KBQ_LOCALE_SERVICE } from '@koobiq/components/core'; import { KbqFormFieldModule } from '@koobiq/components/form-field'; import { KbqIconModule } from '@koobiq/components/icon'; import { @@ -11,6 +12,14 @@ import { KbqCustomTimeRangeType, KbqTimeRangeModule } from '@koobiq/components/time-range'; +import { of } from 'rxjs'; + +const ExampleLocalizedData = new InjectionToken>('ExampleLocalizedData', { + factory: () => ({ + 'ru-RU': 'Период', + default: 'Period' + }) +}); export function customTypesFactory(adapter: DateAdapter): KbqCustomTimeRangeType[] { const currentYear = adapter.startOf(adapter.today(), 'year'); @@ -77,7 +86,7 @@ export function customTypesFactory(adapter: DateAdapter): KbqCustomTimeRan @if (!context.type) { - {{ context.formattedDate }} + {{ placeholder() }} } @else { {{ context.type | titlecase }} } @@ -107,4 +116,9 @@ export function customTypesFactory(adapter: DateAdapter): KbqCustomTimeRan class: 'layout-flex layout-row layout-align-center-center layout-gap-3xl' } }) -export class TimeRangeCustomOptionExample {} +export class TimeRangeCustomOptionExample { + private readonly data = inject(ExampleLocalizedData); + private readonly localeId = toSignal(inject(KBQ_LOCALE_SERVICE, { optional: true })?.changes || of('')); + + protected readonly placeholder = computed(() => this.data[this.localeId() || 'default'] ?? this.data.default); +}