@rt-tools/ui-kit-v2 0.41.0 → 0.42.0

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.
@@ -213,8 +213,8 @@ var RtCalendarComponent = class RtCalendarComponent {
213
213
  inputs: ["radius", "radius"]
214
214
  }],
215
215
  ngImport: i0,
216
- template: "<ng-container rtBlock=\"rt-calendar\">\n <div rtElem=\"header\">\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-prev-month\"\n icon=\"chevron-left\"\n variant=\"secondary\"\n radius=\"full\"\n [ariaLabel]=\"prevAriaLabel()\"\n [disabled]=\"!canPrev()\"\n (clicked)=\"prevMonth.emit()\" />\n @if (headerTitle() && months()[0]; as first) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: first }\" />\n }\n <ng-content select=\"[calendarHint]\" />\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-next-month\"\n icon=\"chevron-right\"\n variant=\"secondary\"\n radius=\"full\"\n [ariaLabel]=\"nextAriaLabel()\"\n [disabled]=\"!canNext()\"\n (clicked)=\"nextMonth.emit()\" />\n </div>\n\n <div rtElem=\"months\" qa-dataid=\"calendar-months\" (mouseleave)=\"dayHover.emit(null)\">\n @for (month of months(); track month.key) {\n <div rtElem=\"month\" qa-dataid=\"calendar-month\">\n @if (!headerTitle()) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: month }\" />\n }\n <div rtElem=\"grid\" qa-dataid=\"calendar-grid\">\n @for (weekday of weekdayLabels(); track $index) {\n <span rtElem=\"weekday\" qa-dataid=\"calendar-weekday\">{{ weekday }}</span>\n }\n @for (blank of month.leadingBlanks; track blank) {\n <!-- Пустая ячейка сетки до первого дня месяца: занимает место и ничего не красит. -->\n <span qa-dataid=\"calendar-blank\" aria-hidden=\"true\"></span>\n }\n @for (day of month.days; track day.key) {\n <button\n #dayButton\n rtElem=\"day\"\n qa-dataid=\"calendar-day\"\n type=\"button\"\n [attr.data-iso]=\"day.key\"\n [attr.data-state]=\"day.state\"\n [attr.data-today]=\"day.today ? '' : null\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.aria-pressed]=\"day.state === chosenState ? true : null\"\n [attr.tabindex]=\"rovingKey() === null ? null : day.key === rovingKey() ? 0 : -1\"\n [disabled]=\"day.disabled\"\n (keydown)=\"onDayKey($event, day)\"\n (mouseenter)=\"onDayHover(day)\"\n (focus)=\"onDayHover(day)\"\n (click)=\"onDayClick(day)\">\n <span rtElem=\"day-num\" qa-dataid=\"calendar-day-num\">{{ day.dayOfMonth }}</span>\n @if (sublabelsLoading()) {\n <rt-skeleton\n rtElem=\"day-sublabel\"\n qa-dataid=\"calendar-day-price-skeleton\"\n width=\"80%\"\n radius=\"sm\"\n [height]=\"sublabelSkeletonHeight\" />\n } @else if (day.sublabel) {\n <span rtElem=\"day-sublabel\" qa-dataid=\"calendar-day-price\">{{ day.sublabel }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-content />\n\n <ng-template #titleTpl let-month>\n @if (titleAction()) {\n <button rtElem=\"month-title\" qa-dataid=\"calendar-month-title\" type=\"button\" (click)=\"titleClick.emit(month)\">\n {{ month.label }}\n </button>\n } @else {\n <p rtElem=\"month-title\" qa-dataid=\"calendar-month-title\">{{ month.label }}</p>\n }\n </ng-template>\n</ng-container>\n",
217
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-calendar{--rt-calendar-day-radius: var(--rt-radius-md);--rt-calendar-day-size: auto;--rt-calendar-day-justify: flex-start;--rt-calendar-month-count: 2;--rt-calendar-column-gap: var(--rt-space-0-5);--rt-calendar-range-inner-radius: var(--rt-calendar-day-radius);--rt-calendar-today-ring: inset 0 0 0 var(--rt-border-width-thin) var(--rt-color-action-primary);display:block}.rt-calendar__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--rt-space-4);gap:var(--rt-space-4)}.rt-calendar__nav{flex:0 0 auto}.rt-calendar__header .rt-calendar__month-title{width:auto;padding:var(--rt-space-1) var(--rt-space-2);margin:0}.rt-calendar__months{display:grid;gap:var(--rt-space-6);grid-template-columns:repeat(var(--rt-calendar-month-count),1fr)}.rt-calendar__month-title{display:block;width:100%;padding:0;border:none;margin:0 0 var(--rt-space-2);background:none;color:var(--rt-color-text-primary);font:inherit;font-weight:var(--rt-font-weight-semibold);text-align:center}.rt-calendar__month-title:first-letter{text-transform:uppercase}.rt-calendar__month-title:is(button){border-radius:var(--rt-calendar-day-radius);cursor:pointer}.rt-calendar__month-title:is(button):hover{color:var(--rt-color-text-link)}.rt-calendar__month-title:is(button):focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__grid{display:grid;gap:var(--rt-space-0-5) var(--rt-calendar-column-gap);grid-template-columns:repeat(7,1fr)}.rt-calendar__weekday{padding:var(--rt-space-1) 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-align:center}.rt-calendar__day{display:flex;flex-direction:column;align-items:center;justify-content:var(--rt-calendar-day-justify);padding:var(--rt-space-2) var(--rt-space-0-5);border:none;border-radius:var(--rt-calendar-day-radius);background:none;color:var(--rt-color-text-primary);cursor:pointer;font:inherit;gap:var(--rt-space-0-5);min-block-size:var(--rt-calendar-day-size)}.rt-calendar__day:hover:not(:disabled,[data-state=start],[data-state=end],[data-state=in-range],[data-state=chosen]){background:var(--rt-color-bg-subtle)}.rt-calendar__day:disabled{color:var(--rt-color-text-muted);cursor:not-allowed;opacity:.45}.rt-calendar__day[data-state=past],.rt-calendar__day[data-state=blocked]{color:var(--rt-color-text-muted);opacity:.45}.rt-calendar__day[data-state=busy]{color:var(--rt-color-text-muted);opacity:.45;text-decoration:line-through}.rt-calendar__day[data-today]{box-shadow:var(--rt-calendar-today-ring)}.rt-calendar__day:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__day[data-state=start],.rt-calendar__day[data-state=end],.rt-calendar__day[data-state=chosen]{background:var(--rt-color-action-primary);color:var(--rt-color-action-on-primary)}.rt-calendar__day[data-state=start]{border-end-end-radius:var(--rt-calendar-range-inner-radius);border-start-end-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=end]{border-end-start-radius:var(--rt-calendar-range-inner-radius);border-start-start-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=in-range]{border-radius:var(--rt-calendar-range-inner-radius);background:var(--rt-color-action-primary-soft);color:var(--rt-color-text-link);font-weight:var(--rt-font-weight-semibold)}.rt-calendar__day-num{font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium)}.rt-calendar__day-sublabel{font-size:var(--rt-text-2xs);opacity:.75}@container rt-place (width <= 640px){.rt-calendar__months{grid-template-columns:1fr}.rt-calendar__month:last-child{display:none}}@media(width<=640px){:where(html:not(:has(.rt-container))) .rt-calendar__months{grid-template-columns:1fr}:where(html:not(:has(.rt-container))) .rt-calendar__month:last-child{display:none}}@container rt-place (width <= 374px){.rt-calendar__day{padding-inline:0}.rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}@media(width<=374px){:where(html:not(:has(.rt-container))) .rt-calendar__day{padding-inline:0}:where(html:not(:has(.rt-container))) .rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}.rt-calendar[data-rt-radius=none]{--rt-calendar-day-radius: var(--rt-radius-none)}.rt-calendar[data-rt-radius=xs]{--rt-calendar-day-radius: var(--rt-radius-xs)}.rt-calendar[data-rt-radius=sm]{--rt-calendar-day-radius: var(--rt-radius-sm)}.rt-calendar[data-rt-radius=ms]{--rt-calendar-day-radius: var(--rt-radius-ms)}.rt-calendar[data-rt-radius=md]{--rt-calendar-day-radius: var(--rt-radius-md)}.rt-calendar[data-rt-radius=lg]{--rt-calendar-day-radius: var(--rt-radius-lg)}.rt-calendar[data-rt-radius=xl]{--rt-calendar-day-radius: var(--rt-radius-xl)}.rt-calendar[data-rt-radius=\"2xl\"]{--rt-calendar-day-radius: var(--rt-radius-2xl)}.rt-calendar[data-rt-radius=full]{--rt-calendar-day-radius: var(--rt-radius-full)}}\n"],
216
+ template: "<ng-container rtBlock=\"rt-calendar\">\n <div rtElem=\"header\">\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-prev-month\"\n icon=\"chevron-left\"\n variant=\"ghost\"\n iconColor=\"muted\"\n radius=\"full\"\n [ariaLabel]=\"prevAriaLabel()\"\n [disabled]=\"!canPrev()\"\n (clicked)=\"prevMonth.emit()\" />\n @if (headerTitle() && months()[0]; as first) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: first }\" />\n }\n <ng-content select=\"[calendarHint]\" />\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-next-month\"\n icon=\"chevron-right\"\n variant=\"ghost\"\n iconColor=\"muted\"\n radius=\"full\"\n [ariaLabel]=\"nextAriaLabel()\"\n [disabled]=\"!canNext()\"\n (clicked)=\"nextMonth.emit()\" />\n </div>\n\n <div rtElem=\"months\" qa-dataid=\"calendar-months\" (mouseleave)=\"dayHover.emit(null)\">\n @for (month of months(); track month.key) {\n <div rtElem=\"month\" qa-dataid=\"calendar-month\">\n @if (!headerTitle()) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: month }\" />\n }\n <div rtElem=\"grid\" qa-dataid=\"calendar-grid\">\n @for (weekday of weekdayLabels(); track $index) {\n <span rtElem=\"weekday\" qa-dataid=\"calendar-weekday\">{{ weekday }}</span>\n }\n @if (month.leadingDays) {\n @for (day of month.leadingDays; track day.key) {\n <span\n rtElem=\"outside\"\n qa-dataid=\"calendar-outside-day\"\n aria-hidden=\"true\"\n [attr.data-iso]=\"day.key\"\n [attr.data-crossed]=\"day.crossed ? '' : null\">\n {{ day.dayOfMonth }}\n </span>\n }\n } @else {\n @for (blank of month.leadingBlanks; track blank) {\n <!-- Пустая ячейка сетки до первого дня месяца: занимает место и ничего не красит. -->\n <span qa-dataid=\"calendar-blank\" aria-hidden=\"true\"></span>\n }\n }\n @for (day of month.days; track day.key) {\n <button\n #dayButton\n rtElem=\"day\"\n qa-dataid=\"calendar-day\"\n type=\"button\"\n [attr.data-iso]=\"day.key\"\n [attr.data-state]=\"day.state\"\n [attr.data-today]=\"day.today ? '' : null\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.aria-pressed]=\"day.state === chosenState ? true : null\"\n [attr.tabindex]=\"rovingKey() === null ? null : day.key === rovingKey() ? 0 : -1\"\n [disabled]=\"day.disabled\"\n (keydown)=\"onDayKey($event, day)\"\n (mouseenter)=\"onDayHover(day)\"\n (focus)=\"onDayHover(day)\"\n (click)=\"onDayClick(day)\">\n <span rtElem=\"day-num\" qa-dataid=\"calendar-day-num\">{{ day.dayOfMonth }}</span>\n @if (sublabelsLoading()) {\n <rt-skeleton\n rtElem=\"day-sublabel\"\n qa-dataid=\"calendar-day-price-skeleton\"\n width=\"80%\"\n radius=\"sm\"\n [height]=\"sublabelSkeletonHeight\" />\n } @else if (day.sublabel) {\n <span rtElem=\"day-sublabel\" qa-dataid=\"calendar-day-price\">{{ day.sublabel }}</span>\n }\n </button>\n }\n @for (day of month.trailingDays ?? []; track day.key) {\n <span\n rtElem=\"outside\"\n qa-dataid=\"calendar-outside-day\"\n aria-hidden=\"true\"\n [attr.data-iso]=\"day.key\"\n [attr.data-crossed]=\"day.crossed ? '' : null\">\n {{ day.dayOfMonth }}\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-content />\n\n <ng-template #titleTpl let-month>\n @if (titleAction()) {\n <button rtElem=\"month-title\" qa-dataid=\"calendar-month-title\" type=\"button\" (click)=\"titleClick.emit(month)\">\n {{ month.label }}\n </button>\n } @else {\n <p rtElem=\"month-title\" qa-dataid=\"calendar-month-title\">{{ month.label }}</p>\n }\n </ng-template>\n</ng-container>\n",
217
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-calendar{--rt-calendar-day-radius: var(--rt-radius-md);--rt-calendar-day-size: auto;--rt-calendar-day-justify: flex-start;--rt-calendar-month-count: 2;--rt-calendar-column-gap: var(--rt-space-0-5);--rt-calendar-range-inner-radius: var(--rt-calendar-day-radius);--rt-calendar-today-ring: inset 0 0 0 var(--rt-border-width-thin) var(--rt-color-action-primary);display:block}.rt-calendar__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--rt-space-4);gap:var(--rt-space-4)}.rt-calendar__nav{flex:0 0 auto}.rt-calendar__header .rt-calendar__month-title{width:auto;padding:var(--rt-space-1) var(--rt-space-2);margin:0}.rt-calendar__months{display:grid;gap:var(--rt-space-6);grid-template-columns:repeat(var(--rt-calendar-month-count),1fr)}.rt-calendar__month-title{display:block;width:100%;padding:0;border:none;margin:0 0 var(--rt-space-2);background:none;color:var(--rt-color-text-primary);font:inherit;font-weight:var(--rt-font-weight-semibold);text-align:center}.rt-calendar__month-title:first-letter{text-transform:uppercase}.rt-calendar__month-title:is(button){border-radius:var(--rt-calendar-day-radius);cursor:pointer}.rt-calendar__month-title:is(button):hover{color:var(--rt-color-text-link)}.rt-calendar__month-title:is(button):focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__grid{display:grid;gap:var(--rt-space-0-5) var(--rt-calendar-column-gap);grid-template-columns:repeat(7,1fr)}.rt-calendar__weekday{padding:var(--rt-space-1) 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-align:center}.rt-calendar__day{display:flex;flex-direction:column;align-items:center;justify-content:var(--rt-calendar-day-justify);padding:var(--rt-space-2) var(--rt-space-0-5);border:none;border-radius:var(--rt-calendar-day-radius);background:none;color:var(--rt-color-text-primary);cursor:pointer;font:inherit;gap:var(--rt-space-0-5);min-block-size:var(--rt-calendar-day-size)}.rt-calendar__day:hover:not(:disabled,[data-state=start],[data-state=end],[data-state=in-range],[data-state=chosen]){background:var(--rt-color-bg-subtle)}.rt-calendar__day:disabled{color:var(--rt-color-text-muted);cursor:not-allowed;opacity:.45}.rt-calendar__day[data-state=past],.rt-calendar__day[data-state=blocked]{color:var(--rt-color-text-muted);opacity:.45}.rt-calendar__day[data-state=busy]{color:var(--rt-color-text-muted);opacity:.45;text-decoration:line-through}.rt-calendar__day[data-today]{box-shadow:var(--rt-calendar-today-ring)}.rt-calendar__day[data-today]:not([data-state=start],[data-state=end],[data-state=chosen],[data-state=in-range]){color:var(--rt-color-action-primary)}.rt-calendar__day:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__day[data-state=start],.rt-calendar__day[data-state=end],.rt-calendar__day[data-state=chosen]{background:var(--rt-color-action-primary);color:var(--rt-color-action-on-primary)}.rt-calendar__day[data-state=start]{border-end-end-radius:var(--rt-calendar-range-inner-radius);border-start-end-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=end]{border-end-start-radius:var(--rt-calendar-range-inner-radius);border-start-start-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=in-range]{border-radius:var(--rt-calendar-range-inner-radius);background:var(--rt-color-action-primary-soft);color:var(--rt-color-text-link);font-weight:var(--rt-font-weight-semibold)}.rt-calendar__day-num{font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium)}.rt-calendar__day[data-today] .rt-calendar__day-num{font-weight:var(--rt-font-weight-semibold)}.rt-calendar__outside{display:flex;align-items:center;justify-content:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium);min-block-size:var(--rt-calendar-day-size);opacity:.45}.rt-calendar__outside[data-crossed]{text-decoration:line-through}.rt-calendar__day-sublabel{font-size:var(--rt-text-2xs);opacity:.75}@container rt-place (width <= 640px){.rt-calendar__months{grid-template-columns:1fr}.rt-calendar__month:last-child:not(:first-child){display:none}}@media(width<=640px){:where(html:not(:has(.rt-container))) .rt-calendar__months{grid-template-columns:1fr}:where(html:not(:has(.rt-container))) .rt-calendar__month:last-child:not(:first-child){display:none}}@container rt-place (width <= 374px){.rt-calendar__day{padding-inline:0}.rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}@media(width<=374px){:where(html:not(:has(.rt-container))) .rt-calendar__day{padding-inline:0}:where(html:not(:has(.rt-container))) .rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}.rt-calendar[data-rt-radius=none]{--rt-calendar-day-radius: var(--rt-radius-none)}.rt-calendar[data-rt-radius=xs]{--rt-calendar-day-radius: var(--rt-radius-xs)}.rt-calendar[data-rt-radius=sm]{--rt-calendar-day-radius: var(--rt-radius-sm)}.rt-calendar[data-rt-radius=ms]{--rt-calendar-day-radius: var(--rt-radius-ms)}.rt-calendar[data-rt-radius=md]{--rt-calendar-day-radius: var(--rt-radius-md)}.rt-calendar[data-rt-radius=lg]{--rt-calendar-day-radius: var(--rt-radius-lg)}.rt-calendar[data-rt-radius=xl]{--rt-calendar-day-radius: var(--rt-radius-xl)}.rt-calendar[data-rt-radius=\"2xl\"]{--rt-calendar-day-radius: var(--rt-radius-2xl)}.rt-calendar[data-rt-radius=full]{--rt-calendar-day-radius: var(--rt-radius-full)}}\n"],
218
218
  dependencies: [
219
219
  {
220
220
  kind: "directive",
@@ -306,8 +306,8 @@ i0.ɵɵngDeclareClassMetadata({
306
306
  inputs: ["radius"]
307
307
  }],
308
308
  host: { class: BEM_BLOCK },
309
- template: "<ng-container rtBlock=\"rt-calendar\">\n <div rtElem=\"header\">\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-prev-month\"\n icon=\"chevron-left\"\n variant=\"secondary\"\n radius=\"full\"\n [ariaLabel]=\"prevAriaLabel()\"\n [disabled]=\"!canPrev()\"\n (clicked)=\"prevMonth.emit()\" />\n @if (headerTitle() && months()[0]; as first) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: first }\" />\n }\n <ng-content select=\"[calendarHint]\" />\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-next-month\"\n icon=\"chevron-right\"\n variant=\"secondary\"\n radius=\"full\"\n [ariaLabel]=\"nextAriaLabel()\"\n [disabled]=\"!canNext()\"\n (clicked)=\"nextMonth.emit()\" />\n </div>\n\n <div rtElem=\"months\" qa-dataid=\"calendar-months\" (mouseleave)=\"dayHover.emit(null)\">\n @for (month of months(); track month.key) {\n <div rtElem=\"month\" qa-dataid=\"calendar-month\">\n @if (!headerTitle()) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: month }\" />\n }\n <div rtElem=\"grid\" qa-dataid=\"calendar-grid\">\n @for (weekday of weekdayLabels(); track $index) {\n <span rtElem=\"weekday\" qa-dataid=\"calendar-weekday\">{{ weekday }}</span>\n }\n @for (blank of month.leadingBlanks; track blank) {\n <!-- Пустая ячейка сетки до первого дня месяца: занимает место и ничего не красит. -->\n <span qa-dataid=\"calendar-blank\" aria-hidden=\"true\"></span>\n }\n @for (day of month.days; track day.key) {\n <button\n #dayButton\n rtElem=\"day\"\n qa-dataid=\"calendar-day\"\n type=\"button\"\n [attr.data-iso]=\"day.key\"\n [attr.data-state]=\"day.state\"\n [attr.data-today]=\"day.today ? '' : null\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.aria-pressed]=\"day.state === chosenState ? true : null\"\n [attr.tabindex]=\"rovingKey() === null ? null : day.key === rovingKey() ? 0 : -1\"\n [disabled]=\"day.disabled\"\n (keydown)=\"onDayKey($event, day)\"\n (mouseenter)=\"onDayHover(day)\"\n (focus)=\"onDayHover(day)\"\n (click)=\"onDayClick(day)\">\n <span rtElem=\"day-num\" qa-dataid=\"calendar-day-num\">{{ day.dayOfMonth }}</span>\n @if (sublabelsLoading()) {\n <rt-skeleton\n rtElem=\"day-sublabel\"\n qa-dataid=\"calendar-day-price-skeleton\"\n width=\"80%\"\n radius=\"sm\"\n [height]=\"sublabelSkeletonHeight\" />\n } @else if (day.sublabel) {\n <span rtElem=\"day-sublabel\" qa-dataid=\"calendar-day-price\">{{ day.sublabel }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-content />\n\n <ng-template #titleTpl let-month>\n @if (titleAction()) {\n <button rtElem=\"month-title\" qa-dataid=\"calendar-month-title\" type=\"button\" (click)=\"titleClick.emit(month)\">\n {{ month.label }}\n </button>\n } @else {\n <p rtElem=\"month-title\" qa-dataid=\"calendar-month-title\">{{ month.label }}</p>\n }\n </ng-template>\n</ng-container>\n",
310
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-calendar{--rt-calendar-day-radius: var(--rt-radius-md);--rt-calendar-day-size: auto;--rt-calendar-day-justify: flex-start;--rt-calendar-month-count: 2;--rt-calendar-column-gap: var(--rt-space-0-5);--rt-calendar-range-inner-radius: var(--rt-calendar-day-radius);--rt-calendar-today-ring: inset 0 0 0 var(--rt-border-width-thin) var(--rt-color-action-primary);display:block}.rt-calendar__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--rt-space-4);gap:var(--rt-space-4)}.rt-calendar__nav{flex:0 0 auto}.rt-calendar__header .rt-calendar__month-title{width:auto;padding:var(--rt-space-1) var(--rt-space-2);margin:0}.rt-calendar__months{display:grid;gap:var(--rt-space-6);grid-template-columns:repeat(var(--rt-calendar-month-count),1fr)}.rt-calendar__month-title{display:block;width:100%;padding:0;border:none;margin:0 0 var(--rt-space-2);background:none;color:var(--rt-color-text-primary);font:inherit;font-weight:var(--rt-font-weight-semibold);text-align:center}.rt-calendar__month-title:first-letter{text-transform:uppercase}.rt-calendar__month-title:is(button){border-radius:var(--rt-calendar-day-radius);cursor:pointer}.rt-calendar__month-title:is(button):hover{color:var(--rt-color-text-link)}.rt-calendar__month-title:is(button):focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__grid{display:grid;gap:var(--rt-space-0-5) var(--rt-calendar-column-gap);grid-template-columns:repeat(7,1fr)}.rt-calendar__weekday{padding:var(--rt-space-1) 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-align:center}.rt-calendar__day{display:flex;flex-direction:column;align-items:center;justify-content:var(--rt-calendar-day-justify);padding:var(--rt-space-2) var(--rt-space-0-5);border:none;border-radius:var(--rt-calendar-day-radius);background:none;color:var(--rt-color-text-primary);cursor:pointer;font:inherit;gap:var(--rt-space-0-5);min-block-size:var(--rt-calendar-day-size)}.rt-calendar__day:hover:not(:disabled,[data-state=start],[data-state=end],[data-state=in-range],[data-state=chosen]){background:var(--rt-color-bg-subtle)}.rt-calendar__day:disabled{color:var(--rt-color-text-muted);cursor:not-allowed;opacity:.45}.rt-calendar__day[data-state=past],.rt-calendar__day[data-state=blocked]{color:var(--rt-color-text-muted);opacity:.45}.rt-calendar__day[data-state=busy]{color:var(--rt-color-text-muted);opacity:.45;text-decoration:line-through}.rt-calendar__day[data-today]{box-shadow:var(--rt-calendar-today-ring)}.rt-calendar__day:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__day[data-state=start],.rt-calendar__day[data-state=end],.rt-calendar__day[data-state=chosen]{background:var(--rt-color-action-primary);color:var(--rt-color-action-on-primary)}.rt-calendar__day[data-state=start]{border-end-end-radius:var(--rt-calendar-range-inner-radius);border-start-end-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=end]{border-end-start-radius:var(--rt-calendar-range-inner-radius);border-start-start-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=in-range]{border-radius:var(--rt-calendar-range-inner-radius);background:var(--rt-color-action-primary-soft);color:var(--rt-color-text-link);font-weight:var(--rt-font-weight-semibold)}.rt-calendar__day-num{font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium)}.rt-calendar__day-sublabel{font-size:var(--rt-text-2xs);opacity:.75}@container rt-place (width <= 640px){.rt-calendar__months{grid-template-columns:1fr}.rt-calendar__month:last-child{display:none}}@media(width<=640px){:where(html:not(:has(.rt-container))) .rt-calendar__months{grid-template-columns:1fr}:where(html:not(:has(.rt-container))) .rt-calendar__month:last-child{display:none}}@container rt-place (width <= 374px){.rt-calendar__day{padding-inline:0}.rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}@media(width<=374px){:where(html:not(:has(.rt-container))) .rt-calendar__day{padding-inline:0}:where(html:not(:has(.rt-container))) .rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}.rt-calendar[data-rt-radius=none]{--rt-calendar-day-radius: var(--rt-radius-none)}.rt-calendar[data-rt-radius=xs]{--rt-calendar-day-radius: var(--rt-radius-xs)}.rt-calendar[data-rt-radius=sm]{--rt-calendar-day-radius: var(--rt-radius-sm)}.rt-calendar[data-rt-radius=ms]{--rt-calendar-day-radius: var(--rt-radius-ms)}.rt-calendar[data-rt-radius=md]{--rt-calendar-day-radius: var(--rt-radius-md)}.rt-calendar[data-rt-radius=lg]{--rt-calendar-day-radius: var(--rt-radius-lg)}.rt-calendar[data-rt-radius=xl]{--rt-calendar-day-radius: var(--rt-radius-xl)}.rt-calendar[data-rt-radius=\"2xl\"]{--rt-calendar-day-radius: var(--rt-radius-2xl)}.rt-calendar[data-rt-radius=full]{--rt-calendar-day-radius: var(--rt-radius-full)}}\n"]
309
+ template: "<ng-container rtBlock=\"rt-calendar\">\n <div rtElem=\"header\">\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-prev-month\"\n icon=\"chevron-left\"\n variant=\"ghost\"\n iconColor=\"muted\"\n radius=\"full\"\n [ariaLabel]=\"prevAriaLabel()\"\n [disabled]=\"!canPrev()\"\n (clicked)=\"prevMonth.emit()\" />\n @if (headerTitle() && months()[0]; as first) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: first }\" />\n }\n <ng-content select=\"[calendarHint]\" />\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-next-month\"\n icon=\"chevron-right\"\n variant=\"ghost\"\n iconColor=\"muted\"\n radius=\"full\"\n [ariaLabel]=\"nextAriaLabel()\"\n [disabled]=\"!canNext()\"\n (clicked)=\"nextMonth.emit()\" />\n </div>\n\n <div rtElem=\"months\" qa-dataid=\"calendar-months\" (mouseleave)=\"dayHover.emit(null)\">\n @for (month of months(); track month.key) {\n <div rtElem=\"month\" qa-dataid=\"calendar-month\">\n @if (!headerTitle()) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: month }\" />\n }\n <div rtElem=\"grid\" qa-dataid=\"calendar-grid\">\n @for (weekday of weekdayLabels(); track $index) {\n <span rtElem=\"weekday\" qa-dataid=\"calendar-weekday\">{{ weekday }}</span>\n }\n @if (month.leadingDays) {\n @for (day of month.leadingDays; track day.key) {\n <span\n rtElem=\"outside\"\n qa-dataid=\"calendar-outside-day\"\n aria-hidden=\"true\"\n [attr.data-iso]=\"day.key\"\n [attr.data-crossed]=\"day.crossed ? '' : null\">\n {{ day.dayOfMonth }}\n </span>\n }\n } @else {\n @for (blank of month.leadingBlanks; track blank) {\n <!-- Пустая ячейка сетки до первого дня месяца: занимает место и ничего не красит. -->\n <span qa-dataid=\"calendar-blank\" aria-hidden=\"true\"></span>\n }\n }\n @for (day of month.days; track day.key) {\n <button\n #dayButton\n rtElem=\"day\"\n qa-dataid=\"calendar-day\"\n type=\"button\"\n [attr.data-iso]=\"day.key\"\n [attr.data-state]=\"day.state\"\n [attr.data-today]=\"day.today ? '' : null\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.aria-pressed]=\"day.state === chosenState ? true : null\"\n [attr.tabindex]=\"rovingKey() === null ? null : day.key === rovingKey() ? 0 : -1\"\n [disabled]=\"day.disabled\"\n (keydown)=\"onDayKey($event, day)\"\n (mouseenter)=\"onDayHover(day)\"\n (focus)=\"onDayHover(day)\"\n (click)=\"onDayClick(day)\">\n <span rtElem=\"day-num\" qa-dataid=\"calendar-day-num\">{{ day.dayOfMonth }}</span>\n @if (sublabelsLoading()) {\n <rt-skeleton\n rtElem=\"day-sublabel\"\n qa-dataid=\"calendar-day-price-skeleton\"\n width=\"80%\"\n radius=\"sm\"\n [height]=\"sublabelSkeletonHeight\" />\n } @else if (day.sublabel) {\n <span rtElem=\"day-sublabel\" qa-dataid=\"calendar-day-price\">{{ day.sublabel }}</span>\n }\n </button>\n }\n @for (day of month.trailingDays ?? []; track day.key) {\n <span\n rtElem=\"outside\"\n qa-dataid=\"calendar-outside-day\"\n aria-hidden=\"true\"\n [attr.data-iso]=\"day.key\"\n [attr.data-crossed]=\"day.crossed ? '' : null\">\n {{ day.dayOfMonth }}\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-content />\n\n <ng-template #titleTpl let-month>\n @if (titleAction()) {\n <button rtElem=\"month-title\" qa-dataid=\"calendar-month-title\" type=\"button\" (click)=\"titleClick.emit(month)\">\n {{ month.label }}\n </button>\n } @else {\n <p rtElem=\"month-title\" qa-dataid=\"calendar-month-title\">{{ month.label }}</p>\n }\n </ng-template>\n</ng-container>\n",
310
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-calendar{--rt-calendar-day-radius: var(--rt-radius-md);--rt-calendar-day-size: auto;--rt-calendar-day-justify: flex-start;--rt-calendar-month-count: 2;--rt-calendar-column-gap: var(--rt-space-0-5);--rt-calendar-range-inner-radius: var(--rt-calendar-day-radius);--rt-calendar-today-ring: inset 0 0 0 var(--rt-border-width-thin) var(--rt-color-action-primary);display:block}.rt-calendar__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--rt-space-4);gap:var(--rt-space-4)}.rt-calendar__nav{flex:0 0 auto}.rt-calendar__header .rt-calendar__month-title{width:auto;padding:var(--rt-space-1) var(--rt-space-2);margin:0}.rt-calendar__months{display:grid;gap:var(--rt-space-6);grid-template-columns:repeat(var(--rt-calendar-month-count),1fr)}.rt-calendar__month-title{display:block;width:100%;padding:0;border:none;margin:0 0 var(--rt-space-2);background:none;color:var(--rt-color-text-primary);font:inherit;font-weight:var(--rt-font-weight-semibold);text-align:center}.rt-calendar__month-title:first-letter{text-transform:uppercase}.rt-calendar__month-title:is(button){border-radius:var(--rt-calendar-day-radius);cursor:pointer}.rt-calendar__month-title:is(button):hover{color:var(--rt-color-text-link)}.rt-calendar__month-title:is(button):focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__grid{display:grid;gap:var(--rt-space-0-5) var(--rt-calendar-column-gap);grid-template-columns:repeat(7,1fr)}.rt-calendar__weekday{padding:var(--rt-space-1) 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-align:center}.rt-calendar__day{display:flex;flex-direction:column;align-items:center;justify-content:var(--rt-calendar-day-justify);padding:var(--rt-space-2) var(--rt-space-0-5);border:none;border-radius:var(--rt-calendar-day-radius);background:none;color:var(--rt-color-text-primary);cursor:pointer;font:inherit;gap:var(--rt-space-0-5);min-block-size:var(--rt-calendar-day-size)}.rt-calendar__day:hover:not(:disabled,[data-state=start],[data-state=end],[data-state=in-range],[data-state=chosen]){background:var(--rt-color-bg-subtle)}.rt-calendar__day:disabled{color:var(--rt-color-text-muted);cursor:not-allowed;opacity:.45}.rt-calendar__day[data-state=past],.rt-calendar__day[data-state=blocked]{color:var(--rt-color-text-muted);opacity:.45}.rt-calendar__day[data-state=busy]{color:var(--rt-color-text-muted);opacity:.45;text-decoration:line-through}.rt-calendar__day[data-today]{box-shadow:var(--rt-calendar-today-ring)}.rt-calendar__day[data-today]:not([data-state=start],[data-state=end],[data-state=chosen],[data-state=in-range]){color:var(--rt-color-action-primary)}.rt-calendar__day:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-calendar__day[data-state=start],.rt-calendar__day[data-state=end],.rt-calendar__day[data-state=chosen]{background:var(--rt-color-action-primary);color:var(--rt-color-action-on-primary)}.rt-calendar__day[data-state=start]{border-end-end-radius:var(--rt-calendar-range-inner-radius);border-start-end-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=end]{border-end-start-radius:var(--rt-calendar-range-inner-radius);border-start-start-radius:var(--rt-calendar-range-inner-radius)}.rt-calendar__day[data-state=in-range]{border-radius:var(--rt-calendar-range-inner-radius);background:var(--rt-color-action-primary-soft);color:var(--rt-color-text-link);font-weight:var(--rt-font-weight-semibold)}.rt-calendar__day-num{font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium)}.rt-calendar__day[data-today] .rt-calendar__day-num{font-weight:var(--rt-font-weight-semibold)}.rt-calendar__outside{display:flex;align-items:center;justify-content:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium);min-block-size:var(--rt-calendar-day-size);opacity:.45}.rt-calendar__outside[data-crossed]{text-decoration:line-through}.rt-calendar__day-sublabel{font-size:var(--rt-text-2xs);opacity:.75}@container rt-place (width <= 640px){.rt-calendar__months{grid-template-columns:1fr}.rt-calendar__month:last-child:not(:first-child){display:none}}@media(width<=640px){:where(html:not(:has(.rt-container))) .rt-calendar__months{grid-template-columns:1fr}:where(html:not(:has(.rt-container))) .rt-calendar__month:last-child:not(:first-child){display:none}}@container rt-place (width <= 374px){.rt-calendar__day{padding-inline:0}.rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}@media(width<=374px){:where(html:not(:has(.rt-container))) .rt-calendar__day{padding-inline:0}:where(html:not(:has(.rt-container))) .rt-calendar__day-sublabel{font-size:var(--rt-text-3xs)}}.rt-calendar[data-rt-radius=none]{--rt-calendar-day-radius: var(--rt-radius-none)}.rt-calendar[data-rt-radius=xs]{--rt-calendar-day-radius: var(--rt-radius-xs)}.rt-calendar[data-rt-radius=sm]{--rt-calendar-day-radius: var(--rt-radius-sm)}.rt-calendar[data-rt-radius=ms]{--rt-calendar-day-radius: var(--rt-radius-ms)}.rt-calendar[data-rt-radius=md]{--rt-calendar-day-radius: var(--rt-radius-md)}.rt-calendar[data-rt-radius=lg]{--rt-calendar-day-radius: var(--rt-radius-lg)}.rt-calendar[data-rt-radius=xl]{--rt-calendar-day-radius: var(--rt-radius-xl)}.rt-calendar[data-rt-radius=\"2xl\"]{--rt-calendar-day-radius: var(--rt-radius-2xl)}.rt-calendar[data-rt-radius=full]{--rt-calendar-day-radius: var(--rt-radius-full)}}\n"]
311
311
  }]
312
312
  }],
313
313
  ctorParameters: () => [],
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-calendar.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/calendar/rt-calendar.model.ts","../../../projects/ui-kit-v2/src/lib/components/calendar/rt-calendar.component.ts","../../../projects/ui-kit-v2/src/lib/components/calendar/rt-calendar.component.html"],"sourcesContent":["/**\n * Контракты `rt-calendar` — презентационный месячный грид с range-выбором.\n *\n * Компонент домен-агностичен: состояния/цены/доступность дней вычисляет\n * consumer и передаёт готовые view-модели месяцев. Клики по дням и навигация\n * по месяцам эмитятся наружу; никакой доменной логики внутри нет.\n */\n\n/** Состояние ячейки дня — модификатор `data-state` на кнопке дня. */\nexport enum ERtCalendarDayState {\n Free = 'free',\n Busy = 'busy',\n Past = 'past',\n Blocked = 'blocked',\n Start = 'start',\n End = 'end',\n InRange = 'in-range',\n /** Выбранный день одиночного выбора — залит. */\n Chosen = 'chosen',\n}\n\nexport namespace IRtCalendar {\n /** View-модель дня. `key` — стабильный ключ (ISO-дата). */\n export interface Day {\n key: string;\n dayOfMonth: number;\n /** Подпись под числом (цена за ночь и т.п.); пустая строка не рендерится. */\n sublabel: string;\n state: ERtCalendarDayState;\n disabled: boolean;\n /** Сегодняшний день — обведён рамкой поверх любого состояния. */\n today?: boolean;\n }\n\n /** Клавиша сетки, нажатая на дне. */\n export interface GridKey {\n key: string;\n day: Day;\n }\n\n /** View-модель месяца: подпись, пустые ячейки до первого дня, дни. */\n export interface Month {\n key: string;\n label: string;\n leadingBlanks: readonly number[];\n days: readonly Day[];\n }\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterRenderEffect,\n booleanAttribute,\n computed,\n signal,\n viewChildren,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n InputSignal,\n InputSignalWithTransform,\n output,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtSkeletonComponent } from '@rt-tools/ui-kit-v2/skeleton';\nimport { ERtCalendarDayState, IRtCalendar } from './rt-calendar.model';\n\nconst BEM_BLOCK: string = 'rt-calendar';\n\n/** Клавиши, что календарь в режиме сетки отдаёт наружу вместо прокрутки страницы. */\nconst GRID_KEYS: ReadonlySet<string> = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End']);\n\n@Component({\n selector: 'rt-calendar',\n templateUrl: './rt-calendar.component.html',\n styleUrl: './rt-calendar.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n NgTemplateOutlet,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtIconButtonComponent,\n RtSkeletonComponent,\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtCalendarComponent {\n /** Фокус двигала клавиша: только тогда календарь переводит фокус на `activeKey`. */\n readonly #keyed: WritableSignal<boolean> = signal(false);\n\n protected readonly sublabelSkeletonHeight: string = '12px';\n protected readonly chosenState: ERtCalendarDayState = ERtCalendarDayState.Chosen;\n\n protected readonly dayButtons: Signal<readonly ElementRef<HTMLButtonElement>[]> =\n viewChildren<ElementRef<HTMLButtonElement>>('dayButton');\n\n /**\n * День, что стоит в порядке обхода Tab в режиме сетки: `activeKey`, иначе выбранный, иначе\n * сегодняшний, иначе первый доступный. Вне режима сетки — `null`, и каждый день обходится Tab.\n */\n protected readonly rovingKey: Signal<string | null> = computed((): string | null => {\n if (!this.grid()) {\n return null;\n }\n const days: IRtCalendar.Day[] = this.months().flatMap((month: IRtCalendar.Month): IRtCalendar.Day[] => [...month.days]);\n const active: string | null = this.activeKey();\n const pick: IRtCalendar.Day | undefined =\n days.find((day: IRtCalendar.Day): boolean => day.key === active) ??\n days.find((day: IRtCalendar.Day): boolean => day.state === ERtCalendarDayState.Chosen) ??\n days.find((day: IRtCalendar.Day): boolean => day.today === true) ??\n days.find((day: IRtCalendar.Day): boolean => !day.disabled);\n return pick?.key ?? null;\n });\n\n public readonly months: InputSignal<readonly IRtCalendar.Month[]> = input.required<readonly IRtCalendar.Month[]>();\n public readonly weekdayLabels: InputSignal<readonly string[]> = input.required<readonly string[]>();\n public readonly canPrev: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly canNext: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** Локализованные aria-подписи кнопок навигации — задаёт consumer. */\n public readonly prevAriaLabel: InputSignal<string> = input<string>('');\n public readonly nextAriaLabel: InputSignal<string> = input<string>('');\n public readonly sublabelsLoading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /**\n * Режим сетки: один день в обходе Tab, клавиши стрелок, PageUp/PageDown, Home и End уходят\n * наружу событием `gridKey`, а фокус встаёт на `activeKey`. Выключен — поведение прежнее.\n */\n public readonly grid: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** День с фокусом в режиме сетки, ключ `YYYY-MM-DD`; его ставит consumer по `gridKey`. */\n public readonly activeKey: InputSignal<string | null> = input<string | null>(null);\n /** Заголовок месяца — кнопка, что шлёт `titleClick`: так consumer открывает выбор месяца. */\n public readonly titleAction: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** Заголовок первого месяца стоит в шапке между стрелками, а не над сеткой. */\n public readonly headerTitle: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n public readonly prevMonth: OutputEmitterRef<void> = output<void>();\n public readonly nextMonth: OutputEmitterRef<void> = output<void>();\n public readonly dayClick: OutputEmitterRef<IRtCalendar.Day> = output<IRtCalendar.Day>();\n /** Клавиша сетки на дне: куда перевести фокус, решает consumer. */\n public readonly gridKey: OutputEmitterRef<IRtCalendar.GridKey> = output<IRtCalendar.GridKey>();\n public readonly titleClick: OutputEmitterRef<IRtCalendar.Month> = output<IRtCalendar.Month>();\n /** День под указателем или с фокусом; `null` — указатель ушёл с месяцев. По нему рисуется будущий диапазон. */\n public readonly dayHover: OutputEmitterRef<IRtCalendar.Day | null> = output<IRtCalendar.Day | null>();\n\n constructor() {\n afterRenderEffect((): void => {\n const key: string | null = this.rovingKey();\n if (!this.#keyed() || key === null) {\n return;\n }\n this.dayButtons()\n .find((button: ElementRef<HTMLButtonElement>): boolean => button.nativeElement.dataset['iso'] === key)\n ?.nativeElement.focus();\n });\n }\n\n protected onDayKey(event: KeyboardEvent, day: IRtCalendar.Day): void {\n if (!this.grid() || !GRID_KEYS.has(event.key)) {\n return;\n }\n event.preventDefault();\n this.#keyed.set(true);\n this.gridKey.emit({ key: event.key, day });\n }\n\n protected onDayHover(day: IRtCalendar.Day): void {\n if (!day.disabled) {\n this.dayHover.emit(day);\n }\n }\n\n protected onDayClick(day: IRtCalendar.Day): void {\n if (!day.disabled) {\n this.dayClick.emit(day);\n }\n }\n}\n","<ng-container rtBlock=\"rt-calendar\">\n <div rtElem=\"header\">\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-prev-month\"\n icon=\"chevron-left\"\n variant=\"secondary\"\n radius=\"full\"\n [ariaLabel]=\"prevAriaLabel()\"\n [disabled]=\"!canPrev()\"\n (clicked)=\"prevMonth.emit()\" />\n @if (headerTitle() && months()[0]; as first) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: first }\" />\n }\n <ng-content select=\"[calendarHint]\" />\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-next-month\"\n icon=\"chevron-right\"\n variant=\"secondary\"\n radius=\"full\"\n [ariaLabel]=\"nextAriaLabel()\"\n [disabled]=\"!canNext()\"\n (clicked)=\"nextMonth.emit()\" />\n </div>\n\n <div rtElem=\"months\" qa-dataid=\"calendar-months\" (mouseleave)=\"dayHover.emit(null)\">\n @for (month of months(); track month.key) {\n <div rtElem=\"month\" qa-dataid=\"calendar-month\">\n @if (!headerTitle()) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: month }\" />\n }\n <div rtElem=\"grid\" qa-dataid=\"calendar-grid\">\n @for (weekday of weekdayLabels(); track $index) {\n <span rtElem=\"weekday\" qa-dataid=\"calendar-weekday\">{{ weekday }}</span>\n }\n @for (blank of month.leadingBlanks; track blank) {\n <!-- Пустая ячейка сетки до первого дня месяца: занимает место и ничего не красит. -->\n <span qa-dataid=\"calendar-blank\" aria-hidden=\"true\"></span>\n }\n @for (day of month.days; track day.key) {\n <button\n #dayButton\n rtElem=\"day\"\n qa-dataid=\"calendar-day\"\n type=\"button\"\n [attr.data-iso]=\"day.key\"\n [attr.data-state]=\"day.state\"\n [attr.data-today]=\"day.today ? '' : null\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.aria-pressed]=\"day.state === chosenState ? true : null\"\n [attr.tabindex]=\"rovingKey() === null ? null : day.key === rovingKey() ? 0 : -1\"\n [disabled]=\"day.disabled\"\n (keydown)=\"onDayKey($event, day)\"\n (mouseenter)=\"onDayHover(day)\"\n (focus)=\"onDayHover(day)\"\n (click)=\"onDayClick(day)\">\n <span rtElem=\"day-num\" qa-dataid=\"calendar-day-num\">{{ day.dayOfMonth }}</span>\n @if (sublabelsLoading()) {\n <rt-skeleton\n rtElem=\"day-sublabel\"\n qa-dataid=\"calendar-day-price-skeleton\"\n width=\"80%\"\n radius=\"sm\"\n [height]=\"sublabelSkeletonHeight\" />\n } @else if (day.sublabel) {\n <span rtElem=\"day-sublabel\" qa-dataid=\"calendar-day-price\">{{ day.sublabel }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-content />\n\n <ng-template #titleTpl let-month>\n @if (titleAction()) {\n <button rtElem=\"month-title\" qa-dataid=\"calendar-month-title\" type=\"button\" (click)=\"titleClick.emit(month)\">\n {{ month.label }}\n </button>\n } @else {\n <p rtElem=\"month-title\" qa-dataid=\"calendar-month-title\">{{ month.label }}</p>\n }\n </ng-template>\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-calendar.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/calendar/rt-calendar.model.ts","../../../projects/ui-kit-v2/src/lib/components/calendar/rt-calendar.component.ts","../../../projects/ui-kit-v2/src/lib/components/calendar/rt-calendar.component.html"],"sourcesContent":["/**\n * Контракты `rt-calendar` — презентационный месячный грид с range-выбором.\n *\n * Компонент домен-агностичен: состояния/цены/доступность дней вычисляет\n * consumer и передаёт готовые view-модели месяцев. Клики по дням и навигация\n * по месяцам эмитятся наружу; никакой доменной логики внутри нет.\n */\n\n/** Состояние ячейки дня — модификатор `data-state` на кнопке дня. */\nexport enum ERtCalendarDayState {\n Free = 'free',\n Busy = 'busy',\n Past = 'past',\n Blocked = 'blocked',\n Start = 'start',\n End = 'end',\n InRange = 'in-range',\n /** Выбранный день одиночного выбора — залит. */\n Chosen = 'chosen',\n}\n\nexport namespace IRtCalendar {\n /** View-модель дня. `key` — стабильный ключ (ISO-дата). */\n export interface Day {\n key: string;\n dayOfMonth: number;\n /** Подпись под числом (цена за ночь и т.п.); пустая строка не рендерится. */\n sublabel: string;\n state: ERtCalendarDayState;\n disabled: boolean;\n /** Сегодняшний день — обведён рамкой поверх любого состояния. */\n today?: boolean;\n }\n\n /** Клавиша сетки, нажатая на дне. */\n export interface GridKey {\n key: string;\n day: Day;\n }\n\n /** День соседнего месяца в начале или в конце сетки: бледный, не нажимается. */\n export interface OutsideDay {\n key: string;\n dayOfMonth: number;\n /** День за границей выбора — ещё и зачёркнут. */\n crossed: boolean;\n }\n\n /**\n * View-модель месяца: подпись, пустые ячейки до первого дня, дни. Дни соседних месяцев\n * необязательны: заданы — встают на место пустых ячеек и дописывают последнюю неделю.\n */\n export interface Month {\n key: string;\n label: string;\n leadingBlanks: readonly number[];\n days: readonly Day[];\n leadingDays?: readonly OutsideDay[];\n trailingDays?: readonly OutsideDay[];\n }\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterRenderEffect,\n booleanAttribute,\n computed,\n signal,\n viewChildren,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n InputSignal,\n InputSignalWithTransform,\n output,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtSkeletonComponent } from '@rt-tools/ui-kit-v2/skeleton';\nimport { ERtCalendarDayState, IRtCalendar } from './rt-calendar.model';\n\nconst BEM_BLOCK: string = 'rt-calendar';\n\n/** Клавиши, что календарь в режиме сетки отдаёт наружу вместо прокрутки страницы. */\nconst GRID_KEYS: ReadonlySet<string> = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End']);\n\n@Component({\n selector: 'rt-calendar',\n templateUrl: './rt-calendar.component.html',\n styleUrl: './rt-calendar.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n NgTemplateOutlet,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtIconButtonComponent,\n RtSkeletonComponent,\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtCalendarComponent {\n /** Фокус двигала клавиша: только тогда календарь переводит фокус на `activeKey`. */\n readonly #keyed: WritableSignal<boolean> = signal(false);\n\n protected readonly sublabelSkeletonHeight: string = '12px';\n protected readonly chosenState: ERtCalendarDayState = ERtCalendarDayState.Chosen;\n\n protected readonly dayButtons: Signal<readonly ElementRef<HTMLButtonElement>[]> =\n viewChildren<ElementRef<HTMLButtonElement>>('dayButton');\n\n /**\n * День, что стоит в порядке обхода Tab в режиме сетки: `activeKey`, иначе выбранный, иначе\n * сегодняшний, иначе первый доступный. Вне режима сетки — `null`, и каждый день обходится Tab.\n */\n protected readonly rovingKey: Signal<string | null> = computed((): string | null => {\n if (!this.grid()) {\n return null;\n }\n const days: IRtCalendar.Day[] = this.months().flatMap((month: IRtCalendar.Month): IRtCalendar.Day[] => [...month.days]);\n const active: string | null = this.activeKey();\n const pick: IRtCalendar.Day | undefined =\n days.find((day: IRtCalendar.Day): boolean => day.key === active) ??\n days.find((day: IRtCalendar.Day): boolean => day.state === ERtCalendarDayState.Chosen) ??\n days.find((day: IRtCalendar.Day): boolean => day.today === true) ??\n days.find((day: IRtCalendar.Day): boolean => !day.disabled);\n return pick?.key ?? null;\n });\n\n public readonly months: InputSignal<readonly IRtCalendar.Month[]> = input.required<readonly IRtCalendar.Month[]>();\n public readonly weekdayLabels: InputSignal<readonly string[]> = input.required<readonly string[]>();\n public readonly canPrev: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly canNext: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** Локализованные aria-подписи кнопок навигации — задаёт consumer. */\n public readonly prevAriaLabel: InputSignal<string> = input<string>('');\n public readonly nextAriaLabel: InputSignal<string> = input<string>('');\n public readonly sublabelsLoading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /**\n * Режим сетки: один день в обходе Tab, клавиши стрелок, PageUp/PageDown, Home и End уходят\n * наружу событием `gridKey`, а фокус встаёт на `activeKey`. Выключен — поведение прежнее.\n */\n public readonly grid: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** День с фокусом в режиме сетки, ключ `YYYY-MM-DD`; его ставит consumer по `gridKey`. */\n public readonly activeKey: InputSignal<string | null> = input<string | null>(null);\n /** Заголовок месяца — кнопка, что шлёт `titleClick`: так consumer открывает выбор месяца. */\n public readonly titleAction: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** Заголовок первого месяца стоит в шапке между стрелками, а не над сеткой. */\n public readonly headerTitle: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n public readonly prevMonth: OutputEmitterRef<void> = output<void>();\n public readonly nextMonth: OutputEmitterRef<void> = output<void>();\n public readonly dayClick: OutputEmitterRef<IRtCalendar.Day> = output<IRtCalendar.Day>();\n /** Клавиша сетки на дне: куда перевести фокус, решает consumer. */\n public readonly gridKey: OutputEmitterRef<IRtCalendar.GridKey> = output<IRtCalendar.GridKey>();\n public readonly titleClick: OutputEmitterRef<IRtCalendar.Month> = output<IRtCalendar.Month>();\n /** День под указателем или с фокусом; `null` — указатель ушёл с месяцев. По нему рисуется будущий диапазон. */\n public readonly dayHover: OutputEmitterRef<IRtCalendar.Day | null> = output<IRtCalendar.Day | null>();\n\n constructor() {\n afterRenderEffect((): void => {\n const key: string | null = this.rovingKey();\n if (!this.#keyed() || key === null) {\n return;\n }\n this.dayButtons()\n .find((button: ElementRef<HTMLButtonElement>): boolean => button.nativeElement.dataset['iso'] === key)\n ?.nativeElement.focus();\n });\n }\n\n protected onDayKey(event: KeyboardEvent, day: IRtCalendar.Day): void {\n if (!this.grid() || !GRID_KEYS.has(event.key)) {\n return;\n }\n event.preventDefault();\n this.#keyed.set(true);\n this.gridKey.emit({ key: event.key, day });\n }\n\n protected onDayHover(day: IRtCalendar.Day): void {\n if (!day.disabled) {\n this.dayHover.emit(day);\n }\n }\n\n protected onDayClick(day: IRtCalendar.Day): void {\n if (!day.disabled) {\n this.dayClick.emit(day);\n }\n }\n}\n","<ng-container rtBlock=\"rt-calendar\">\n <div rtElem=\"header\">\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-prev-month\"\n icon=\"chevron-left\"\n variant=\"ghost\"\n iconColor=\"muted\"\n radius=\"full\"\n [ariaLabel]=\"prevAriaLabel()\"\n [disabled]=\"!canPrev()\"\n (clicked)=\"prevMonth.emit()\" />\n @if (headerTitle() && months()[0]; as first) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: first }\" />\n }\n <ng-content select=\"[calendarHint]\" />\n <rt-icon-button\n rtElem=\"nav\"\n qa-dataid=\"calendar-next-month\"\n icon=\"chevron-right\"\n variant=\"ghost\"\n iconColor=\"muted\"\n radius=\"full\"\n [ariaLabel]=\"nextAriaLabel()\"\n [disabled]=\"!canNext()\"\n (clicked)=\"nextMonth.emit()\" />\n </div>\n\n <div rtElem=\"months\" qa-dataid=\"calendar-months\" (mouseleave)=\"dayHover.emit(null)\">\n @for (month of months(); track month.key) {\n <div rtElem=\"month\" qa-dataid=\"calendar-month\">\n @if (!headerTitle()) {\n <ng-container *ngTemplateOutlet=\"titleTpl; context: { $implicit: month }\" />\n }\n <div rtElem=\"grid\" qa-dataid=\"calendar-grid\">\n @for (weekday of weekdayLabels(); track $index) {\n <span rtElem=\"weekday\" qa-dataid=\"calendar-weekday\">{{ weekday }}</span>\n }\n @if (month.leadingDays) {\n @for (day of month.leadingDays; track day.key) {\n <span\n rtElem=\"outside\"\n qa-dataid=\"calendar-outside-day\"\n aria-hidden=\"true\"\n [attr.data-iso]=\"day.key\"\n [attr.data-crossed]=\"day.crossed ? '' : null\">\n {{ day.dayOfMonth }}\n </span>\n }\n } @else {\n @for (blank of month.leadingBlanks; track blank) {\n <!-- Пустая ячейка сетки до первого дня месяца: занимает место и ничего не красит. -->\n <span qa-dataid=\"calendar-blank\" aria-hidden=\"true\"></span>\n }\n }\n @for (day of month.days; track day.key) {\n <button\n #dayButton\n rtElem=\"day\"\n qa-dataid=\"calendar-day\"\n type=\"button\"\n [attr.data-iso]=\"day.key\"\n [attr.data-state]=\"day.state\"\n [attr.data-today]=\"day.today ? '' : null\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.aria-pressed]=\"day.state === chosenState ? true : null\"\n [attr.tabindex]=\"rovingKey() === null ? null : day.key === rovingKey() ? 0 : -1\"\n [disabled]=\"day.disabled\"\n (keydown)=\"onDayKey($event, day)\"\n (mouseenter)=\"onDayHover(day)\"\n (focus)=\"onDayHover(day)\"\n (click)=\"onDayClick(day)\">\n <span rtElem=\"day-num\" qa-dataid=\"calendar-day-num\">{{ day.dayOfMonth }}</span>\n @if (sublabelsLoading()) {\n <rt-skeleton\n rtElem=\"day-sublabel\"\n qa-dataid=\"calendar-day-price-skeleton\"\n width=\"80%\"\n radius=\"sm\"\n [height]=\"sublabelSkeletonHeight\" />\n } @else if (day.sublabel) {\n <span rtElem=\"day-sublabel\" qa-dataid=\"calendar-day-price\">{{ day.sublabel }}</span>\n }\n </button>\n }\n @for (day of month.trailingDays ?? []; track day.key) {\n <span\n rtElem=\"outside\"\n qa-dataid=\"calendar-outside-day\"\n aria-hidden=\"true\"\n [attr.data-iso]=\"day.key\"\n [attr.data-crossed]=\"day.crossed ? '' : null\">\n {{ day.dayOfMonth }}\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-content />\n\n <ng-template #titleTpl let-month>\n @if (titleAction()) {\n <button rtElem=\"month-title\" qa-dataid=\"calendar-month-title\" type=\"button\" (click)=\"titleClick.emit(month)\">\n {{ month.label }}\n </button>\n } @else {\n <p rtElem=\"month-title\" qa-dataid=\"calendar-month-title\">{{ month.label }}</p>\n }\n </ng-template>\n</ng-container>\n"],"mappings":"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"}